mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 04:31:50 +00:00
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:
commit
943c2acea5
68
Documentation/devicetree/bindings/mmc/atmel-hsmci.txt
Normal file
68
Documentation/devicetree/bindings/mmc/atmel-hsmci.txt
Normal 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>;
|
||||
};
|
||||
};
|
87
Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
Normal file
87
Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt
Normal 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>;
|
||||
};
|
||||
};
|
@ -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:
|
||||
|
25
Documentation/devicetree/bindings/mmc/pxa-mmc.txt
Normal file
25
Documentation/devicetree/bindings/mmc/pxa-mmc.txt
Normal 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>;
|
||||
};
|
||||
|
53
Documentation/devicetree/bindings/mmc/samsung-sdhci.txt
Normal file
53
Documentation/devicetree/bindings/mmc/samsung-sdhci.txt
Normal 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.
|
14
Documentation/devicetree/bindings/mmc/sdhci-dove.txt
Normal file
14
Documentation/devicetree/bindings/mmc/sdhci-dove.txt
Normal 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>;
|
||||
};
|
18
Documentation/devicetree/bindings/mmc/sdhci-spear.txt
Normal file
18
Documentation/devicetree/bindings/mmc/sdhci-spear.txt
Normal 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>;
|
||||
};
|
79
Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt
Normal file
79
Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt
Normal 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>;
|
||||
};
|
||||
};
|
@ -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
|
||||
|
@ -80,8 +80,7 @@
|
||||
};
|
||||
|
||||
sdhci@70000000 {
|
||||
int-gpio = <&gpio1 0 0>;
|
||||
power-gpio = <&gpio1 2 1>;
|
||||
cd-gpios = <&gpio1 0 0>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
|
@ -103,8 +103,6 @@
|
||||
};
|
||||
|
||||
sdhci@70000000 {
|
||||
power-gpio = <&gpio0 2 1>;
|
||||
power_always_enb;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
253
drivers/mmc/host/dw_mmc-exynos.c
Normal file
253
drivers/mmc/host/dw_mmc-exynos.c
Normal 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");
|
@ -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>");
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
20
drivers/mmc/host/dw_mmc-pltfm.h
Normal file
20
drivers/mmc/host/dw_mmc-pltfm.h
Normal 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_ */
|
@ -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;
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
@ -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),
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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.");
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user