MMC highlights for 3.7:

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)
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 
 iQIcBAABAgAGBQJQcfc/AAoJEHNBYZ7TNxYMB3wQALPObZUjKBsR38N2llPUOz5M
 nTMNYa99Pg3/Du5EgXKwYDkoYG1M9yjNTdxBmz3Sz9cIkLueZHoDmqvpgZJv9vRn
 5l0TncExC+T2Tn7qjE5axgM7fus5r9SLKCOtbE+V8jATTWeG0d/X0DdzvKPpJLmb
 uLPmqNG50LdQQUoCkcDU3hiDONqQnOx4tDq4C7rTlf+Cr8pJXRoBPEF0C1PTvs64
 0AP8oXDtirz+RIR5xTELy08o4SVS4Wcn63PH1H56kmAIjdT5FeVnAOeyF9Aer/+R
 Sz3qMrN/sNSEEkbgCGQLJVVYACNdgB1WXdxhqk2d996iwtEQgkVB8S2ziyhpZTZ2
 SxgCMvfYf5ySOIuzvyEScGdKjw6DSV01HDr9eyFJqIYaDOBp+kUJkbM2O8ISf+Kb
 rudrc58mdfPPhX5rqjEYBKGtyC6q+LvRGOwO8QJNvZ0wAFAg4nCzcD9btAl65QR1
 7aM0qp+55pyc2xyUO9q5AvOwiaBU2sYYuBCUm1zzK3HQ8x5ZKpueQwa2KBmEa2f+
 Qp6oflWNeG/X53WHCurl/ECZY5Y4w4esHPMWXVXJP8Ao+3D2Wofkp4CSGcQClZSd
 /OBGSw9g70BIKwOTUvU9tD3ALQsG+A9UHmG7RQBhmcQFaKY709bfhzSG3/jHymSg
 AKr0VSezE/DTj6URvxaq
 =qyY5
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2012-10-10 10:58:42 +09:00
commit 943c2acea5
55 changed files with 2277 additions and 809 deletions

View File

@ -0,0 +1,68 @@
* 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>;
};
};

View File

@ -0,0 +1,87 @@
* 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>;
};
};

View File

@ -9,12 +9,17 @@ Interpreted by the OF core:
Required properties:
- 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:
- cd-gpios: Specify GPIOs for card detection, 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
- 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
Example:

View File

@ -0,0 +1,25 @@
* 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>;
};

View File

@ -0,0 +1,53 @@
* 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.

View File

@ -0,0 +1,14 @@
* 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>;
};

View File

@ -0,0 +1,18 @@
* 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>;
};

View File

@ -0,0 +1,79 @@
* 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>;
};
};

View File

@ -1544,7 +1544,7 @@ S: Supported
F: drivers/rtc/rtc-bfin.c
BLACKFIN SDH DRIVER
M: Cliff Cai <cliff.cai@analog.com>
M: Sonic Zhang <sonic.zhang@analog.com>
L: uclinux-dist-devel@blackfin.uclinux.org
W: http://blackfin.uclinux.org
S: Supported
@ -5207,8 +5207,10 @@ S: Maintained
F: drivers/mmc/host/omap.c
OMAP HS MMC SUPPORT
M: Venkatraman S <svenkatr@ti.com>
L: linux-mmc@vger.kernel.org
L: linux-omap@vger.kernel.org
S: Orphan
S: Maintained
F: drivers/mmc/host/omap_hsmmc.c
OMAP RANDOM NUMBER GENERATOR SUPPORT

View File

@ -80,8 +80,7 @@
};
sdhci@70000000 {
int-gpio = <&gpio1 0 0>;
power-gpio = <&gpio1 2 1>;
cd-gpios = <&gpio1 0 0>;
status = "okay";
};

View File

@ -103,8 +103,6 @@
};
sdhci@70000000 {
power-gpio = <&gpio0 2 1>;
power_always_enb;
status = "okay";
};

View File

@ -26,6 +26,7 @@
#include <linux/suspend.h>
#include <linux/fault-inject.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@ -41,6 +42,12 @@
#include "sd_ops.h"
#include "sdio_ops.h"
/*
* Background operations can take a long time, depending on the housekeeping
* operations the card has to perform.
*/
#define MMC_BKOPS_MAX_TIMEOUT (4 * 60 * 1000) /* max time to wait in ms */
static struct workqueue_struct *workqueue;
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
@ -245,6 +252,70 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
host->ops->request(host, mrq);
}
/**
* mmc_start_bkops - start BKOPS for supported cards
* @card: MMC card to start BKOPS
* @form_exception: A flag to indicate if this function was
* called due to an exception raised by the card
*
* Start background operations whenever requested.
* When the urgent BKOPS bit is set in a R1 command response
* then background operations should be started immediately.
*/
void mmc_start_bkops(struct mmc_card *card, bool from_exception)
{
int err;
int timeout;
bool use_busy_signal;
BUG_ON(!card);
if (!card->ext_csd.bkops_en || mmc_card_doing_bkops(card))
return;
err = mmc_read_bkops_status(card);
if (err) {
pr_err("%s: Failed to read bkops status: %d\n",
mmc_hostname(card->host), err);
return;
}
if (!card->ext_csd.raw_bkops_status)
return;
if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 &&
from_exception)
return;
mmc_claim_host(card->host);
if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
timeout = MMC_BKOPS_MAX_TIMEOUT;
use_busy_signal = true;
} else {
timeout = 0;
use_busy_signal = false;
}
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal);
if (err) {
pr_warn("%s: Error %d starting bkops\n",
mmc_hostname(card->host), err);
goto out;
}
/*
* For urgent bkops status (LEVEL_2 and more)
* bkops executed synchronously, otherwise
* the operation is in progress
*/
if (!use_busy_signal)
mmc_card_set_doing_bkops(card);
out:
mmc_release_host(card->host);
}
EXPORT_SYMBOL(mmc_start_bkops);
static void mmc_wait_done(struct mmc_request *mrq)
{
complete(&mrq->completion);
@ -354,6 +425,14 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
if (host->areq) {
mmc_wait_for_req_done(host, host->areq->mrq);
err = host->areq->err_check(host->card, host->areq);
/*
* Check BKOPS urgency for each R1 response
*/
if (host->card && mmc_card_mmc(host->card) &&
((mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1) ||
(mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1B)) &&
(host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT))
mmc_start_bkops(host->card, true);
}
if (!err && areq)
@ -398,7 +477,7 @@ EXPORT_SYMBOL(mmc_wait_for_req);
* @card: the MMC card associated with the HPI transfer
*
* Issued High Priority Interrupt, and check for card status
* util out-of prg-state.
* until out-of prg-state.
*/
int mmc_interrupt_hpi(struct mmc_card *card)
{
@ -424,8 +503,9 @@ int mmc_interrupt_hpi(struct mmc_card *card)
case R1_STATE_IDLE:
case R1_STATE_READY:
case R1_STATE_STBY:
case R1_STATE_TRAN:
/*
* In idle states, HPI is not needed and the caller
* In idle and transfer states, HPI is not needed and the caller
* can issue the next intended command immediately
*/
goto out;
@ -488,6 +568,64 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries
EXPORT_SYMBOL(mmc_wait_for_cmd);
/**
* mmc_stop_bkops - stop ongoing BKOPS
* @card: MMC card to check BKOPS
*
* Send HPI command to stop ongoing background operations to
* allow rapid servicing of foreground operations, e.g. read/
* writes. Wait until the card comes out of the programming state
* to avoid errors in servicing read/write requests.
*/
int mmc_stop_bkops(struct mmc_card *card)
{
int err = 0;
BUG_ON(!card);
err = mmc_interrupt_hpi(card);
/*
* If err is EINVAL, we can't issue an HPI.
* It should complete the BKOPS.
*/
if (!err || (err == -EINVAL)) {
mmc_card_clr_doing_bkops(card);
err = 0;
}
return err;
}
EXPORT_SYMBOL(mmc_stop_bkops);
int mmc_read_bkops_status(struct mmc_card *card)
{
int err;
u8 *ext_csd;
/*
* In future work, we should consider storing the entire ext_csd.
*/
ext_csd = kmalloc(512, GFP_KERNEL);
if (!ext_csd) {
pr_err("%s: could not allocate buffer to receive the ext_csd.\n",
mmc_hostname(card->host));
return -ENOMEM;
}
mmc_claim_host(card->host);
err = mmc_send_ext_csd(card, ext_csd);
mmc_release_host(card->host);
if (err)
goto out;
card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXP_EVENTS_STATUS];
out:
kfree(ext_csd);
return err;
}
EXPORT_SYMBOL(mmc_read_bkops_status);
/**
* mmc_set_data_timeout - set the timeout for a data command
* @data: data phase for command
@ -975,7 +1113,8 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
int tmp;
int voltage;
/* REVISIT mmc_vddrange_to_ocrmask() may have set some
/*
* REVISIT mmc_vddrange_to_ocrmask() may have set some
* bits this regulator doesn't quite support ... don't
* be too picky, most cards and regulators are OK with
* a 0.1V range goof (it's a small error percentage).
@ -989,12 +1128,13 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
max_uV = min_uV + 100 * 1000;
}
/* avoid needless changes to this voltage; the regulator
* might not allow this operation
/*
* If we're using a fixed/static regulator, don't call
* regulator_set_voltage; it would fail.
*/
voltage = regulator_get_voltage(supply);
if (mmc->caps2 & MMC_CAP2_BROKEN_VOLTAGE)
if (regulator_count_voltages(supply) == 1)
min_uV = max_uV = voltage;
if (voltage < 0)
@ -1133,48 +1273,6 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
mmc_host_clk_release(host);
}
static void mmc_poweroff_notify(struct mmc_host *host)
{
struct mmc_card *card;
unsigned int timeout;
unsigned int notify_type = EXT_CSD_NO_POWER_NOTIFICATION;
int err = 0;
card = host->card;
mmc_claim_host(host);
/*
* Send power notify command only if card
* is mmc and notify state is powered ON
*/
if (card && mmc_card_mmc(card) &&
(card->poweroff_notify_state == MMC_POWERED_ON)) {
if (host->power_notify_type == MMC_HOST_PW_NOTIFY_SHORT) {
notify_type = EXT_CSD_POWER_OFF_SHORT;
timeout = card->ext_csd.generic_cmd6_time;
card->poweroff_notify_state = MMC_POWEROFF_SHORT;
} else {
notify_type = EXT_CSD_POWER_OFF_LONG;
timeout = card->ext_csd.power_off_longtime;
card->poweroff_notify_state = MMC_POWEROFF_LONG;
}
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_OFF_NOTIFICATION,
notify_type, timeout);
if (err && err != -EBADMSG)
pr_err("Device failed to respond within %d poweroff "
"time. Forcefully powering down the device\n",
timeout);
/* Set the card state to no notification after the poweroff */
card->poweroff_notify_state = MMC_NO_POWER_NOTIFICATION;
}
mmc_release_host(host);
}
/*
* Apply power to the MMC stack. This is a two-stage process.
* First, we enable power to the card without the clock running.
@ -1237,8 +1335,6 @@ static void mmc_power_up(struct mmc_host *host)
void mmc_power_off(struct mmc_host *host)
{
int err = 0;
if (host->ios.power_mode == MMC_POWER_OFF)
return;
@ -1247,22 +1343,6 @@ void mmc_power_off(struct mmc_host *host)
host->ios.clock = 0;
host->ios.vdd = 0;
/*
* For eMMC 4.5 device send AWAKE command before
* POWER_OFF_NOTIFY command, because in sleep state
* eMMC 4.5 devices respond to only RESET and AWAKE cmd
*/
if (host->card && mmc_card_is_sleep(host->card) &&
host->bus_ops->resume) {
err = host->bus_ops->resume(host);
if (!err)
mmc_poweroff_notify(host);
else
pr_warning("%s: error %d during resume "
"(continue with poweroff sequence)\n",
mmc_hostname(host), err);
}
/*
* Reset ocr mask to be the highest possible voltage supported for
@ -2052,6 +2132,11 @@ void mmc_rescan(struct work_struct *work)
if (host->rescan_disable)
return;
/* If there is a non-removable card registered, only scan once */
if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
return;
host->rescan_entered = 1;
mmc_bus_get(host);
/*
@ -2327,9 +2412,14 @@ int mmc_suspend_host(struct mmc_host *host)
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
if (host->bus_ops->suspend)
if (host->bus_ops->suspend) {
if (mmc_card_doing_bkops(host->card)) {
err = mmc_stop_bkops(host->card);
if (err)
goto out;
}
err = host->bus_ops->suspend(host);
}
if (err == -ENOSYS || !host->bus_ops->resume) {
/*
@ -2411,15 +2501,24 @@ int mmc_pm_notify(struct notifier_block *notify_block,
struct mmc_host *host = container_of(
notify_block, struct mmc_host, pm_notify);
unsigned long flags;
int err = 0;
switch (mode) {
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
if (host->card && mmc_card_mmc(host->card) &&
mmc_card_doing_bkops(host->card)) {
err = mmc_stop_bkops(host->card);
if (err) {
pr_err("%s: didn't stop bkops\n",
mmc_hostname(host));
return err;
}
mmc_card_clr_doing_bkops(host->card);
}
spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 1;
host->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT;
spin_unlock_irqrestore(&host->lock, flags);
cancel_delayed_work_sync(&host->detect);
@ -2443,7 +2542,6 @@ int mmc_pm_notify(struct notifier_block *notify_block,
spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 0;
host->power_notify_type = MMC_HOST_PW_NOTIFY_LONG;
spin_unlock_irqrestore(&host->lock, flags);
mmc_detect_change(host, 0);

View File

@ -281,7 +281,7 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
if (err)
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, "\n");
BUG_ON(n != EXT_CSD_STR_LEN);

View File

@ -463,6 +463,17 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
}
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 */
if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
card->ext_csd.hpi = 1;
@ -996,7 +1007,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* so check for success and update the flag
*/
if (!err)
card->poweroff_notify_state = MMC_POWERED_ON;
card->ext_csd.power_off_notification = EXT_CSD_POWER_ON;
}
/*
@ -1262,6 +1273,35 @@ 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.
*/
@ -1322,11 +1362,11 @@ static int mmc_suspend(struct mmc_host *host)
BUG_ON(!host->card);
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);
if (!err)
mmc_card_set_sleep(host->card);
} else if (!mmc_host_is_spi(host))
else if (!mmc_host_is_spi(host))
err = mmc_deselect_cards(host);
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
mmc_release_host(host);
@ -1348,11 +1388,7 @@ static int mmc_resume(struct mmc_host *host)
BUG_ON(!host->card);
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);
return err;
@ -1363,7 +1399,6 @@ static int mmc_power_restore(struct mmc_host *host)
int ret;
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
mmc_card_clr_sleep(host->card);
mmc_claim_host(host);
ret = mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host);

View File

@ -230,6 +230,10 @@ mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
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
mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
u32 opcode, void *buf, unsigned len)
@ -239,13 +243,19 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
struct mmc_data data = {0};
struct scatterlist sg;
void *data_buf;
int is_on_stack;
/* dma onto stack is unsafe/nonportable, but callers to this
* routine normally provide temporary on-stack buffers ...
*/
data_buf = kmalloc(len, GFP_KERNEL);
if (data_buf == NULL)
return -ENOMEM;
is_on_stack = object_is_on_stack(buf);
if (is_on_stack) {
/*
* dma onto stack is unsafe/nonportable, but callers to this
* routine normally provide temporary on-stack buffers ...
*/
data_buf = kmalloc(len, GFP_KERNEL);
if (!data_buf)
return -ENOMEM;
} else
data_buf = buf;
mrq.cmd = &cmd;
mrq.data = &data;
@ -280,8 +290,10 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
mmc_wait_for_req(host, &mrq);
memcpy(buf, data_buf, len);
kfree(data_buf);
if (is_on_stack) {
memcpy(buf, data_buf, len);
kfree(data_buf);
}
if (cmd.error)
return cmd.error;
@ -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 ret, i;
u32 *csd_tmp;
if (!mmc_host_is_spi(card->host))
return mmc_send_cxd_native(card->host, card->rca << 16,
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)
return ret;
goto err;
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 ret, i;
u32 *cid_tmp;
if (!mmc_host_is_spi(host)) {
if (!host->card)
@ -320,14 +340,20 @@ int mmc_send_cid(struct mmc_host *host, u32 *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)
return ret;
goto err;
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)
@ -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
* @set: cmd set values
* @index: EXT_CSD register index
* @value: value to program into EXT_CSD register
* @timeout_ms: timeout (ms) for operation performed by register write,
* 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.
*/
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms)
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms, bool use_busy_signal)
{
int err;
struct mmc_command cmd = {0};
@ -392,13 +419,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
(index << 16) |
(value << 8) |
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;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (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 */
do {
err = mmc_send_status(card, &status);
@ -423,6 +460,13 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
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);
int mmc_send_status(struct mmc_card *card, u32 *status)

View File

@ -193,14 +193,7 @@ static int sdio_bus_remove(struct device *dev)
}
#ifdef CONFIG_PM
static int pm_no_operation(struct device *dev)
{
return 0;
}
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(
pm_generic_runtime_suspend,
pm_generic_runtime_resume,

View File

@ -100,7 +100,13 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)
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);

View File

@ -540,6 +540,15 @@ config MMC_DW_PLTFM
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
tristate "Synopsys Designware MCI support on PCI bus"
depends on MMC_DW && PCI

View File

@ -39,6 +39,7 @@ obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
obj-$(CONFIG_MMC_DW) += dw_mmc.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_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o

View File

@ -140,6 +140,13 @@
#define atmci_writel(port,reg,value) \
__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:
* 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3.

View File

@ -19,6 +19,9 @@
#include <linux/interrupt.h>
#include <linux/ioport.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/scatterlist.h>
#include <linux/seq_file.h>
@ -71,7 +74,7 @@ enum atmci_pdc_buf {
};
struct atmel_mci_caps {
bool has_dma;
bool has_dma_conf_reg;
bool has_pdc;
bool has_cfg_reg;
bool has_cstor_reg;
@ -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, "IMR", buf[ATMCI_IMR / 4]);
if (host->caps.has_dma) {
if (host->caps.has_dma_conf_reg) {
u32 val;
val = buf[ATMCI_DMA / 4];
@ -500,6 +503,70 @@ err:
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)
{
return atmci_readl(host, ATMCI_VERSION) & 0x00000fff;
@ -774,7 +841,7 @@ static void atmci_dma_complete(void *arg)
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 */
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)
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,
data->sg_len, direction);
@ -2046,6 +2115,13 @@ static int __init atmci_init_slot(struct atmel_mci *host,
slot->sdc_reg = sdc_reg;
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->f_min = DIV_ROUND_UP(host->bus_hz, 512);
mmc->f_max = host->bus_hz / 2;
@ -2169,7 +2245,10 @@ static bool atmci_configure_dma(struct atmel_mci *host)
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;
/* Try to grab a DMA channel */
@ -2210,8 +2289,8 @@ static void __init atmci_get_cap(struct atmel_mci *host)
dev_info(&host->pdev->dev,
"version: 0x%x\n", version);
host->caps.has_dma = 0;
host->caps.has_pdc = 1;
host->caps.has_dma_conf_reg = 0;
host->caps.has_pdc = ATMCI_PDC_CONNECTED;
host->caps.has_cfg_reg = 0;
host->caps.has_cstor_reg = 0;
host->caps.has_highspeed = 0;
@ -2228,12 +2307,7 @@ static void __init atmci_get_cap(struct atmel_mci *host)
host->caps.has_odd_clk_div = 1;
case 0x400:
case 0x300:
#ifdef CONFIG_AT_HDMAC
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_dma_conf_reg = 1;
host->caps.has_pdc = 0;
host->caps.has_cfg_reg = 1;
host->caps.has_cstor_reg = 1;
@ -2268,8 +2342,14 @@ static int __init atmci_probe(struct platform_device *pdev)
if (!regs)
return -ENXIO;
pdata = pdev->dev.platform_data;
if (!pdata)
return -ENXIO;
if (!pdata) {
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);
if (irq < 0)
return irq;
@ -2308,7 +2388,7 @@ static int __init atmci_probe(struct platform_device *pdev)
/* Get MCI capabilities and set operations according to it */
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->submit_data = &atmci_submit_data_dma;
host->stop_transfer = &atmci_stop_transfer_dma;
@ -2487,6 +2567,7 @@ static struct platform_driver atmci_driver = {
.driver = {
.name = "atmel_mci",
.pm = ATMCI_PM_OPS,
.of_match_table = of_match_ptr(atmci_dt_ids),
},
};

View File

@ -24,9 +24,7 @@
#include <asm/portmux.h>
#include <asm/bfin_sdh.h>
#if defined(CONFIG_BF51x)
#define bfin_read_SDH_PWR_CTL bfin_read_RSI_PWR_CTL
#define bfin_write_SDH_PWR_CTL bfin_write_RSI_PWR_CTL
#if defined(CONFIG_BF51x) || defined(__ADSPBF60x__)
#define bfin_read_SDH_CLK_CTL bfin_read_RSI_CLK_CTL
#define bfin_write_SDH_CLK_CTL bfin_write_RSI_CLK_CTL
#define bfin_write_SDH_ARGUMENT bfin_write_RSI_ARGUMENT
@ -45,8 +43,16 @@
#define bfin_write_SDH_E_STATUS bfin_write_RSI_E_STATUS
#define bfin_read_SDH_STATUS bfin_read_RSI_STATUS
#define bfin_write_SDH_MASK0 bfin_write_RSI_MASK0
#define bfin_write_SDH_E_MASK bfin_write_RSI_E_MASK
#define bfin_read_SDH_CFG bfin_read_RSI_CFG
#define bfin_write_SDH_CFG bfin_write_RSI_CFG
# if defined(__ADSPBF60x__)
# define bfin_read_SDH_BLK_SIZE bfin_read_RSI_BLKSZ
# define bfin_write_SDH_BLK_SIZE bfin_write_RSI_BLKSZ
# else
# define bfin_read_SDH_PWR_CTL bfin_read_RSI_PWR_CTL
# define bfin_write_SDH_PWR_CTL bfin_write_RSI_PWR_CTL
# endif
#endif
struct sdh_host {
@ -62,6 +68,7 @@ struct sdh_host {
dma_addr_t sg_dma;
int dma_len;
unsigned long sclk;
unsigned int imask;
unsigned int power_mode;
unsigned int clk_div;
@ -127,11 +134,15 @@ static int sdh_setup_data(struct sdh_host *host, struct mmc_data *data)
/* Only supports power-of-2 block size */
if (data->blksz & (data->blksz - 1))
return -EINVAL;
#ifndef RSI_BLKSZ
data_ctl |= ((ffs(data->blksz) - 1) << 4);
#else
bfin_write_SDH_BLK_SIZE(data->blksz);
#endif
bfin_write_SDH_DATA_CTL(data_ctl);
/* the time of a host clock period in ns */
cycle_ns = 1000000000 / (get_sclk() / (2 * (host->clk_div + 1)));
cycle_ns = 1000000000 / (host->sclk / (2 * (host->clk_div + 1)));
timeout = data->timeout_ns / cycle_ns;
timeout += data->timeout_clks;
bfin_write_SDH_DATA_TIMER(timeout);
@ -145,8 +156,13 @@ static int sdh_setup_data(struct sdh_host *host, struct mmc_data *data)
sdh_enable_stat_irq(host, (DAT_CRC_FAIL | DAT_TIME_OUT | DAT_END));
host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma_dir);
#if defined(CONFIG_BF54x)
dma_cfg |= DMAFLOW_ARRAY | NDSIZE_5 | RESTART | WDSIZE_32 | DMAEN;
#if defined(CONFIG_BF54x) || defined(CONFIG_BF60x)
dma_cfg |= DMAFLOW_ARRAY | RESTART | WDSIZE_32 | DMAEN;
# ifdef RSI_BLKSZ
dma_cfg |= PSIZE_32 | NDSIZE_3;
# else
dma_cfg |= NDSIZE_5;
# endif
{
struct scatterlist *sg;
int i;
@ -156,7 +172,7 @@ static int sdh_setup_data(struct sdh_host *host, struct mmc_data *data)
host->sg_cpu[i].x_count = sg_dma_len(sg) / 4;
host->sg_cpu[i].x_modify = 4;
dev_dbg(mmc_dev(host->mmc), "%d: start_addr:0x%lx, "
"cfg:0x%x, x_count:0x%x, x_modify:0x%x\n",
"cfg:0x%lx, x_count:0x%lx, x_modify:0x%lx\n",
i, host->sg_cpu[i].start_addr,
host->sg_cpu[i].cfg, host->sg_cpu[i].x_count,
host->sg_cpu[i].x_modify);
@ -172,6 +188,7 @@ static int sdh_setup_data(struct sdh_host *host, struct mmc_data *data)
set_dma_curr_desc_addr(host->dma_ch, (unsigned long *)host->sg_dma);
set_dma_x_count(host->dma_ch, 0);
set_dma_x_modify(host->dma_ch, 0);
SSYNC();
set_dma_config(host->dma_ch, dma_cfg);
#elif defined(CONFIG_BF51x)
/* RSI DMA doesn't work in array mode */
@ -179,6 +196,7 @@ static int sdh_setup_data(struct sdh_host *host, struct mmc_data *data)
set_dma_start_addr(host->dma_ch, sg_dma_address(&data->sg[0]));
set_dma_x_count(host->dma_ch, length / 4);
set_dma_x_modify(host->dma_ch, 4);
SSYNC();
set_dma_config(host->dma_ch, dma_cfg);
#endif
bfin_write_SDH_DATA_CTL(bfin_read_SDH_DATA_CTL() | DTX_DMA_E | DTX_E);
@ -296,7 +314,6 @@ static int sdh_data_done(struct sdh_host *host, unsigned int stat)
else
data->bytes_xfered = 0;
sdh_disable_stat_irq(host, DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL | RX_OVERRUN | TX_UNDERRUN);
bfin_write_SDH_STATUS_CLR(DAT_END_STAT | DAT_TIMEOUT_STAT | \
DAT_CRC_FAIL_STAT | DAT_BLK_END_STAT | RX_OVERRUN | TX_UNDERRUN);
bfin_write_SDH_DATA_CTL(0);
@ -321,74 +338,115 @@ static void sdh_request(struct mmc_host *mmc, struct mmc_request *mrq)
dev_dbg(mmc_dev(host->mmc), "%s enter, mrp:%p, cmd:%p\n", __func__, mrq, mrq->cmd);
WARN_ON(host->mrq != NULL);
spin_lock(&host->lock);
host->mrq = mrq;
host->data = mrq->data;
if (mrq->data && mrq->data->flags & MMC_DATA_READ) {
ret = sdh_setup_data(host, mrq->data);
if (ret)
return;
goto data_err;
}
sdh_start_cmd(host, mrq->cmd);
data_err:
spin_unlock(&host->lock);
}
static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct sdh_host *host;
unsigned long flags;
u16 clk_ctl = 0;
#ifndef RSI_BLKSZ
u16 pwr_ctl = 0;
#endif
u16 cfg;
host = mmc_priv(mmc);
spin_lock_irqsave(&host->lock, flags);
if (ios->clock) {
unsigned long sys_clk, ios_clk;
spin_lock(&host->lock);
cfg = bfin_read_SDH_CFG();
cfg |= MWE;
switch (ios->bus_width) {
case MMC_BUS_WIDTH_4:
#ifndef RSI_BLKSZ
cfg &= ~PD_SDDAT3;
#endif
cfg |= PUP_SDDAT3;
/* Enable 4 bit SDIO */
cfg |= SD4E;
clk_ctl |= WIDE_BUS_4;
break;
case MMC_BUS_WIDTH_8:
#ifndef RSI_BLKSZ
cfg &= ~PD_SDDAT3;
#endif
cfg |= PUP_SDDAT3;
/* Disable 4 bit SDIO */
cfg &= ~SD4E;
clk_ctl |= BYTE_BUS_8;
break;
default:
cfg &= ~PUP_SDDAT3;
/* Disable 4 bit SDIO */
cfg &= ~SD4E;
}
host->power_mode = ios->power_mode;
#ifndef RSI_BLKSZ
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) {
pwr_ctl |= ROD_CTL;
# ifndef CONFIG_SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
pwr_ctl |= SD_CMD_OD;
# endif
}
if (ios->power_mode != MMC_POWER_OFF)
pwr_ctl |= PWR_ON;
else
pwr_ctl &= ~PWR_ON;
bfin_write_SDH_PWR_CTL(pwr_ctl);
#else
# ifndef CONFIG_SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
cfg |= SD_CMD_OD;
else
cfg &= ~SD_CMD_OD;
# endif
if (ios->power_mode != MMC_POWER_OFF)
cfg |= PWR_ON;
else
cfg &= ~PWR_ON;
bfin_write_SDH_CFG(cfg);
#endif
SSYNC();
if (ios->power_mode == MMC_POWER_ON && ios->clock) {
unsigned char clk_div;
ios_clk = 2 * ios->clock;
sys_clk = get_sclk();
clk_div = sys_clk / ios_clk;
if (sys_clk % ios_clk == 0)
clk_div -= 1;
clk_div = (get_sclk() / ios->clock - 1) / 2;
clk_div = min_t(unsigned char, clk_div, 0xFF);
clk_ctl |= clk_div;
clk_ctl |= CLK_E;
host->clk_div = clk_div;
bfin_write_SDH_CLK_CTL(clk_ctl);
} else
sdh_stop_clock(host);
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
#ifdef CONFIG_SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
pwr_ctl |= ROD_CTL;
#else
pwr_ctl |= SD_CMD_OD | ROD_CTL;
#endif
if (ios->bus_width == MMC_BUS_WIDTH_4) {
cfg = bfin_read_SDH_CFG();
cfg &= ~PD_SDDAT3;
cfg |= PUP_SDDAT3;
/* Enable 4 bit SDIO */
cfg |= (SD4E | MWE);
bfin_write_SDH_CFG(cfg);
clk_ctl |= WIDE_BUS;
} else {
cfg = bfin_read_SDH_CFG();
cfg |= MWE;
bfin_write_SDH_CFG(cfg);
}
bfin_write_SDH_CLK_CTL(clk_ctl);
host->power_mode = ios->power_mode;
/* set up sdh interrupt mask*/
if (ios->power_mode == MMC_POWER_ON)
pwr_ctl |= PWR_ON;
bfin_write_SDH_PWR_CTL(pwr_ctl);
bfin_write_SDH_MASK0(DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL |
RX_OVERRUN | TX_UNDERRUN | CMD_SENT | CMD_RESP_END |
CMD_TIME_OUT | CMD_CRC_FAIL);
else
bfin_write_SDH_MASK0(0);
SSYNC();
spin_unlock_irqrestore(&host->lock, flags);
spin_unlock(&host->lock);
dev_dbg(mmc_dev(host->mmc), "SDH: clk_div = 0x%x actual clock:%ld expected clock:%d\n",
host->clk_div,
@ -405,7 +463,7 @@ static irqreturn_t sdh_dma_irq(int irq, void *devid)
{
struct sdh_host *host = devid;
dev_dbg(mmc_dev(host->mmc), "%s enter, irq_stat: 0x%04x\n", __func__,
dev_dbg(mmc_dev(host->mmc), "%s enter, irq_stat: 0x%04lx\n", __func__,
get_dma_curr_irqstat(host->dma_ch));
clear_dma_irqstat(host->dma_ch);
SSYNC();
@ -420,6 +478,9 @@ static irqreturn_t sdh_stat_irq(int irq, void *devid)
int handled = 0;
dev_dbg(mmc_dev(host->mmc), "%s enter\n", __func__);
spin_lock(&host->lock);
status = bfin_read_SDH_E_STATUS();
if (status & SD_CARD_DET) {
mmc_detect_change(host->mmc, 0);
@ -437,11 +498,30 @@ static irqreturn_t sdh_stat_irq(int irq, void *devid)
if (status & (DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL | RX_OVERRUN | TX_UNDERRUN))
handled |= sdh_data_done(host, status);
spin_unlock(&host->lock);
dev_dbg(mmc_dev(host->mmc), "%s exit\n\n", __func__);
return IRQ_RETVAL(handled);
}
static void sdh_reset(void)
{
#if defined(CONFIG_BF54x)
/* Secure Digital Host shares DMA with Nand controller */
bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1);
#endif
bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN);
SSYNC();
/* Disable card inserting detection pin. set MMC_CAP_NEEDS_POLL, and
* mmc stack will do the detection.
*/
bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | (PUP_SDDAT | PUP_SDDAT3));
SSYNC();
}
static int __devinit sdh_probe(struct platform_device *pdev)
{
struct mmc_host *mmc;
@ -462,8 +542,16 @@ static int __devinit sdh_probe(struct platform_device *pdev)
}
mmc->ops = &sdh_ops;
mmc->max_segs = 32;
#if defined(CONFIG_BF51x)
mmc->max_segs = 1;
#else
mmc->max_segs = PAGE_SIZE / sizeof(struct dma_desc_array);
#endif
#ifdef RSI_BLKSZ
mmc->max_seg_size = -1;
#else
mmc->max_seg_size = 1 << 16;
#endif
mmc->max_blk_size = 1 << 11;
mmc->max_blk_count = 1 << 11;
mmc->max_req_size = PAGE_SIZE;
@ -473,6 +561,7 @@ static int __devinit sdh_probe(struct platform_device *pdev)
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_NEEDS_POLL;
host = mmc_priv(mmc);
host->mmc = mmc;
host->sclk = get_sclk();
spin_lock_init(&host->lock);
host->irq = drv_data->irq_int0;
@ -497,7 +586,6 @@ static int __devinit sdh_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, mmc);
mmc_add_host(mmc);
ret = request_irq(host->irq, sdh_stat_irq, 0, "SDH Status IRQ", host);
if (ret) {
@ -510,20 +598,10 @@ static int __devinit sdh_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "unable to request peripheral pins\n");
goto out4;
}
#if defined(CONFIG_BF54x)
/* Secure Digital Host shares DMA with Nand controller */
bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1);
#endif
bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN);
SSYNC();
/* Disable card inserting detection pin. set MMC_CAP_NEES_POLL, and
* mmc stack will do the detection.
*/
bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | (PUP_SDDAT | PUP_SDDAT3));
SSYNC();
sdh_reset();
mmc_add_host(mmc);
return 0;
out4:
@ -571,7 +649,6 @@ static int sdh_suspend(struct platform_device *dev, pm_message_t state)
if (mmc)
ret = mmc_suspend_host(mmc);
bfin_write_SDH_PWR_CTL(bfin_read_SDH_PWR_CTL() & ~PWR_ON);
peripheral_free_list(drv_data->pin_req);
return ret;
@ -589,16 +666,7 @@ static int sdh_resume(struct platform_device *dev)
return ret;
}
bfin_write_SDH_PWR_CTL(bfin_read_SDH_PWR_CTL() | PWR_ON);
#if defined(CONFIG_BF54x)
/* Secure Digital Host shares DMA with Nand controller */
bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1);
#endif
bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN);
SSYNC();
bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | (PUP_SDDAT | PUP_SDDAT3));
SSYNC();
sdh_reset();
if (mmc)
ret = mmc_resume_host(mmc);

View File

@ -30,11 +30,12 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/edma.h>
#include <linux/mmc/mmc.h>
#include <linux/platform_data/mmc-davinci.h>
#include <mach/edma.h>
/*
* Register Definitions
@ -200,21 +201,13 @@ struct mmc_davinci_host {
u32 bytes_left;
u32 rxdma, txdma;
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;
bool use_dma;
bool do_dma;
bool sdio_int;
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. */
unsigned int sg_len;
struct scatterlist *sg;
@ -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)
{
int sync_dev;
struct dma_chan *sync_dev;
if (host->data_dir == DAVINCI_MMC_DATADIR_READ)
sync_dev = host->rxdma;
sync_dev = host->dma_rx;
else
sync_dev = host->txdma;
sync_dev = host->dma_tx;
edma_stop(sync_dev);
edma_clean_channel(sync_dev);
dmaengine_terminate_all(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 */
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 edmacc_param *template;
int channel, slot;
unsigned link;
struct scatterlist *sg;
unsigned sg_len;
unsigned bytes_left = host->bytes_left;
const unsigned shift = ffs(rw_threshold) - 1;
struct dma_chan *chan;
struct dma_async_tx_descriptor *desc;
int ret = 0;
if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
template = &host->tx_template;
channel = host->txdma;
struct dma_slave_config dma_tx_conf = {
.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 {
template = &host->rx_template;
channel = host->rxdma;
struct dma_slave_config dma_rx_conf = {
.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);
dmaengine_submit(desc);
dma_async_issue_pending(chan);
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)
edma_clear_event(channel);
edma_start(channel);
out:
return ret;
}
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 mask = rw_threshold - 1;
int ret = 0;
host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
((data->flags & MMC_DATA_WRITE)
@ -583,70 +498,48 @@ static int mmc_davinci_start_dma_transfer(struct mmc_davinci_host *host,
}
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
davinci_release_dma_channels(struct mmc_davinci_host *host)
{
unsigned i;
if (!host->use_dma)
return;
for (i = 0; i < host->n_link; i++)
edma_free_slot(host->links[i]);
edma_free_channel(host->txdma);
edma_free_channel(host->rxdma);
dma_release_channel(host->dma_tx);
dma_release_channel(host->dma_rx);
}
static int __init davinci_acquire_dma_channels(struct mmc_davinci_host *host)
{
u32 link_size;
int r, i;
int r;
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;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
host->dma_tx =
dma_request_channel(mask, edma_filter_fn, &host->txdma);
if (!host->dma_tx) {
dev_err(mmc_dev(host->mmc), "Can't get dma_tx channel\n");
return -ENODEV;
}
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);
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;
}
mmc_davinci_dma_setup(host, false, &host->rx_template);
/* Allocate parameter RAM slots, which will later be bound to a
* channel as needed to handle a scatterlist.
*/
link_size = min_t(unsigned, host->nr_sg, ARRAY_SIZE(host->links));
for (i = 0; i < link_size; i++) {
r = edma_alloc_slot(EDMA_CTLR(host->txdma), EDMA_SLOT_ANY);
if (r < 0) {
dev_dbg(mmc_dev(host->mmc), "dma PaRAM alloc --> %d\n",
r);
break;
}
host->links[i] = r;
}
host->n_link = i;
return 0;
free_master_write:
edma_free_channel(host->txdma);
dma_release_channel(host->dma_tx);
return r;
}
@ -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
* 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) */
mmc->max_seg_size = MAX_CCNT * rw_threshold;

View File

@ -0,0 +1,253 @@
/*
* 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");

View File

@ -59,7 +59,7 @@ static int __devinit dw_mci_pci_probe(struct pci_dev *pdev,
host->irq = pdev->irq;
host->irq_flags = IRQF_SHARED;
host->dev = pdev->dev;
host->dev = &pdev->dev;
host->pdata = &pci_board_data;
host->regs = pci_iomap(pdev, PCI_BAR_NO, COMPLETE_BAR);
@ -140,18 +140,7 @@ static struct pci_driver dw_mci_pci_driver = {
},
};
static int __init dw_mci_init(void)
{
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_pci_driver(dw_mci_pci_driver);
MODULE_DESCRIPTION("DW Multimedia Card PCI Interface driver");
MODULE_AUTHOR("Shashidhar Hiremath <shashidharh@vayavyalabs.com>");

View File

@ -19,59 +19,63 @@
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/dw_mmc.h>
#include <linux/of.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 resource *regs;
int ret;
host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
if (!host)
return -ENOMEM;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs) {
ret = -ENXIO;
goto err_free;
}
if (!regs)
return -ENXIO;
host->irq = platform_get_irq(pdev, 0);
if (host->irq < 0) {
ret = host->irq;
goto err_free;
}
if (host->irq < 0)
return host->irq;
host->dev = pdev->dev;
host->drv_data = drv_data;
host->dev = &pdev->dev;
host->irq_flags = 0;
host->pdata = pdev->dev.platform_data;
ret = -ENOMEM;
host->regs = ioremap(regs->start, resource_size(regs));
host->regs = devm_request_and_ioremap(&pdev->dev, regs);
if (!host->regs)
goto err_free;
return -ENOMEM;
if (host->drv_data->init) {
ret = host->drv_data->init(host);
if (ret)
return ret;
}
platform_set_drvdata(pdev, host);
ret = dw_mci_probe(host);
if (ret)
goto err_out;
return ret;
err_out:
iounmap(host->regs);
err_free:
kfree(host);
return ret;
}
EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
static int __exit dw_mci_pltfm_remove(struct platform_device *pdev)
static int __devinit dw_mci_pltfm_probe(struct platform_device *pdev)
{
return dw_mci_pltfm_register(pdev, NULL);
}
static int __devexit dw_mci_pltfm_remove(struct platform_device *pdev)
{
struct dw_mci *host = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
dw_mci_remove(host);
iounmap(host->regs);
kfree(host);
return 0;
}
EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
#ifdef CONFIG_PM_SLEEP
/*
@ -105,12 +109,20 @@ static int dw_mci_pltfm_resume(struct device *dev)
#define dw_mci_pltfm_resume NULL
#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 = {
.remove = __exit_p(dw_mci_pltfm_remove),
.driver = {
.name = "dw_mmc",
.of_match_table = of_match_ptr(dw_mci_pltfm_match),
.pm = &dw_mci_pltfm_pmops,
},
};

View File

@ -0,0 +1,20 @@
/*
* 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_ */

View File

@ -33,6 +33,7 @@
#include <linux/bitops.h>
#include <linux/regulator/consumer.h>
#include <linux/workqueue.h>
#include <linux/of.h>
#include "dw_mmc.h"
@ -230,6 +231,7 @@ static void dw_mci_set_timeout(struct dw_mci *host)
static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
{
struct mmc_data *data;
struct dw_mci_slot *slot = mmc_priv(mmc);
u32 cmdr;
cmd->error = -EINPROGRESS;
@ -259,6 +261,9 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
cmdr |= SDMMC_CMD_DAT_WR;
}
if (slot->host->drv_data->prepare_command)
slot->host->drv_data->prepare_command(slot->host, &cmdr);
return cmdr;
}
@ -266,7 +271,7 @@ static void dw_mci_start_command(struct dw_mci *host,
struct mmc_command *cmd, u32 cmd_flags)
{
host->cmd = cmd;
dev_vdbg(&host->dev,
dev_vdbg(host->dev,
"start command: ARGR=0x%08x CMDR=0x%08x\n",
cmd->arg, cmd_flags);
@ -308,7 +313,7 @@ static void dw_mci_dma_cleanup(struct dw_mci *host)
if (data)
if (!data->host_cookie)
dma_unmap_sg(&host->dev,
dma_unmap_sg(host->dev,
data->sg,
data->sg_len,
dw_mci_get_dma_dir(data));
@ -334,7 +339,7 @@ static void dw_mci_idmac_complete_dma(struct dw_mci *host)
{
struct mmc_data *data = host->data;
dev_vdbg(&host->dev, "DMA complete\n");
dev_vdbg(host->dev, "DMA complete\n");
host->dma_ops->cleanup(host);
@ -405,23 +410,11 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
static int dw_mci_idmac_init(struct dw_mci *host)
{
struct idmac_desc *p;
int i, dma_support;
int i;
/* Number of descriptors in the ring buffer */
host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
/* Check if Hardware Configuration Register has support for DMA */
dma_support = (mci_readl(host, HCON) >> 16) & 0x3;
if (!dma_support || dma_support > 2) {
dev_err(&host->dev,
"Host Controller does not support IDMA Tx.\n");
host->dma_ops = NULL;
return -ENODEV;
}
dev_info(&host->dev, "Using internal DMA controller.\n");
/* Forward link the descriptor list */
for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
@ -476,7 +469,7 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host,
return -EINVAL;
}
sg_len = dma_map_sg(&host->dev,
sg_len = dma_map_sg(host->dev,
data->sg,
data->sg_len,
dw_mci_get_dma_dir(data));
@ -519,7 +512,7 @@ static void dw_mci_post_req(struct mmc_host *mmc,
return;
if (data->host_cookie)
dma_unmap_sg(&slot->host->dev,
dma_unmap_sg(slot->host->dev,
data->sg,
data->sg_len,
dw_mci_get_dma_dir(data));
@ -545,7 +538,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
host->using_dma = 1;
dev_vdbg(&host->dev,
dev_vdbg(host->dev,
"sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
sg_len);
@ -814,6 +807,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
slot->clock = ios->clock;
}
if (slot->host->drv_data->set_ios)
slot->host->drv_data->set_ios(slot->host, ios);
switch (ios->power_mode) {
case MMC_POWER_UP:
set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
@ -830,7 +826,9 @@ static int dw_mci_get_ro(struct mmc_host *mmc)
struct dw_mci_board *brd = slot->host->pdata;
/* Use platform get_ro function, else try on board write protect */
if (brd->get_ro)
if (brd->quirks & DW_MCI_QUIRK_NO_WRITE_PROTECT)
read_only = 0;
else if (brd->get_ro)
read_only = brd->get_ro(slot->id);
else
read_only =
@ -939,12 +937,12 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
slot = list_entry(host->queue.next,
struct dw_mci_slot, queue_node);
list_del(&slot->queue_node);
dev_vdbg(&host->dev, "list not empty: %s is next\n",
dev_vdbg(host->dev, "list not empty: %s is next\n",
mmc_hostname(slot->mmc));
host->state = STATE_SENDING_CMD;
dw_mci_start_request(host, slot);
} else {
dev_vdbg(&host->dev, "list empty\n");
dev_vdbg(host->dev, "list empty\n");
host->state = STATE_IDLE;
}
@ -1083,7 +1081,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
data->bytes_xfered = 0;
data->error = -ETIMEDOUT;
} else {
dev_err(&host->dev,
dev_err(host->dev,
"data FIFO error "
"(status=%08x)\n",
status);
@ -1767,12 +1765,60 @@ static void dw_mci_work_routine_card(struct work_struct *work)
}
}
static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
#ifdef CONFIG_OF
/* given a slot id, find out the device node representing that slot */
static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
{
struct device_node *np;
const __be32 *addr;
int len;
if (!dev || !dev->of_node)
return NULL;
for_each_child_of_node(dev->of_node, np) {
addr = of_get_property(np, "reg", &len);
if (!addr || (len < sizeof(int)))
continue;
if (be32_to_cpup(addr) == slot)
return np;
}
return NULL;
}
/* find out bus-width for a given slot */
static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot)
{
struct device_node *np = dw_mci_of_find_slot_node(dev, slot);
u32 bus_wd = 1;
if (!np)
return 1;
if (of_property_read_u32(np, "bus-width", &bus_wd))
dev_err(dev, "bus-width property not found, assuming width"
" as 1\n");
return bus_wd;
}
#else /* CONFIG_OF */
static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot)
{
return 1;
}
static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
{
return NULL;
}
#endif /* CONFIG_OF */
static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
{
struct mmc_host *mmc;
struct dw_mci_slot *slot;
int ctrl_id, ret;
u8 bus_width;
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->dev);
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
if (!mmc)
return -ENOMEM;
@ -1780,6 +1826,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
slot->id = id;
slot->mmc = mmc;
slot->host = host;
host->slot[id] = slot;
mmc->ops = &dw_mci_ops;
mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
@ -1800,21 +1847,44 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
if (host->pdata->caps)
mmc->caps = host->pdata->caps;
if (host->dev->of_node) {
ctrl_id = of_alias_get_id(host->dev->of_node, "mshc");
if (ctrl_id < 0)
ctrl_id = 0;
} else {
ctrl_id = to_platform_device(host->dev)->id;
}
if (host->drv_data && host->drv_data->caps)
mmc->caps |= host->drv_data->caps[ctrl_id];
if (host->pdata->caps2)
mmc->caps2 = host->pdata->caps2;
if (host->pdata->get_bus_wd)
if (host->pdata->get_bus_wd(slot->id) >= 4)
mmc->caps |= MMC_CAP_4_BIT_DATA;
bus_width = host->pdata->get_bus_wd(slot->id);
else if (host->dev->of_node)
bus_width = dw_mci_of_get_bus_wd(host->dev, slot->id);
else
bus_width = 1;
if (host->drv_data->setup_bus) {
struct device_node *slot_np;
slot_np = dw_mci_of_find_slot_node(host->dev, slot->id);
ret = host->drv_data->setup_bus(host, slot_np, bus_width);
if (ret)
goto err_setup_bus;
}
switch (bus_width) {
case 8:
mmc->caps |= MMC_CAP_8_BIT_DATA;
case 4:
mmc->caps |= MMC_CAP_4_BIT_DATA;
}
if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
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;
if (host->pdata->blk_settings) {
mmc->max_segs = host->pdata->blk_settings->max_segs;
mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
@ -1850,7 +1920,6 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
else
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
host->slot[id] = slot;
mmc_add_host(mmc);
#if defined(CONFIG_DEBUG_FS)
@ -1867,6 +1936,10 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
queue_work(host->card_workqueue, &host->card_work);
return 0;
err_setup_bus:
mmc_free_host(mmc);
return -EINVAL;
}
static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
@ -1884,10 +1957,10 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
static void dw_mci_init_dma(struct dw_mci *host)
{
/* Alloc memory for sg translation */
host->sg_cpu = dma_alloc_coherent(&host->dev, PAGE_SIZE,
host->sg_cpu = dma_alloc_coherent(host->dev, PAGE_SIZE,
&host->sg_dma, GFP_KERNEL);
if (!host->sg_cpu) {
dev_err(&host->dev, "%s: could not alloc DMA memory\n",
dev_err(host->dev, "%s: could not alloc DMA memory\n",
__func__);
goto no_dma;
}
@ -1895,6 +1968,7 @@ static void dw_mci_init_dma(struct dw_mci *host)
/* Determine which DMA interface to use */
#ifdef CONFIG_MMC_DW_IDMAC
host->dma_ops = &dw_mci_idmac_ops;
dev_info(&host->dev, "Using internal DMA controller.\n");
#endif
if (!host->dma_ops)
@ -1903,12 +1977,12 @@ static void dw_mci_init_dma(struct dw_mci *host)
if (host->dma_ops->init && host->dma_ops->start &&
host->dma_ops->stop && host->dma_ops->cleanup) {
if (host->dma_ops->init(host)) {
dev_err(&host->dev, "%s: Unable to initialize "
dev_err(host->dev, "%s: Unable to initialize "
"DMA Controller.\n", __func__);
goto no_dma;
}
} else {
dev_err(&host->dev, "DMA initialization not found.\n");
dev_err(host->dev, "DMA initialization not found.\n");
goto no_dma;
}
@ -1916,7 +1990,7 @@ static void dw_mci_init_dma(struct dw_mci *host)
return;
no_dma:
dev_info(&host->dev, "Using PIO mode.\n");
dev_info(host->dev, "Using PIO mode.\n");
host->use_dma = 0;
return;
}
@ -1942,30 +2016,133 @@ static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
return false;
}
#ifdef CONFIG_OF
static struct dw_mci_of_quirks {
char *quirk;
int id;
} of_quirks[] = {
{
.quirk = "supports-highspeed",
.id = DW_MCI_QUIRK_HIGHSPEED,
}, {
.quirk = "broken-cd",
.id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION,
},
};
static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
{
struct dw_mci_board *pdata;
struct device *dev = host->dev;
struct device_node *np = dev->of_node;
int idx, ret;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dev_err(dev, "could not allocate memory for pdata\n");
return ERR_PTR(-ENOMEM);
}
/* find out number of slots supported */
if (of_property_read_u32(dev->of_node, "num-slots",
&pdata->num_slots)) {
dev_info(dev, "num-slots property not found, "
"assuming 1 slot is available\n");
pdata->num_slots = 1;
}
/* get quirks */
for (idx = 0; idx < ARRAY_SIZE(of_quirks); idx++)
if (of_get_property(np, of_quirks[idx].quirk, NULL))
pdata->quirks |= of_quirks[idx].id;
if (of_property_read_u32(np, "fifo-depth", &pdata->fifo_depth))
dev_info(dev, "fifo-depth property not found, using "
"value of FIFOTH register as default\n");
of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);
if (host->drv_data->parse_dt) {
ret = host->drv_data->parse_dt(host);
if (ret)
return ERR_PTR(ret);
}
return pdata;
}
#else /* CONFIG_OF */
static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
{
return ERR_PTR(-EINVAL);
}
#endif /* CONFIG_OF */
int dw_mci_probe(struct dw_mci *host)
{
int width, i, ret = 0;
u32 fifo_size;
int init_slots = 0;
if (!host->pdata || !host->pdata->init) {
dev_err(&host->dev,
"Platform data must supply init function\n");
return -ENODEV;
if (!host->pdata) {
host->pdata = dw_mci_parse_dt(host);
if (IS_ERR(host->pdata)) {
dev_err(host->dev, "platform data not available\n");
return -EINVAL;
}
}
if (!host->pdata->select_slot && host->pdata->num_slots > 1) {
dev_err(&host->dev,
dev_err(host->dev,
"Platform data must supply select_slot function\n");
return -ENODEV;
}
if (!host->pdata->bus_hz) {
dev_err(&host->dev,
"Platform data must supply bus speed\n");
return -ENODEV;
host->biu_clk = clk_get(host->dev, "biu");
if (IS_ERR(host->biu_clk)) {
dev_dbg(host->dev, "biu clock not available\n");
} else {
ret = clk_prepare_enable(host->biu_clk);
if (ret) {
dev_err(host->dev, "failed to enable biu clock\n");
clk_put(host->biu_clk);
return ret;
}
}
host->ciu_clk = clk_get(host->dev, "ciu");
if (IS_ERR(host->ciu_clk)) {
dev_dbg(host->dev, "ciu clock not available\n");
} else {
ret = clk_prepare_enable(host->ciu_clk);
if (ret) {
dev_err(host->dev, "failed to enable ciu clock\n");
clk_put(host->ciu_clk);
goto err_clk_biu;
}
}
if (IS_ERR(host->ciu_clk))
host->bus_hz = host->pdata->bus_hz;
else
host->bus_hz = clk_get_rate(host->ciu_clk);
if (host->drv_data->setup_clock) {
ret = host->drv_data->setup_clock(host);
if (ret) {
dev_err(host->dev,
"implementation specific clock setup failed\n");
goto err_clk_ciu;
}
}
if (!host->bus_hz) {
dev_err(host->dev,
"Platform data must supply bus speed\n");
ret = -ENODEV;
goto err_clk_ciu;
}
host->bus_hz = host->pdata->bus_hz;
host->quirks = host->pdata->quirks;
spin_lock_init(&host->lock);
@ -1998,7 +2175,7 @@ int dw_mci_probe(struct dw_mci *host)
}
/* Reset all blocks */
if (!mci_wait_reset(&host->dev, host))
if (!mci_wait_reset(host->dev, host))
return -ENODEV;
host->dma_ops = host->pdata->dma_ops;
@ -2054,10 +2231,18 @@ int dw_mci_probe(struct dw_mci *host)
/* We need at least one slot to succeed */
for (i = 0; i < host->num_slots; i++) {
ret = dw_mci_init_slot(host, i);
if (ret) {
ret = -ENODEV;
goto err_init_slot;
}
if (ret)
dev_dbg(host->dev, "slot %d init failed\n", i);
else
init_slots++;
}
if (init_slots) {
dev_info(host->dev, "%d slots initialized\n", init_slots);
} else {
dev_dbg(host->dev, "attempted to initialize %d slots, "
"but failed on all\n", host->num_slots);
goto err_init_slot;
}
/*
@ -2065,7 +2250,7 @@ int dw_mci_probe(struct dw_mci *host)
* Need to check the version-id and set data-offset for DATA register.
*/
host->verid = SDMMC_GET_VERID(mci_readl(host, VERID));
dev_info(&host->dev, "Version ID is %04x\n", host->verid);
dev_info(host->dev, "Version ID is %04x\n", host->verid);
if (host->verid < DW_MMC_240A)
host->data_offset = DATA_OFFSET;
@ -2082,22 +2267,16 @@ int dw_mci_probe(struct dw_mci *host)
DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */
dev_info(&host->dev, "DW MMC controller at irq %d, "
dev_info(host->dev, "DW MMC controller at irq %d, "
"%d bit host data width, "
"%u deep fifo\n",
host->irq, width, fifo_size);
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
dev_info(&host->dev, "Internal DMAC interrupt fix enabled.\n");
dev_info(host->dev, "Internal DMAC interrupt fix enabled.\n");
return 0;
err_init_slot:
/* De-init any initialized slots */
while (i > 0) {
if (host->slot[i])
dw_mci_cleanup_slot(host->slot[i], i);
i--;
}
free_irq(host->irq, host);
err_workqueue:
@ -2106,13 +2285,24 @@ err_workqueue:
err_dmaunmap:
if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host);
dma_free_coherent(&host->dev, PAGE_SIZE,
dma_free_coherent(host->dev, PAGE_SIZE,
host->sg_cpu, host->sg_dma);
if (host->vmmc) {
regulator_disable(host->vmmc);
regulator_put(host->vmmc);
}
err_clk_ciu:
if (!IS_ERR(host->ciu_clk)) {
clk_disable_unprepare(host->ciu_clk);
clk_put(host->ciu_clk);
}
err_clk_biu:
if (!IS_ERR(host->biu_clk)) {
clk_disable_unprepare(host->biu_clk);
clk_put(host->biu_clk);
}
return ret;
}
EXPORT_SYMBOL(dw_mci_probe);
@ -2125,7 +2315,7 @@ void dw_mci_remove(struct dw_mci *host)
mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
for (i = 0; i < host->num_slots; i++) {
dev_dbg(&host->dev, "remove slot %d\n", i);
dev_dbg(host->dev, "remove slot %d\n", i);
if (host->slot[i])
dw_mci_cleanup_slot(host->slot[i], i);
}
@ -2136,7 +2326,7 @@ void dw_mci_remove(struct dw_mci *host)
free_irq(host->irq, host);
destroy_workqueue(host->card_workqueue);
dma_free_coherent(&host->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
dma_free_coherent(host->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host);
@ -2146,6 +2336,12 @@ void dw_mci_remove(struct dw_mci *host)
regulator_put(host->vmmc);
}
if (!IS_ERR(host->ciu_clk))
clk_disable_unprepare(host->ciu_clk);
if (!IS_ERR(host->biu_clk))
clk_disable_unprepare(host->biu_clk);
clk_put(host->ciu_clk);
clk_put(host->biu_clk);
}
EXPORT_SYMBOL(dw_mci_remove);
@ -2188,7 +2384,7 @@ int dw_mci_resume(struct dw_mci *host)
if (host->vmmc)
regulator_enable(host->vmmc);
if (!mci_wait_reset(&host->dev, host)) {
if (!mci_wait_reset(host->dev, host)) {
ret = -ENODEV;
return ret;
}

View File

@ -182,4 +182,28 @@ extern int dw_mci_suspend(struct dw_mci *host);
extern int dw_mci_resume(struct dw_mci *host);
#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_ */

View File

@ -1532,20 +1532,7 @@ static struct spi_driver mmc_spi_driver = {
.remove = __devexit_p(mmc_spi_remove),
};
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_spi_driver(mmc_spi_driver);
MODULE_AUTHOR("Mike Lavender, David Brownell, "
"Hans-Peter Nilsson, Jan Nikitenko");

View File

@ -1309,14 +1309,10 @@ static int __devinit mmci_probe(struct amba_device *dev,
goto host_free;
}
ret = clk_prepare(host->clk);
ret = clk_prepare_enable(host->clk);
if (ret)
goto clk_free;
ret = clk_enable(host->clk);
if (ret)
goto clk_unprep;
host->plat = plat;
host->variant = variant;
host->mclk = clk_get_rate(host->clk);
@ -1515,9 +1511,7 @@ static int __devinit mmci_probe(struct amba_device *dev,
err_gpio_cd:
iounmap(host->base);
clk_disable:
clk_disable(host->clk);
clk_unprep:
clk_unprepare(host->clk);
clk_disable_unprepare(host->clk);
clk_free:
clk_put(host->clk);
host_free:
@ -1564,8 +1558,7 @@ static int __devexit mmci_remove(struct amba_device *dev)
gpio_free(host->gpio_cd);
iounmap(host->base);
clk_disable(host->clk);
clk_unprepare(host->clk);
clk_disable_unprepare(host->clk);
clk_put(host->clk);
if (host->vcc)

View File

@ -44,6 +44,7 @@
#include <mach/hardware.h>
#define DRIVER_NAME "mxc-mmc"
#define MXCMCI_TIMEOUT_MS 10000
#define MMC_REG_STR_STP_CLK 0x00
#define MMC_REG_STATUS 0x04
@ -150,6 +151,8 @@ struct mxcmci_host {
int dmareq;
struct dma_slave_config dma_slave_config;
struct imx_dma_data dma_data;
struct timer_list watchdog;
};
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)
dmaengine_submit(host->desc);
dma_async_issue_pending(host->dma);
mod_timer(&host->watchdog, jiffies + msecs_to_jiffies(MXCMCI_TIMEOUT_MS));
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,
unsigned int cmdat)
{
@ -305,8 +331,14 @@ static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
int_cntr = INT_END_CMD_RES_EN;
if (mxcmci_use_dma(host))
int_cntr |= INT_READ_OP_EN | INT_WRITE_OP_DONE_EN;
if (mxcmci_use_dma(host)) {
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);
if (host->use_sdio)
@ -345,11 +377,9 @@ static int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat)
struct mmc_data *data = host->data;
int data_error;
if (mxcmci_use_dma(host)) {
dmaengine_terminate_all(host->dma);
if (mxcmci_use_dma(host))
dma_unmap_sg(host->dma->device->dev, data->sg, data->sg_len,
host->dma_dir);
}
if (stat & STATUS_ERR_MASK) {
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)
mxcmci_cmd_done(host, stat);
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);
}
if (host->default_irq_mask &&
(stat & (STATUS_CARD_INSERTION | STATUS_CARD_REMOVAL)))
@ -836,6 +868,34 @@ static bool filter(struct dma_chan *chan, void *param)
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 = {
.request = mxcmci_request,
.set_ios = mxcmci_set_ios,
@ -968,6 +1028,10 @@ static int mxcmci_probe(struct platform_device *pdev)
mmc_add_host(mmc);
init_timer(&host->watchdog);
host->watchdog.function = &mxcmci_watchdog;
host->watchdog.data = (unsigned long)mmc;
return 0;
out_free_irq:

View File

@ -27,16 +27,10 @@
#include <linux/mmc/card.h>
#include <linux/clk.h>
#include <linux/scatterlist.h>
#include <linux/i2c/tps65010.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <plat/mmc.h>
#include <asm/gpio.h>
#include <plat/dma.h>
#include <plat/fpga.h>
#define OMAP_MMC_REG_CMD 0x00
#define OMAP_MMC_REG_ARGL 0x01
@ -105,7 +99,6 @@ struct mmc_omap_slot {
u16 saved_con;
u16 bus_mode;
unsigned int fclk_freq;
unsigned powered:1;
struct tasklet_struct cover_tasklet;
struct timer_list cover_timer;
@ -137,7 +130,6 @@ struct mmc_omap_host {
unsigned int phys_base;
int irq;
unsigned char bus_mode;
unsigned char hw_bus_mode;
unsigned int reg_shift;
struct work_struct cmd_abort_work;
@ -695,22 +687,29 @@ mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
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[] = {
"EOC", "CD", "CB", "BRS", "EOFB", "DTO", "DCRC", "CTO",
"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++)
if (status & (1 << i)) {
if (c)
printk(" ");
printk("%s", mmc_omap_status_bits[i]);
c++;
}
if (status & (1 << i))
buf += sprintf(buf, " %s", mmc_omap_status_bits[i]);
dev_vdbg(mmc_dev(host->mmc), "%s\n", res);
}
#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)
{
@ -744,12 +743,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
cmd = host->cmd->opcode;
else
cmd = -1;
#ifdef CONFIG_MMC_DEBUG
dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ",
status, cmd);
mmc_omap_report_irq(status);
printk("\n");
#endif
mmc_omap_report_irq(host, status);
if (host->total_bytes_left) {
if ((status & OMAP_MMC_STAT_A_FULL) ||
(status & OMAP_MMC_STAT_END_OF_DATA))

View File

@ -35,7 +35,6 @@
#include <linux/mmc/core.h>
#include <linux/mmc/mmc.h>
#include <linux/io.h>
#include <linux/semaphore.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
@ -44,7 +43,6 @@
#include <plat/cpu.h>
/* OMAP HSMMC Host Controller Registers */
#define OMAP_HSMMC_SYSCONFIG 0x0010
#define OMAP_HSMMC_SYSSTATUS 0x0014
#define OMAP_HSMMC_CON 0x002C
#define OMAP_HSMMC_BLK 0x0104
@ -161,8 +159,6 @@ struct omap_hsmmc_host {
unsigned int dma_sg_idx;
unsigned char bus_mode;
unsigned char power_mode;
u32 *buffer;
u32 bytesleft;
int suspended;
int irq;
int use_dma, dma_ch;
@ -171,7 +167,6 @@ struct omap_hsmmc_host {
int slot_id;
int response_busy;
int context_loss;
int vdd;
int protect_card;
int reqs_blocked;
int use_reg;
@ -300,12 +295,12 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
struct regulator *reg;
int ocr_value = 0;
mmc_slot(host).set_power = omap_hsmmc_set_power;
reg = regulator_get(host->dev, "vmmc");
if (IS_ERR(reg)) {
dev_dbg(host->dev, "vmmc regulator missing\n");
return PTR_ERR(reg);
} else {
mmc_slot(host).set_power = omap_hsmmc_set_power;
host->vcc = reg;
ocr_value = mmc_regulator_get_ocrmask(reg);
if (!mmc_slot(host).ocr_mask) {
@ -495,7 +490,7 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
unsigned long regval;
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);
@ -579,21 +574,8 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
if (host->context_loss == context_loss)
return 1;
/* Wait for hardware reset */
timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
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 (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE)
return 1;
if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
if (host->power_mode != MMC_POWER_OFF &&
@ -745,7 +727,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
{
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);
host->cmd = cmd;
@ -934,7 +916,7 @@ static void omap_hsmmc_dbg_report_irq(struct omap_hsmmc_host *host, u32 status)
buf += len;
}
dev_dbg(mmc_dev(host->mmc), "%s\n", res);
dev_vdbg(mmc_dev(host->mmc), "%s\n", res);
}
#else
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,
__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)
{
struct mmc_data *data;
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;
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) {
omap_hsmmc_dbg_report_irq(host, status);
if ((status & CMD_TIMEOUT) ||
(status & CMD_CRC)) {
if (host->cmd) {
if (status & CMD_TIMEOUT) {
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 (status & (CMD_TIMEOUT | DATA_TIMEOUT))
hsmmc_command_incomplete(host, -ETIMEDOUT);
else if (status & (CMD_CRC | DATA_CRC))
hsmmc_command_incomplete(host, -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;
if (host->data)
end_trans = 1;
end_cmd = 1;
if (host->data || host->response_busy) {
end_trans = 1;
host->response_busy = 0;
}
}
OMAP_HSMMC_WRITE(host->base, STAT, status);
if (end_cmd || ((status & CC) && host->cmd))
omap_hsmmc_cmd_done(host, host->cmd);
if ((end_trans || (status & TC)) && host->mrq)
@ -1062,11 +1012,13 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
int status;
status = OMAP_HSMMC_READ(host->base, STAT);
do {
while (status & INT_EN_MASK && host->req_in_progress) {
omap_hsmmc_do_irq(host, status);
/* Flush posted write */
OMAP_HSMMC_WRITE(host->base, STAT, status);
status = OMAP_HSMMC_READ(host->base, STAT);
} while (status & INT_EN_MASK);
}
return IRQ_HANDLED;
}
@ -1501,12 +1453,10 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
case MMC_POWER_OFF:
mmc_slot(host).set_power(host->dev, host->slot_id,
0, 0);
host->vdd = 0;
break;
case MMC_POWER_UP:
mmc_slot(host).set_power(host->dev, host->slot_id,
1, ios->vdd);
host->vdd = ios->vdd;
break;
case MMC_POWER_ON:
do_send_init_stream = 1;
@ -1598,10 +1548,6 @@ static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
value = OMAP_HSMMC_READ(host->base, 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(host);
}
@ -1659,8 +1605,6 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
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",
OMAP_HSMMC_READ(host->base, CON));
seq_printf(s, "HCTL:\t\t0x%08x\n",
@ -2105,8 +2049,7 @@ static int omap_hsmmc_suspend(struct device *dev)
if (ret) {
host->suspended = 0;
if (host->pdata->resume) {
ret = host->pdata->resume(dev, host->slot_id);
if (ret)
if (host->pdata->resume(dev, host->slot_id))
dev_dbg(dev, "Unmask interrupt failed\n");
}
goto err;

View File

@ -30,6 +30,9 @@
#include <linux/regulator/consumer.h>
#include <linux/gpio.h>
#include <linux/gfp.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
#include <asm/sizes.h>
@ -573,6 +576,50 @@ static irqreturn_t pxamci_detect_irq(int irq, void *devid)
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)
{
struct mmc_host *mmc;
@ -580,6 +627,10 @@ static int pxamci_probe(struct platform_device *pdev)
struct resource *r, *dmarx, *dmatx;
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);
irq = platform_get_irq(pdev, 0);
if (!r || irq < 0)
@ -866,6 +917,7 @@ static struct platform_driver pxamci_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(pxa_mmc_dt_ids),
#ifdef CONFIG_PM
.pm = &pxamci_pm_ops,
#endif

View File

@ -24,6 +24,7 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/mmc/host.h>
#include <linux/of.h>
#include "sdhci-pltfm.h"
@ -126,11 +127,18 @@ static int __devexit sdhci_dove_remove(struct platform_device *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 = {
.driver = {
.name = "sdhci-dove",
.owner = THIS_MODULE,
.pm = SDHCI_PLTFM_PMOPS,
.of_match_table = of_match_ptr(sdhci_dove_of_match_table),
},
.probe = sdhci_dove_probe,
.remove = __devexit_p(sdhci_dove_remove),

View File

@ -21,6 +21,32 @@
#include "sdhci-pltfm.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)
{
u16 ret;
@ -144,7 +170,7 @@ static void esdhc_of_resume(struct sdhci_host *host)
#endif
static struct sdhci_ops sdhci_esdhc_ops = {
.read_l = sdhci_be32bs_readl,
.read_l = esdhc_readl,
.read_w = esdhc_readw,
.read_b = esdhc_readb,
.write_l = sdhci_be32bs_writel,
@ -161,9 +187,13 @@ static struct sdhci_ops sdhci_esdhc_ops = {
};
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
| SDHCI_QUIRK_NO_CARD_NO_RESET,
| SDHCI_QUIRK_NO_CARD_NO_RESET
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.ops = &sdhci_esdhc_ops,
};

View File

@ -1476,24 +1476,7 @@ static struct 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_pci_driver(sdhci_driver);
MODULE_AUTHOR("Pierre Ossman <pierre@ossman.eu>");
MODULE_DESCRIPTION("Secure Digital Host Controller Interface PCI driver");

View File

@ -75,6 +75,9 @@ void sdhci_get_of_property(struct platform_device *pdev)
if (sdhci_of_wp_inverted(np))
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"))
host->quirks |= SDHCI_QUIRK_BROKEN_DMA;

View File

@ -197,7 +197,7 @@ static int __devinit sdhci_pxav2_probe(struct platform_device *pdev)
goto err_clk_get;
}
pltfm_host->clk = clk;
clk_enable(clk);
clk_prepare_enable(clk);
host->quirks = SDHCI_QUIRK_BROKEN_ADMA
| SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
@ -239,7 +239,7 @@ static int __devinit sdhci_pxav2_probe(struct platform_device *pdev)
return 0;
err_add_host:
clk_disable(clk);
clk_disable_unprepare(clk);
clk_put(clk);
err_clk_get:
sdhci_pltfm_free(pdev);
@ -255,7 +255,7 @@ static int __devexit sdhci_pxav2_remove(struct platform_device *pdev)
sdhci_remove_host(host, 1);
clk_disable(pltfm_host->clk);
clk_disable_unprepare(pltfm_host->clk);
clk_put(pltfm_host->clk);
sdhci_pltfm_free(pdev);
kfree(pxa);

View File

@ -24,12 +24,14 @@
#include <linux/gpio.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/platform_data/pxa_sdhci.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include "sdhci.h"
#include "sdhci-pltfm.h"
@ -182,6 +184,7 @@ static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev)
struct device_node *np = dev->of_node;
u32 bus_width;
u32 clk_delay_cycles;
enum of_gpio_flags gpio_flags;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@ -198,6 +201,10 @@ static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev)
if (clk_delay_cycles > 0)
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;
}
#else
@ -231,14 +238,14 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev)
pltfm_host = sdhci_priv(host);
pltfm_host->priv = pxa;
clk = clk_get(dev, "PXA-SDHCLK");
clk = clk_get(dev, NULL);
if (IS_ERR(clk)) {
dev_err(dev, "failed to get io clock\n");
ret = PTR_ERR(clk);
goto err_clk_get;
}
pltfm_host->clk = clk;
clk_enable(clk);
clk_prepare_enable(clk);
host->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
@ -266,12 +273,25 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev)
host->quirks |= pdata->quirks;
if (pdata->host_caps)
host->mmc->caps |= pdata->host_caps;
if (pdata->host_caps2)
host->mmc->caps2 |= pdata->host_caps2;
if (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;
sdhci_get_of_property(pdev);
ret = sdhci_add_host(host);
if (ret) {
dev_err(&pdev->dev, "failed to add host\n");
@ -283,8 +303,10 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev)
return 0;
err_add_host:
clk_disable(clk);
clk_disable_unprepare(clk);
clk_put(clk);
mmc_gpio_free_cd(host->mmc);
err_cd_req:
err_clk_get:
sdhci_pltfm_free(pdev);
kfree(pxa);
@ -296,11 +318,16 @@ static int __devexit sdhci_pxav3_remove(struct platform_device *pdev)
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_pxa *pxa = pltfm_host->priv;
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
sdhci_remove_host(host, 1);
clk_disable(pltfm_host->clk);
clk_disable_unprepare(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);
kfree(pxa);

View File

@ -34,6 +34,9 @@
#define MAX_BUS_CLK (4)
/* Number of gpio's used is max data bus width + command and clock lines */
#define NUM_GPIOS(x) (x + 2)
/**
* struct sdhci_s3c - S3C SDHCI instance
* @host: The SDHCI host created
@ -41,6 +44,7 @@
* @ioarea: The resource created when we claimed the IO area.
* @pdata: The platform data for this controller.
* @cur_clk: The index of the current bus clock.
* @gpios: List of gpio numbers parsed from device tree.
* @clk_io: The clock for the internal bus interface.
* @clk_bus: The clocks that are available for the SD/MMC bus clock.
*/
@ -52,6 +56,7 @@ struct sdhci_s3c {
unsigned int cur_clk;
int ext_cd_irq;
int ext_cd_gpio;
int *gpios;
struct clk *clk_io;
struct clk *clk_bus[MAX_BUS_CLK];
@ -166,7 +171,7 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
dev_dbg(&ourhost->pdev->dev, "clk %d: rate %ld, want %d, got %ld\n",
src, rate, wanted, rate / div);
return (wanted - (rate / div));
return wanted - (rate / div);
}
/**
@ -203,10 +208,12 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
best_src, clock, best);
/* select the new clock source */
if (ourhost->cur_clk != best_src) {
struct clk *clk = ourhost->clk_bus[best_src];
clk_enable(clk);
clk_disable(ourhost->clk_bus[ourhost->cur_clk]);
/* turn clock off to card before changing clock source */
writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
@ -288,6 +295,7 @@ static unsigned int sdhci_cmu_get_min_clock(struct sdhci_host *host)
static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_s3c *ourhost = to_s3c(host);
struct device *dev = &ourhost->pdev->dev;
unsigned long timeout;
u16 clk = 0;
@ -309,8 +317,8 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
& SDHCI_CLOCK_INT_STABLE)) {
if (timeout == 0) {
printk(KERN_ERR "%s: Internal clock never "
"stabilised.\n", mmc_hostname(host->mmc));
dev_err(dev, "%s: Internal clock never stabilised.\n",
mmc_hostname(host->mmc));
return;
}
timeout--;
@ -404,7 +412,9 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc)
if (sc->ext_cd_irq &&
request_threaded_irq(sc->ext_cd_irq, NULL,
sdhci_s3c_gpio_card_detect_thread,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
dev_name(dev), sc) == 0) {
int status = gpio_get_value(sc->ext_cd_gpio);
if (pdata->ext_cd_gpio_invert)
@ -419,9 +429,121 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc)
}
}
#ifdef CONFIG_OF
static int __devinit sdhci_s3c_parse_dt(struct device *dev,
struct sdhci_host *host, struct s3c_sdhci_platdata *pdata)
{
struct device_node *node = dev->of_node;
struct sdhci_s3c *ourhost = to_s3c(host);
u32 max_width;
int gpio, cnt, ret;
/* if the bus-width property is not specified, assume width as 1 */
if (of_property_read_u32(node, "bus-width", &max_width))
max_width = 1;
pdata->max_width = max_width;
ourhost->gpios = devm_kzalloc(dev, NUM_GPIOS(pdata->max_width) *
sizeof(int), GFP_KERNEL);
if (!ourhost->gpios)
return -ENOMEM;
/* get the card detection method */
if (of_get_property(node, "broken-cd", 0)) {
pdata->cd_type = S3C_SDHCI_CD_NONE;
goto setup_bus;
}
if (of_get_property(node, "non-removable", 0)) {
pdata->cd_type = S3C_SDHCI_CD_PERMANENT;
goto setup_bus;
}
gpio = of_get_named_gpio(node, "cd-gpios", 0);
if (gpio_is_valid(gpio)) {
pdata->cd_type = S3C_SDHCI_CD_GPIO;
goto found_cd;
} else if (gpio != -ENOENT) {
dev_err(dev, "invalid card detect gpio specified\n");
return -EINVAL;
}
gpio = of_get_named_gpio(node, "samsung,cd-pinmux-gpio", 0);
if (gpio_is_valid(gpio)) {
pdata->cd_type = S3C_SDHCI_CD_INTERNAL;
goto found_cd;
} else if (gpio != -ENOENT) {
dev_err(dev, "invalid card detect gpio specified\n");
return -EINVAL;
}
dev_info(dev, "assuming no card detect line available\n");
pdata->cd_type = S3C_SDHCI_CD_NONE;
found_cd:
if (pdata->cd_type == S3C_SDHCI_CD_GPIO) {
pdata->ext_cd_gpio = gpio;
ourhost->ext_cd_gpio = -1;
if (of_get_property(node, "cd-inverted", NULL))
pdata->ext_cd_gpio_invert = 1;
} else if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) {
ret = gpio_request(gpio, "sdhci-cd");
if (ret) {
dev_err(dev, "card detect gpio request failed\n");
return -EINVAL;
}
ourhost->ext_cd_gpio = gpio;
}
setup_bus:
/* get the gpios for command, clock and data lines */
for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) {
gpio = of_get_gpio(node, cnt);
if (!gpio_is_valid(gpio)) {
dev_err(dev, "invalid gpio[%d]\n", cnt);
goto err_free_dt_cd_gpio;
}
ourhost->gpios[cnt] = gpio;
}
for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) {
ret = gpio_request(ourhost->gpios[cnt], "sdhci-gpio");
if (ret) {
dev_err(dev, "gpio[%d] request failed\n", cnt);
goto err_free_dt_gpios;
}
}
return 0;
err_free_dt_gpios:
while (--cnt >= 0)
gpio_free(ourhost->gpios[cnt]);
err_free_dt_cd_gpio:
if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL)
gpio_free(ourhost->ext_cd_gpio);
return -EINVAL;
}
#else
static int __devinit sdhci_s3c_parse_dt(struct device *dev,
struct sdhci_host *host, struct s3c_sdhci_platdata *pdata)
{
return -EINVAL;
}
#endif
static const struct of_device_id sdhci_s3c_dt_match[];
static inline struct sdhci_s3c_drv_data *sdhci_s3c_get_driver_data(
struct platform_device *pdev)
{
#ifdef CONFIG_OF
if (pdev->dev.of_node) {
const struct of_device_id *match;
match = of_match_node(sdhci_s3c_dt_match, pdev->dev.of_node);
return (struct sdhci_s3c_drv_data *)match->data;
}
#endif
return (struct sdhci_s3c_drv_data *)
platform_get_device_id(pdev)->driver_data;
}
@ -436,7 +558,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
struct resource *res;
int ret, irq, ptr, clks;
if (!pdev->dev.platform_data) {
if (!pdev->dev.platform_data && !pdev->dev.of_node) {
dev_err(dev, "no device data specified\n");
return -ENOENT;
}
@ -452,21 +574,28 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
dev_err(dev, "sdhci_alloc_host() failed\n");
return PTR_ERR(host);
}
sc = sdhci_priv(host);
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
ret = -ENOMEM;
goto err_io_clk;
goto err_pdata;
}
if (pdev->dev.of_node) {
ret = sdhci_s3c_parse_dt(&pdev->dev, host, pdata);
if (ret)
goto err_pdata;
} else {
memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata));
sc->ext_cd_gpio = -1; /* invalid gpio number */
}
memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata));
drv_data = sdhci_s3c_get_driver_data(pdev);
sc = sdhci_priv(host);
sc->host = host;
sc->pdev = pdev;
sc->pdata = pdata;
sc->ext_cd_gpio = -1; /* invalid gpio number */
platform_set_drvdata(pdev, host);
@ -486,9 +615,8 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
snprintf(name, 14, "mmc_busclk.%d", ptr);
clk = clk_get(dev, name);
if (IS_ERR(clk)) {
if (IS_ERR(clk))
continue;
}
clks++;
sc->clk_bus[ptr] = clk;
@ -499,8 +627,6 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
*/
sc->cur_clk = ptr;
clk_enable(clk);
dev_info(dev, "clock source %d: %s (%ld Hz)\n",
ptr, name, clk_get_rate(clk));
}
@ -511,6 +637,10 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
goto err_no_busclks;
}
#ifndef CONFIG_PM_RUNTIME
clk_enable(sc->clk_bus[sc->cur_clk]);
#endif
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
host->ioaddr = devm_request_and_ioremap(&pdev->dev, res);
if (!host->ioaddr) {
@ -616,12 +746,17 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
gpio_is_valid(pdata->ext_cd_gpio))
sdhci_s3c_setup_card_detect_gpio(sc);
#ifdef CONFIG_PM_RUNTIME
clk_disable(sc->clk_io);
#endif
return 0;
err_req_regs:
#ifndef CONFIG_PM_RUNTIME
clk_disable(sc->clk_bus[sc->cur_clk]);
#endif
for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
if (sc->clk_bus[ptr]) {
clk_disable(sc->clk_bus[ptr]);
clk_put(sc->clk_bus[ptr]);
}
}
@ -631,6 +766,12 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
clk_put(sc->clk_io);
err_io_clk:
for (ptr = 0; ptr < NUM_GPIOS(sc->pdata->max_width); ptr++)
gpio_free(sc->gpios[ptr]);
if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL)
gpio_free(sc->ext_cd_gpio);
err_pdata:
sdhci_free_host(host);
return ret;
@ -638,9 +779,9 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
{
struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_s3c *sc = sdhci_priv(host);
struct s3c_sdhci_platdata *pdata = sc->pdata;
int ptr;
if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup)
@ -652,19 +793,30 @@ static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
if (gpio_is_valid(sc->ext_cd_gpio))
gpio_free(sc->ext_cd_gpio);
#ifdef CONFIG_PM_RUNTIME
clk_enable(sc->clk_io);
#endif
sdhci_remove_host(host, 1);
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev);
for (ptr = 0; ptr < 3; ptr++) {
#ifndef CONFIG_PM_RUNTIME
clk_disable(sc->clk_bus[sc->cur_clk]);
#endif
for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
if (sc->clk_bus[ptr]) {
clk_disable(sc->clk_bus[ptr]);
clk_put(sc->clk_bus[ptr]);
}
}
clk_disable(sc->clk_io);
clk_put(sc->clk_io);
if (pdev->dev.of_node) {
for (ptr = 0; ptr < NUM_GPIOS(sc->pdata->max_width); ptr++)
gpio_free(sc->gpios[ptr]);
}
sdhci_free_host(host);
platform_set_drvdata(pdev, NULL);
@ -691,15 +843,28 @@ static int sdhci_s3c_resume(struct device *dev)
static int sdhci_s3c_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_s3c *ourhost = to_s3c(host);
struct clk *busclk = ourhost->clk_io;
int ret;
return sdhci_runtime_suspend_host(host);
ret = sdhci_runtime_suspend_host(host);
clk_disable(ourhost->clk_bus[ourhost->cur_clk]);
clk_disable(busclk);
return ret;
}
static int sdhci_s3c_runtime_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_s3c *ourhost = to_s3c(host);
struct clk *busclk = ourhost->clk_io;
int ret;
return sdhci_runtime_resume_host(host);
clk_enable(busclk);
clk_enable(ourhost->clk_bus[ourhost->cur_clk]);
ret = sdhci_runtime_resume_host(host);
return ret;
}
#endif
@ -737,6 +902,16 @@ static struct platform_device_id sdhci_s3c_driver_ids[] = {
};
MODULE_DEVICE_TABLE(platform, sdhci_s3c_driver_ids);
#ifdef CONFIG_OF
static const struct of_device_id sdhci_s3c_dt_match[] = {
{ .compatible = "samsung,s3c6410-sdhci", },
{ .compatible = "samsung,exynos4210-sdhci",
.data = (void *)EXYNOS4_SDHCI_DRV_DATA },
{},
};
MODULE_DEVICE_TABLE(of, sdhci_s3c_dt_match);
#endif
static struct platform_driver sdhci_s3c_driver = {
.probe = sdhci_s3c_probe,
.remove = __devexit_p(sdhci_s3c_remove),
@ -744,6 +919,7 @@ static struct platform_driver sdhci_s3c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "s3c-sdhci",
.of_match_table = of_match_ptr(sdhci_s3c_dt_match),
.pm = SDHCI_S3C_PMOPS,
},
};

View File

@ -20,6 +20,8 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/slab.h>
@ -68,8 +70,42 @@ static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id)
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)
{
struct device_node *np = pdev->dev.of_node;
struct sdhci_host *host;
struct resource *iomem;
struct spear_sdhci *sdhci;
@ -104,14 +140,22 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
goto err;
}
ret = clk_enable(sdhci->clk);
ret = clk_prepare_enable(sdhci->clk);
if (ret) {
dev_dbg(&pdev->dev, "Error enabling clock\n");
goto put_clk;
}
/* overwrite platform_data */
sdhci->data = dev_get_platdata(&pdev->dev);
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);
}
pdev->dev.platform_data = sdhci;
if (pdev->dev.parent)
@ -216,7 +260,7 @@ set_drvdata:
free_host:
sdhci_free_host(host);
disable_clk:
clk_disable(sdhci->clk);
clk_disable_unprepare(sdhci->clk);
put_clk:
clk_put(sdhci->clk);
err:
@ -238,7 +282,7 @@ static int __devexit sdhci_remove(struct platform_device *pdev)
sdhci_remove_host(host, dead);
sdhci_free_host(host);
clk_disable(sdhci->clk);
clk_disable_unprepare(sdhci->clk);
clk_put(sdhci->clk);
return 0;
@ -253,7 +297,7 @@ static int sdhci_suspend(struct device *dev)
ret = sdhci_suspend_host(host);
if (!ret)
clk_disable(sdhci->clk);
clk_disable_unprepare(sdhci->clk);
return ret;
}
@ -264,7 +308,7 @@ static int sdhci_resume(struct device *dev)
struct spear_sdhci *sdhci = dev_get_platdata(dev);
int ret;
ret = clk_enable(sdhci->clk);
ret = clk_prepare_enable(sdhci->clk);
if (ret) {
dev_dbg(dev, "Resume: Error enabling clock\n");
return ret;
@ -276,11 +320,20 @@ static int sdhci_resume(struct device *dev)
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 = {
.driver = {
.name = "sdhci",
.owner = THIS_MODULE,
.pm = &sdhci_pm_ops,
.of_match_table = of_match_ptr(sdhci_spear_id_table),
},
.probe = sdhci_probe,
.remove = __devexit_p(sdhci_remove),

View File

@ -257,10 +257,9 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
int rc;
match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
if (match)
soc_data = match->data;
else
soc_data = &soc_data_tegra20;
if (!match)
return -EINVAL;
soc_data = match->data;
host = sdhci_pltfm_init(pdev, soc_data->pdata);
if (IS_ERR(host))

View File

@ -28,6 +28,7 @@
#include <linux/mmc/mmc.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/slot-gpio.h>
#include "sdhci.h"
@ -1293,6 +1294,13 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
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) {
host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
@ -1597,57 +1605,65 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
spin_unlock_irqrestore(&host->lock, flags);
}
static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
struct mmc_ios *ios)
static int sdhci_do_3_3v_signal_voltage_switch(struct sdhci_host *host,
u16 ctrl)
{
u8 pwr;
u16 clk, ctrl;
u32 present_state;
int ret;
/*
* Signal Voltage Switching is only applicable for Host Controllers
* v3.00 and above.
*/
if (host->version < SDHCI_SPEC_300)
return 0;
/* Set 1.8V Signal Enable in the Host Control2 register to 0 */
ctrl &= ~SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
/*
* 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 */
ctrl &= ~SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
/* Wait for 5ms */
usleep_range(5000, 5500);
/* 3.3V regulator output should be stable within 5 ms */
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (!(ctrl & SDHCI_CTRL_VDD_180))
return 0;
else {
pr_info(DRIVER_NAME ": Switching to 3.3V "
"signalling voltage failed\n");
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;
}
} else if (!(ctrl & SDHCI_CTRL_VDD_180) &&
(ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)) {
/* Stop SDCLK */
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
clk &= ~SDHCI_CLOCK_CARD_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
}
/* Wait for 5ms */
usleep_range(5000, 5500);
/* Check whether DAT[3:0] is 0000 */
present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
if (!((present_state & SDHCI_DATA_LVL_MASK) >>
SDHCI_DATA_LVL_SHIFT)) {
/*
* Enable 1.8V Signal Enable in the Host Control2
* register
*/
/* 3.3V regulator output should be stable within 5 ms */
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (!(ctrl & SDHCI_CTRL_VDD_180))
return 0;
pr_warning("%s: 3.3V regulator output did not became stable\n",
mmc_hostname(host->mmc));
return -EIO;
}
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 */
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
clk &= ~SDHCI_CLOCK_CARD_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
/* Check whether DAT[3:0] is 0000 */
present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
if (!((present_state & SDHCI_DATA_LVL_MASK) >>
SDHCI_DATA_LVL_SHIFT)) {
/*
* Enable 1.8V Signal Enable in the Host Control2
* register
*/
if (host->vqmmc)
ret = regulator_set_voltage(host->vqmmc,
1800000, 1800000);
else
ret = 0;
if (!ret) {
ctrl |= SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
@ -1656,7 +1672,7 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
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_CLOCK_CARD_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
@ -1673,29 +1689,55 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
return 0;
}
}
}
/*
* If we are here, that means the switch to 1.8V signaling
* failed. We power cycle the card, and retry initialization
* sequence by setting S18R to 0.
*/
pwr = sdhci_readb(host, SDHCI_POWER_CONTROL);
pwr &= ~SDHCI_POWER_ON;
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
if (host->vmmc)
regulator_disable(host->vmmc);
/*
* If we are here, that means the switch to 1.8V signaling
* failed. We power cycle the card, and retry initialization
* sequence by setting S18R to 0.
*/
pwr = sdhci_readb(host, SDHCI_POWER_CONTROL);
pwr &= ~SDHCI_POWER_ON;
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
if (host->vmmc)
regulator_disable(host->vmmc);
/* Wait for 1ms as per the spec */
usleep_range(1000, 1500);
pwr |= SDHCI_POWER_ON;
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
if (host->vmmc)
regulator_enable(host->vmmc);
/* Wait for 1ms as per the spec */
usleep_range(1000, 1500);
pwr |= SDHCI_POWER_ON;
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
if (host->vmmc)
regulator_enable(host->vmmc);
pr_info(DRIVER_NAME ": Switching to 1.8V signalling "
"voltage failed, retrying with S18R set to 0\n");
return -EAGAIN;
} else
pr_warning("%s: Switching to 1.8V signalling voltage failed, "
"retrying with S18R set to 0\n", mmc_hostname(host->mmc));
return -EAGAIN;
}
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 */
return 0;
}
@ -2802,6 +2844,18 @@ int sdhci_add_host(struct sdhci_host *host)
!(host->mmc->caps & MMC_CAP_NONREMOVABLE))
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. */
if (caps[1] & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
SDHCI_SUPPORT_DDR50))
@ -2832,15 +2886,6 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps[1] & SDHCI_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 */
host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
SDHCI_RETUNING_TIMER_COUNT_SHIFT;
@ -2862,7 +2907,8 @@ int sdhci_add_host(struct sdhci_host *host)
if (IS_ERR(host->vmmc)) {
pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
host->vmmc = NULL;
}
} else
regulator_enable(host->vmmc);
#ifdef CONFIG_REGULATOR
if (host->vmmc) {
@ -3119,8 +3165,15 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
tasklet_kill(&host->card_tasklet);
tasklet_kill(&host->finish_tasklet);
if (host->vmmc)
if (host->vmmc) {
regulator_disable(host->vmmc);
regulator_put(host->vmmc);
}
if (host->vqmmc) {
regulator_disable(host->vqmmc);
regulator_put(host->vqmmc);
}
kfree(host->adma_desc);
kfree(host->align_buffer);

View File

@ -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_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFRE);
} 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);
} else if (state & INT_CMD12RBE) {
sh_mmcif_writel(host->addr, MMCIF_CE_INT,
@ -1229,6 +1231,10 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
host->sd_error = true;
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 (!host->dma_active)
return IRQ_WAKE_THREAD;

View File

@ -1337,21 +1337,7 @@ static struct pci_driver via_sd_driver = {
.resume = via_sd_resume,
};
static int __init via_sd_drv_init(void)
{
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_pci_driver(via_sd_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("VIA Technologies Inc.");

View File

@ -2358,9 +2358,9 @@ error5:
* which is contained at the end of struct mmc
*/
error4:
usb_free_urb(command_out_urb);
error1:
usb_free_urb(command_res_urb);
error1:
usb_free_urb(command_out_urb);
error0:
return retval;
}

View File

@ -57,6 +57,7 @@ struct mmc_ext_csd {
unsigned int sa_timeout; /* Units: 100ns */
unsigned int generic_cmd6_time; /* Units: 10ms */
unsigned int power_off_longtime; /* Units: ms */
u8 power_off_notification; /* state */
unsigned int hs_max_dtr;
#define MMC_HIGH_26_MAX_DTR 26000000
#define MMC_HIGH_52_MAX_DTR 52000000
@ -76,10 +77,13 @@ struct mmc_ext_csd {
bool hpi_en; /* HPI enablebit */
bool hpi; /* HPI support bit */
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_tag_unit_size; /* DATA TAG UNIT size */
unsigned int boot_ro_lock; /* ro lock support */
bool boot_ro_lockable;
u8 raw_exception_status; /* 53 */
u8 raw_partition_support; /* 160 */
u8 raw_erased_mem_count; /* 181 */
u8 raw_ext_csd_structure; /* 194 */
@ -93,6 +97,7 @@ struct mmc_ext_csd {
u8 raw_sec_erase_mult; /* 230 */
u8 raw_sec_feature_support;/* 231 */
u8 raw_trim_mult; /* 232 */
u8 raw_bkops_status; /* 246 */
u8 raw_sectors[4]; /* 212 - 4 bytes */
unsigned int feature_support;
@ -225,7 +230,7 @@ struct mmc_card {
#define MMC_CARD_SDXC (1<<6) /* card is SDXC */
#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_SLEEP (1<<9) /* card is in sleep state */
#define MMC_STATE_DOING_BKOPS (1<<10) /* card is doing BKOPS */
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_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
@ -241,11 +246,6 @@ struct mmc_card {
#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 */
/* 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_shift; /* if erase unit is power 2 */
@ -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_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
#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_readonly(c) ((c)->state |= MMC_STATE_READONLY)
@ -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_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_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.
*/

View File

@ -134,6 +134,8 @@ struct mmc_host;
struct mmc_card;
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 *,
struct mmc_async_req *, int *);
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);
extern int mmc_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);
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);
#define MMC_ERASE_ARG 0x00000000

View File

@ -78,6 +78,10 @@ struct mmc_data;
* @data_offset: Set the offset of DATA register according to VERID.
* @dev: Device 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.
* @fifo_depth: depth of FIFO.
* @data_shift: log2 of FIFO item size.
@ -156,8 +160,12 @@ struct dw_mci {
u32 fifoth_val;
u16 verid;
u16 data_offset;
struct device dev;
struct device *dev;
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];
/* FIFO push and pull */
@ -201,7 +209,8 @@ struct dw_mci_dma_ops {
#define DW_MCI_QUIRK_HIGHSPEED BIT(2)
/* Unreliable card detection */
#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;
@ -218,7 +227,7 @@ struct dw_mci_board {
u32 num_slots;
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 caps2; /* More capabilities */

View File

@ -259,10 +259,6 @@ struct mmc_host {
#define MMC_CAP2_RO_ACTIVE_HIGH (1 << 11) /* Write-protect signal active high */
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
int clk_requests; /* internal reference counter */
@ -300,6 +296,7 @@ struct mmc_host {
#endif
int rescan_disable; /* disable card detection */
int rescan_entered; /* used with nonremovable devices */
struct mmc_card *card; /* device attached to this host */

View File

@ -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_READY_FOR_DATA (1 << 8) /* sx, a */
#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_STATE_IDLE 0
@ -274,12 +275,15 @@ struct _mmc_csd {
#define EXT_CSD_FLUSH_CACHE 32 /* W */
#define EXT_CSD_CACHE_CTRL 33 /* 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_GP_SIZE_MULT 143 /* R/W */
#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */
#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */
#define EXT_CSD_HPI_MGMT 161 /* 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_WR_REL_PARAM 166 /* RO */
#define EXT_CSD_BOOT_WP 173 /* R/W */
@ -313,11 +317,13 @@ struct _mmc_csd {
#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_360 239 /* RO */
#define EXT_CSD_BKOPS_STATUS 246 /* RO */
#define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */
#define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */
#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */
#define EXT_CSD_TAG_UNIT_SIZE 498 /* RO */
#define EXT_CSD_DATA_TAG_SUPPORT 499 /* RO */
#define EXT_CSD_BKOPS_SUPPORT 502 /* RO */
#define EXT_CSD_HPI_FEATURES 503 /* RO */
/*
@ -377,6 +383,19 @@ struct _mmc_csd {
#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_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
*/

View File

@ -97,7 +97,8 @@ struct sdhci_host {
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 */
struct mmc_host *mmc; /* MMC structure */

View File

@ -49,6 +49,7 @@ struct sdhci_pxa_platdata {
bool ext_cd_gpio_invert;
unsigned int max_speed;
unsigned int host_caps;
unsigned int host_caps2;
unsigned int quirks;
unsigned int pm_caps;
};