MMC core:
- Add support for Marvell SD8787 Wifi/BT chip - Improve UHS support for SDIO - Invent MMC_CAP_3_3V_DDR and a DT binding for eMMC DDR 3.3V mode - Detect Auto BKOPS enable bit - Export eMMC device lifetime information through sysfs - First take to slim down the public mmc headers to avoid abuse - Re-factoring of the mmc block device driver to prepare for blkmq - Cleanup code for the mmc block device driver - Clarify and cleanup code dealing with data requests - Cleanup some code by converting to ida_simple_ functions - Cleanup code dealing with card quirks - Cleanup private and public mmc header files MMC host: - Don't rely on public mmc headers to include non-mmc related headers - meson: Add support for eMMC HS400 mode - meson: Various cleanups and improvements - omap_hsmmc: Use the proper provided busy timeout from the core - sunxi: Enable new timings for the A64 MMC controllers - sunxi: Improvements for clock management - tmio: Improvements for SDIO interrupts - mxs-mmc: Add CMD23 support - sdhci-msm: Enable HS400 enhanced strobe mode support - sdhci-msm: Correct HS400 tuning sequence - sdhci-acpi: Support deferred probe - sdhci-pci: Add support for eMMC HS200 tuning mode on AMD - mediatek: Correct the implementation of card busy detection - dw_mmc: Initial support for ZX mmc controller - sh_mobile_sdhi: Enable support for eMMC HS200 mode - sh_mmcif: Various cleanups and improvements -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJYrC2eAAoJEP4mhCVzWIwpDvYP/j4qMa5uSGOuxrHPorAq1Hru VP11zZGx5iZGFJOrSulDq/El4wnaZFH8ceol3QXvw9ss6YZMFrIdwWVaZxTISFrb Vn84w0lebo7ZWSWsdaMzPowuJVzsqeEwUKH5it1jyp5WnWUAzA6h1wSkwwh8djl5 i05/iHGuOcwMCITvSryqUIGaMZnuXnc6NWIJXaYlL3BQPhaSaxWnkHupGTYzgDtU 8Xkm401iXShKZLCUzuMLZShFIJ2qvnGNmSbMpt9f1VdMvDmKmSJVfs/Tzfyn/E+R 5DEUl/BPgyTx7bbUa45V0gRqbQGqQXACbhaPBcjy8BQn0gH60MjuKxWxM9kUM0Mu 8wa5A73Qo7sFoySCLPtDtOopzozop9No3UWeTv/V1ezzXra52P0oB4gp86Ys6x5G 7GcsmqJ+Km/xMNNP8sS2WQv5l9zFM7dv6+JRxNrBsb1dk5c5pio/RKN8KQ1Wqo/N /p+iCsEi+4iKrpms5ImIpEF1hfEyJtt/wAL0rKE4NhuR8xRhO+EBGj73smrHJVgO JvDkFMlo9ZeE5aj1kmYYTdUcrIK5DRFSPdNWTs7T1B6XeZ8ePcTQxVwXcV01amWM zvx8fIGMm14M774pe85B0kmgki85XQFk0D6j3z8ElWA2QygOZlOdaSviJIZSh4jX aj85sRSJ6EVWJl17GggW =Iv7W -----END PGP SIGNATURE----- Merge tag 'mmc-v4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc Pull MMC updates from Ulf Hansson: "MMC core: - Add support for Marvell SD8787 Wifi/BT chip - Improve UHS support for SDIO - Invent MMC_CAP_3_3V_DDR and a DT binding for eMMC DDR 3.3V mode - Detect Auto BKOPS enable bit - Export eMMC device lifetime information through sysfs - First take to slim down the public mmc headers to avoid abuse - Re-factoring of the mmc block device driver to prepare for blkmq - Cleanup code for the mmc block device driver - Clarify and cleanup code dealing with data requests - Cleanup some code by converting to ida_simple_ functions - Cleanup code dealing with card quirks - Cleanup private and public mmc header files MMC host: - Don't rely on public mmc headers to include non-mmc related headers - meson: Add support for eMMC HS400 mode - meson: Various cleanups and improvements - omap_hsmmc: Use the proper provided busy timeout from the core - sunxi: Enable new timings for the A64 MMC controllers - sunxi: Improvements for clock management - tmio: Improvements for SDIO interrupts - mxs-mmc: Add CMD23 support - sdhci-msm: Enable HS400 enhanced strobe mode support - sdhci-msm: Correct HS400 tuning sequence - sdhci-acpi: Support deferred probe - sdhci-pci: Add support for eMMC HS200 tuning mode on AMD - mediatek: Correct the implementation of card busy detection - dw_mmc: Initial support for ZX mmc controller - sh_mobile_sdhi: Enable support for eMMC HS200 mode - sh_mmcif: Various cleanups and improvements" * tag 'mmc-v4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (145 commits) mmc: core: add mmc prefix for blk_fixups mmc: core: move all quirks together into quirks.h mmc: core: improve the quirks for sdio devices mmc: core: move some sdio IDs out of quirks file mmc: core: change quirks.c to be a header file mmc: sdhci-cadence: fix bit shift of read data from PHY port mmc: Adding AUTO_BKOPS_EN bit set for Auto BKOPS support mmc: MAN_BKOPS_EN inverse debug message logic mmc: meson-gx: add support for HS400 mode mmc: meson-gx: remove unneeded checks in remove mmc: meson-gx: reduce bounce buffer size mmc: meson-gx: set max block count and request size mmc: meson-gx: improve interrupt handling mmc: meson-gx: improve meson_mmc_irq_thread mmc: meson-gx: improve meson_mmc_clk_set mmc: meson-gx: minor improvements in meson_mmc_set_ios mmc: meson: Assign the minimum clk rate as close to 400KHz as possible mmc: core: start to break apart mmc_start_areq() mmc: block: respect bool returned from blk_end_request() mmc: block: return errorcode from mmc_sd_num_wr_blocks() ...
This commit is contained in:
commit
e67bd12d60
|
@ -17,7 +17,7 @@ Required properties:
|
|||
"core" - Main peripheral bus clock
|
||||
"clkin0" - Parent clock of internal mux
|
||||
"clkin1" - Other parent clock of internal mux
|
||||
The driver has an interal mux clock which switches between clkin0 and clkin1 depending on the
|
||||
The driver has an internal mux clock which switches between clkin0 and clkin1 depending on the
|
||||
clock rate requested by the MMC core.
|
||||
|
||||
Example:
|
||||
|
|
16
Documentation/devicetree/bindings/mmc/mmc-pwrseq-sd8787.txt
Normal file
16
Documentation/devicetree/bindings/mmc/mmc-pwrseq-sd8787.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
* Marvell SD8787 power sequence provider
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "mmc-pwrseq-sd8787".
|
||||
- powerdown-gpios: contains a power down GPIO specifier with the
|
||||
default active state
|
||||
- reset-gpios: contains a reset GPIO specifier with the default
|
||||
active state
|
||||
|
||||
Example:
|
||||
|
||||
wifi_pwrseq: wifi_pwrseq {
|
||||
compatible = "mmc-pwrseq-sd8787";
|
||||
powerdown-gpios = <&twl_gpio 0 GPIO_ACTIVE_LOW>;
|
||||
reset-gpios = <&twl_gpio 1 GPIO_ACTIVE_LOW>;
|
||||
}
|
|
@ -40,6 +40,7 @@ Optional properties:
|
|||
- cap-mmc-hw-reset: eMMC hardware reset is supported
|
||||
- cap-sdio-irq: enable SDIO IRQ signalling on this interface
|
||||
- full-pwr-cycle: full power cycle of the card is supported
|
||||
- mmc-ddr-3_3v: eMMC high-speed DDR mode(3.3V I/O) is supported
|
||||
- mmc-ddr-1_8v: eMMC high-speed DDR mode(1.8V I/O) is supported
|
||||
- mmc-ddr-1_2v: eMMC high-speed DDR mode(1.2V I/O) is supported
|
||||
- mmc-hs200-1_8v: eMMC HS200 mode(1.8V I/O) is supported
|
||||
|
|
|
@ -38,7 +38,7 @@ Optional properties:
|
|||
- bus-width: Number of data lines.
|
||||
See: Documentation/devicetree/bindings/mmc/mmc.txt.
|
||||
|
||||
- max-frequency: Can be 200MHz, 100Mz or 50MHz (default) and used for
|
||||
- max-frequency: Can be 200MHz, 100MHz or 50MHz (default) and used for
|
||||
configuring the CCONFIG3 in the mmcss.
|
||||
See: Documentation/devicetree/bindings/mmc/mmc.txt.
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ host controllers refer to the mmc[1] bindings.
|
|||
|
||||
Optional properties:
|
||||
- sdhci-caps-mask: The sdhci capabilities register is incorrect. This 64bit
|
||||
property corresponds to the bits in the sdhci capabilty register. If the bit
|
||||
property corresponds to the bits in the sdhci capability register. If the bit
|
||||
is on in the mask then the bit is incorrect in the register and should be
|
||||
turned off, before applying sdhci-caps.
|
||||
- sdhci-caps: The sdhci capabilities register is incorrect. This 64bit
|
||||
|
|
|
@ -13,6 +13,7 @@ Required properties:
|
|||
* "allwinner,sun5i-a13-mmc"
|
||||
* "allwinner,sun7i-a20-mmc"
|
||||
* "allwinner,sun9i-a80-mmc"
|
||||
* "allwinner,sun50i-a64-emmc"
|
||||
* "allwinner,sun50i-a64-mmc"
|
||||
- reg : mmc controller base registers
|
||||
- clocks : a list with 4 phandle + clock specifier pairs
|
||||
|
|
|
@ -16,7 +16,7 @@ Required Properties:
|
|||
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
|
||||
connected 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
|
||||
|
@ -75,6 +75,17 @@ Optional properties:
|
|||
* card-detect-delay: Delay in milli-seconds before detecting card after card
|
||||
insert event. The default value is 0.
|
||||
|
||||
* data-addr: Override fifo address with value provided by DT. The default FIFO reg
|
||||
offset is assumed as 0x100 (version < 0x240A) and 0x200(version >= 0x240A) by
|
||||
driver. If the controller does not follow this rule, please use this property
|
||||
to set fifo address in device tree.
|
||||
|
||||
* fifo-watermark-aligned: Data done irq is expected if data length is less than
|
||||
watermark in PIO mode. But fifo watermark is requested to be aligned with data
|
||||
length in some SoC so that TX/RX irq can be generated with data done irq. Add this
|
||||
watermark quirk to mark this requirement and force fifo watermark setting
|
||||
accordingly.
|
||||
|
||||
* vmmc-supply: The phandle to the regulator to use for vmmc. If this is
|
||||
specified we'll defer probe until we can find this regulator.
|
||||
|
||||
|
@ -102,6 +113,8 @@ board specific portions as listed below.
|
|||
interrupts = <0 75 0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
data-addr = <0x200>;
|
||||
fifo-watermark-aligned;
|
||||
resets = <&rst 20>;
|
||||
reset-names = "reset";
|
||||
};
|
||||
|
|
|
@ -25,6 +25,19 @@ Required properties:
|
|||
"renesas,sdhi-r8a7795" - SDHI IP on R8A7795 SoC
|
||||
"renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC
|
||||
|
||||
- clocks: Most controllers only have 1 clock source per channel. However, on
|
||||
some variations of this controller, the internal card detection
|
||||
logic that exists in this controller is sectioned off to be run by a
|
||||
separate second clock source to allow the main core clock to be turned
|
||||
off to save power.
|
||||
If 2 clocks are specified by the hardware, you must name them as
|
||||
"core" and "cd". If the controller only has 1 clock, naming is not
|
||||
required.
|
||||
Below is the number clocks for each supported SoC:
|
||||
1: SH73A0, R8A73A4, R8A7740, R8A7778, R8A7779, R8A7790
|
||||
R8A7791, R8A7792, R8A7793, R8A7794, R8A7795, R8A7796
|
||||
2: R7S72100
|
||||
|
||||
Optional properties:
|
||||
- toshiba,mmc-wrprotect-disable: write-protect detection is unavailable
|
||||
- pinctrl-names: should be "default", "state_uhs"
|
||||
|
|
33
Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt
Normal file
33
Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt
Normal file
|
@ -0,0 +1,33 @@
|
|||
* ZTE specific extensions to the Synopsys Designware Mobile Storage
|
||||
Host Controller
|
||||
|
||||
The Synopsys designware mobile storage host controller is used to interface
|
||||
a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
|
||||
differences between the core Synopsys dw mshc controller properties described
|
||||
by synopsys-dw-mshc.txt and the properties used by the ZTE specific
|
||||
extensions to the Synopsys Designware Mobile Storage Host Controller.
|
||||
|
||||
Required Properties:
|
||||
|
||||
* compatible: should be
|
||||
- "zte,zx296718-dw-mshc": for ZX SoCs
|
||||
|
||||
Example:
|
||||
|
||||
mmc1: mmc@1110000 {
|
||||
compatible = "zte,zx296718-dw-mshc";
|
||||
reg = <0x01110000 0x1000>;
|
||||
interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
|
||||
fifo-depth = <32>;
|
||||
data-addr = <0x200>;
|
||||
fifo-watermark-aligned;
|
||||
bus-width = <4>;
|
||||
clock-frequency = <50000000>;
|
||||
clocks = <&topcrm SD0_AHB>, <&topcrm SD0_WCLK>;
|
||||
clock-names = "biu", "ciu";
|
||||
num-slots = <1>;
|
||||
max-frequency = <50000000>;
|
||||
cap-sdio-irq;
|
||||
cap-sd-highspeed;
|
||||
status = "disabled";
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
Marvell 8897/8997 (sd8897/sd8997/pcie8997) SDIO/PCIE devices
|
||||
Marvell 8787/8897/8997 (sd8787/sd8897/sd8997/pcie8997) SDIO/PCIE devices
|
||||
------
|
||||
|
||||
This node provides properties for controlling the Marvell SDIO/PCIE wireless device.
|
||||
|
@ -8,6 +8,7 @@ connects the device to the system.
|
|||
Required properties:
|
||||
|
||||
- compatible : should be one of the following:
|
||||
* "marvell,sd8787"
|
||||
* "marvell,sd8897"
|
||||
* "marvell,sd8997"
|
||||
* "pci11ab,2b42"
|
||||
|
@ -34,6 +35,9 @@ Optional properties:
|
|||
so that the wifi chip can wakeup host platform under certain condition.
|
||||
during system resume, the irq will be disabled to make sure
|
||||
unnecessary interrupt is not received.
|
||||
- vmmc-supply: a phandle of a regulator, supplying VCC to the card
|
||||
- mmc-pwrseq: phandle to the MMC power sequence node. See "mmc-pwrseq-*"
|
||||
for documentation of MMC power sequence bindings.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -46,6 +50,7 @@ so that firmware can wakeup host using this device side pin.
|
|||
&mmc3 {
|
||||
status = "okay";
|
||||
vmmc-supply = <&wlan_en_reg>;
|
||||
mmc-pwrseq = <&wifi_pwrseq>;
|
||||
bus-width = <4>;
|
||||
cap-power-off-card;
|
||||
keep-power-in-suspend;
|
||||
|
|
|
@ -10894,7 +10894,6 @@ SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER
|
|||
M: Jaehoon Chung <jh80.chung@samsung.com>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: include/linux/mmc/dw_mmc.h
|
||||
F: drivers/mmc/host/dw_mmc*
|
||||
|
||||
SYSTEM TRACE MODULE CLASS
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/gpio/machine.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_data/at24.h>
|
||||
#include <linux/platform_data/pca953x.h>
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/videodev2.h>
|
||||
#include <linux/v4l2-dv-timings.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/leds.h>
|
||||
|
||||
#include <media/i2c/tvp514x.h>
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/platform_data/gpio-davinci.h>
|
||||
#include <linux/platform_data/i2c-davinci.h>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/platform_data/gpio-davinci.h>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/fb.h>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/gpio.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/major.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/pm.h>
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/bitops.h>
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/irq.h>
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/usb/gpio_vbus.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/gpio.h>
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ata_platform.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/module.h>
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/mmc/sh_mmcif.h>
|
||||
#include <linux/mmc/boot.h>
|
||||
#include <mach/romimage.h>
|
||||
|
||||
#define MMCIF_BASE (void __iomem *)0xa4ca0000
|
||||
|
@ -22,6 +21,13 @@
|
|||
#define HIZCRC 0xa405015c
|
||||
#define DRVCRA 0xa405018a
|
||||
|
||||
enum {
|
||||
MMCIF_PROGRESS_ENTER,
|
||||
MMCIF_PROGRESS_INIT,
|
||||
MMCIF_PROGRESS_LOAD,
|
||||
MMCIF_PROGRESS_DONE
|
||||
};
|
||||
|
||||
/* SH7724 specific MMCIF loader
|
||||
*
|
||||
* loads the romImage from an MMC card starting from block 512
|
||||
|
@ -30,7 +36,7 @@
|
|||
*/
|
||||
asmlinkage void mmcif_loader(unsigned char *buf, unsigned long no_bytes)
|
||||
{
|
||||
mmcif_update_progress(MMC_PROGRESS_ENTER);
|
||||
mmcif_update_progress(MMCIF_PROGRESS_ENTER);
|
||||
|
||||
/* enable clock to the MMCIF hardware block */
|
||||
__raw_writel(__raw_readl(MSTPCR2) & ~0x20000000, MSTPCR2);
|
||||
|
@ -53,12 +59,12 @@ asmlinkage void mmcif_loader(unsigned char *buf, unsigned long no_bytes)
|
|||
/* high drive capability for MMC pins */
|
||||
__raw_writew(__raw_readw(DRVCRA) | 0x3000, DRVCRA);
|
||||
|
||||
mmcif_update_progress(MMC_PROGRESS_INIT);
|
||||
mmcif_update_progress(MMCIF_PROGRESS_INIT);
|
||||
|
||||
/* setup MMCIF hardware */
|
||||
sh_mmcif_boot_init(MMCIF_BASE);
|
||||
|
||||
mmcif_update_progress(MMC_PROGRESS_LOAD);
|
||||
mmcif_update_progress(MMCIF_PROGRESS_LOAD);
|
||||
|
||||
/* load kernel via MMCIF interface */
|
||||
sh_mmcif_boot_do_read(MMCIF_BASE, 512,
|
||||
|
@ -68,5 +74,5 @@ asmlinkage void mmcif_loader(unsigned char *buf, unsigned long no_bytes)
|
|||
/* disable clock to the MMCIF hardware block */
|
||||
__raw_writel(__raw_readl(MSTPCR2) | 0x20000000, MSTPCR2);
|
||||
|
||||
mmcif_update_progress(MMC_PROGRESS_DONE);
|
||||
mmcif_update_progress(MMCIF_PROGRESS_DONE);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,16 @@ config PWRSEQ_EMMC
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called pwrseq_emmc.
|
||||
|
||||
config PWRSEQ_SD8787
|
||||
tristate "HW reset support for SD8787 BT + Wifi module"
|
||||
depends on OF && (MWIFIEX || BT_MRVL_SDIO)
|
||||
help
|
||||
This selects hardware reset support for the SD8787 BT + Wifi
|
||||
module. By default this option is set to n.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called pwrseq_sd8787.
|
||||
|
||||
config PWRSEQ_SIMPLE
|
||||
tristate "Simple HW reset support for MMC"
|
||||
default y
|
||||
|
|
|
@ -7,9 +7,10 @@ mmc_core-y := core.o bus.o host.o \
|
|||
mmc.o mmc_ops.o sd.o sd_ops.o \
|
||||
sdio.o sdio_ops.o sdio_bus.o \
|
||||
sdio_cis.o sdio_io.o sdio_irq.o \
|
||||
quirks.o slot-gpio.o
|
||||
slot-gpio.o
|
||||
mmc_core-$(CONFIG_OF) += pwrseq.o
|
||||
obj-$(CONFIG_PWRSEQ_SIMPLE) += pwrseq_simple.o
|
||||
obj-$(CONFIG_PWRSEQ_SD8787) += pwrseq_sd8787.o
|
||||
obj-$(CONFIG_PWRSEQ_EMMC) += pwrseq_emmc.o
|
||||
mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o
|
||||
obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
|
||||
|
|
|
@ -47,6 +47,13 @@
|
|||
|
||||
#include "queue.h"
|
||||
#include "block.h"
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
#include "host.h"
|
||||
#include "bus.h"
|
||||
#include "mmc_ops.h"
|
||||
#include "quirks.h"
|
||||
#include "sd_ops.h"
|
||||
|
||||
MODULE_ALIAS("mmc:block");
|
||||
#ifdef MODULE_PARAM_PREFIX
|
||||
|
@ -54,12 +61,6 @@ MODULE_ALIAS("mmc:block");
|
|||
#endif
|
||||
#define MODULE_PARAM_PREFIX "mmcblk."
|
||||
|
||||
#define INAND_CMD38_ARG_EXT_CSD 113
|
||||
#define INAND_CMD38_ARG_ERASE 0x00
|
||||
#define INAND_CMD38_ARG_TRIM 0x01
|
||||
#define INAND_CMD38_ARG_SECERASE 0x80
|
||||
#define INAND_CMD38_ARG_SECTRIM1 0x81
|
||||
#define INAND_CMD38_ARG_SECTRIM2 0x88
|
||||
#define MMC_BLK_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
|
||||
#define MMC_SANITIZE_REQ_TIMEOUT 240000
|
||||
#define MMC_EXTRACT_INDEX_FROM_ARG(x) ((x & 0x00FF0000) >> 16)
|
||||
|
@ -84,7 +85,6 @@ static int max_devices;
|
|||
#define MAX_DEVICES 256
|
||||
|
||||
static DEFINE_IDA(mmc_blk_ida);
|
||||
static DEFINE_SPINLOCK(mmc_blk_lock);
|
||||
|
||||
/*
|
||||
* There is one mmc_blk_data per slot.
|
||||
|
@ -157,11 +157,7 @@ static void mmc_blk_put(struct mmc_blk_data *md)
|
|||
if (md->usage == 0) {
|
||||
int devidx = mmc_get_devidx(md->disk);
|
||||
blk_cleanup_queue(md->queue.queue);
|
||||
|
||||
spin_lock(&mmc_blk_lock);
|
||||
ida_remove(&mmc_blk_ida, devidx);
|
||||
spin_unlock(&mmc_blk_lock);
|
||||
|
||||
ida_simple_remove(&mmc_blk_ida, devidx);
|
||||
put_disk(md->disk);
|
||||
kfree(md);
|
||||
}
|
||||
|
@ -442,9 +438,9 @@ out:
|
|||
static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
||||
struct mmc_blk_ioc_data *idata)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_command cmd = {};
|
||||
struct mmc_data data = {};
|
||||
struct mmc_request mrq = {};
|
||||
struct scatterlist sg;
|
||||
int err;
|
||||
int is_rpmb = false;
|
||||
|
@ -762,15 +758,15 @@ static inline int mmc_blk_part_switch(struct mmc_card *card,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
|
||||
static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks)
|
||||
{
|
||||
int err;
|
||||
u32 result;
|
||||
__be32 *blocks;
|
||||
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct mmc_request mrq = {};
|
||||
struct mmc_command cmd = {};
|
||||
struct mmc_data data = {};
|
||||
|
||||
struct scatterlist sg;
|
||||
|
||||
|
@ -780,9 +776,9 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
|
|||
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
||||
if (err)
|
||||
return (u32)-1;
|
||||
return err;
|
||||
if (!mmc_host_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD))
|
||||
return (u32)-1;
|
||||
return -EIO;
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
|
@ -802,7 +798,7 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
|
|||
|
||||
blocks = kmalloc(4, GFP_KERNEL);
|
||||
if (!blocks)
|
||||
return (u32)-1;
|
||||
return -ENOMEM;
|
||||
|
||||
sg_init_one(&sg, blocks, 4);
|
||||
|
||||
|
@ -812,14 +808,16 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
|
|||
kfree(blocks);
|
||||
|
||||
if (cmd.error || data.error)
|
||||
result = (u32)-1;
|
||||
return -EIO;
|
||||
|
||||
return result;
|
||||
*written_blocks = result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_card_status(struct mmc_card *card, u32 *status, int retries)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
int err;
|
||||
|
||||
cmd.opcode = MMC_SEND_STATUS;
|
||||
|
@ -884,7 +882,7 @@ static int send_stop(struct mmc_card *card, unsigned int timeout_ms,
|
|||
struct request *req, bool *gen_err, u32 *stop_status)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
int err;
|
||||
bool use_r1b_resp = rq_data_dir(req) == WRITE;
|
||||
|
||||
|
@ -1143,7 +1141,7 @@ int mmc_access_rpmb(struct mmc_queue *mq)
|
|||
return false;
|
||||
}
|
||||
|
||||
static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
|
||||
static void mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
|
||||
{
|
||||
struct mmc_blk_data *md = mq->blkdata;
|
||||
struct mmc_card *card = md->queue.card;
|
||||
|
@ -1152,7 +1150,7 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
|
|||
|
||||
if (!mmc_can_erase(card)) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
from = blk_rq_pos(req);
|
||||
|
@ -1164,29 +1162,26 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
|
|||
arg = MMC_TRIM_ARG;
|
||||
else
|
||||
arg = MMC_ERASE_ARG;
|
||||
retry:
|
||||
if (card->quirks & MMC_QUIRK_INAND_CMD38) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
INAND_CMD38_ARG_EXT_CSD,
|
||||
arg == MMC_TRIM_ARG ?
|
||||
INAND_CMD38_ARG_TRIM :
|
||||
INAND_CMD38_ARG_ERASE,
|
||||
0);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
err = mmc_erase(card, from, nr, arg);
|
||||
out:
|
||||
if (err == -EIO && !mmc_blk_reset(md, card->host, type))
|
||||
goto retry;
|
||||
do {
|
||||
err = 0;
|
||||
if (card->quirks & MMC_QUIRK_INAND_CMD38) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
INAND_CMD38_ARG_EXT_CSD,
|
||||
arg == MMC_TRIM_ARG ?
|
||||
INAND_CMD38_ARG_TRIM :
|
||||
INAND_CMD38_ARG_ERASE,
|
||||
0);
|
||||
}
|
||||
if (!err)
|
||||
err = mmc_erase(card, from, nr, arg);
|
||||
} while (err == -EIO && !mmc_blk_reset(md, card->host, type));
|
||||
if (!err)
|
||||
mmc_blk_reset_success(md, type);
|
||||
fail:
|
||||
blk_end_request(req, err, blk_rq_bytes(req));
|
||||
|
||||
return err ? 0 : 1;
|
||||
}
|
||||
|
||||
static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
|
||||
static void mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
|
||||
struct request *req)
|
||||
{
|
||||
struct mmc_blk_data *md = mq->blkdata;
|
||||
|
@ -1249,11 +1244,9 @@ out_retry:
|
|||
mmc_blk_reset_success(md, type);
|
||||
out:
|
||||
blk_end_request(req, err, blk_rq_bytes(req));
|
||||
|
||||
return err ? 0 : 1;
|
||||
}
|
||||
|
||||
static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
|
||||
static void mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
|
||||
{
|
||||
struct mmc_blk_data *md = mq->blkdata;
|
||||
struct mmc_card *card = md->queue.card;
|
||||
|
@ -1264,8 +1257,6 @@ static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
|
|||
ret = -EIO;
|
||||
|
||||
blk_end_request_all(req, ret);
|
||||
|
||||
return ret ? 0 : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1303,7 +1294,7 @@ static enum mmc_blk_status mmc_blk_err_check(struct mmc_card *card,
|
|||
struct mmc_async_req *areq)
|
||||
{
|
||||
struct mmc_queue_req *mq_mrq = container_of(areq, struct mmc_queue_req,
|
||||
mmc_active);
|
||||
areq);
|
||||
struct mmc_blk_request *brq = &mq_mrq->brq;
|
||||
struct request *req = mq_mrq->req;
|
||||
int need_retune = card->host->need_retune;
|
||||
|
@ -1559,17 +1550,19 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
|||
brq->data.sg_len = i;
|
||||
}
|
||||
|
||||
mqrq->mmc_active.mrq = &brq->mrq;
|
||||
mqrq->mmc_active.err_check = mmc_blk_err_check;
|
||||
mqrq->areq.mrq = &brq->mrq;
|
||||
mqrq->areq.err_check = mmc_blk_err_check;
|
||||
|
||||
mmc_queue_bounce_pre(mqrq);
|
||||
}
|
||||
|
||||
static int mmc_blk_cmd_err(struct mmc_blk_data *md, struct mmc_card *card,
|
||||
struct mmc_blk_request *brq, struct request *req,
|
||||
int ret)
|
||||
static bool mmc_blk_rw_cmd_err(struct mmc_blk_data *md, struct mmc_card *card,
|
||||
struct mmc_blk_request *brq, struct request *req,
|
||||
bool old_req_pending)
|
||||
{
|
||||
struct mmc_queue_req *mq_rq;
|
||||
bool req_pending;
|
||||
|
||||
mq_rq = container_of(brq, struct mmc_queue_req, brq);
|
||||
|
||||
/*
|
||||
|
@ -1582,62 +1575,104 @@ static int mmc_blk_cmd_err(struct mmc_blk_data *md, struct mmc_card *card,
|
|||
*/
|
||||
if (mmc_card_sd(card)) {
|
||||
u32 blocks;
|
||||
int err;
|
||||
|
||||
blocks = mmc_sd_num_wr_blocks(card);
|
||||
if (blocks != (u32)-1) {
|
||||
ret = blk_end_request(req, 0, blocks << 9);
|
||||
}
|
||||
err = mmc_sd_num_wr_blocks(card, &blocks);
|
||||
if (err)
|
||||
req_pending = old_req_pending;
|
||||
else
|
||||
req_pending = blk_end_request(req, 0, blocks << 9);
|
||||
} else {
|
||||
ret = blk_end_request(req, 0, brq->data.bytes_xfered);
|
||||
req_pending = blk_end_request(req, 0, brq->data.bytes_xfered);
|
||||
}
|
||||
return ret;
|
||||
return req_pending;
|
||||
}
|
||||
|
||||
static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
||||
static void mmc_blk_rw_cmd_abort(struct mmc_card *card, struct request *req)
|
||||
{
|
||||
if (mmc_card_removed(card))
|
||||
req->rq_flags |= RQF_QUIET;
|
||||
while (blk_end_request(req, -EIO, blk_rq_cur_bytes(req)));
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc_blk_rw_try_restart() - tries to restart the current async request
|
||||
* @mq: the queue with the card and host to restart
|
||||
* @req: a new request that want to be started after the current one
|
||||
*/
|
||||
static void mmc_blk_rw_try_restart(struct mmc_queue *mq, struct request *req)
|
||||
{
|
||||
if (!req)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If the card was removed, just cancel everything and return.
|
||||
*/
|
||||
if (mmc_card_removed(mq->card)) {
|
||||
req->rq_flags |= RQF_QUIET;
|
||||
blk_end_request_all(req, -EIO);
|
||||
return;
|
||||
}
|
||||
/* Else proceed and try to restart the current async request */
|
||||
mmc_blk_rw_rq_prep(mq->mqrq_cur, mq->card, 0, mq);
|
||||
mmc_start_areq(mq->card->host, &mq->mqrq_cur->areq, NULL);
|
||||
}
|
||||
|
||||
static void mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *new_req)
|
||||
{
|
||||
struct mmc_blk_data *md = mq->blkdata;
|
||||
struct mmc_card *card = md->queue.card;
|
||||
struct mmc_blk_request *brq;
|
||||
int ret = 1, disable_multi = 0, retry = 0, type, retune_retry_done = 0;
|
||||
int disable_multi = 0, retry = 0, type, retune_retry_done = 0;
|
||||
enum mmc_blk_status status;
|
||||
struct mmc_queue_req *mq_rq;
|
||||
struct request *req;
|
||||
struct mmc_async_req *areq;
|
||||
struct request *old_req;
|
||||
struct mmc_async_req *new_areq;
|
||||
struct mmc_async_req *old_areq;
|
||||
bool req_pending = true;
|
||||
|
||||
if (!rqc && !mq->mqrq_prev->req)
|
||||
return 0;
|
||||
if (!new_req && !mq->mqrq_prev->req)
|
||||
return;
|
||||
|
||||
do {
|
||||
if (rqc) {
|
||||
if (new_req) {
|
||||
/*
|
||||
* When 4KB native sector is enabled, only 8 blocks
|
||||
* multiple read or write is allowed
|
||||
*/
|
||||
if (mmc_large_sector(card) &&
|
||||
!IS_ALIGNED(blk_rq_sectors(rqc), 8)) {
|
||||
!IS_ALIGNED(blk_rq_sectors(new_req), 8)) {
|
||||
pr_err("%s: Transfer size is not 4KB sector size aligned\n",
|
||||
rqc->rq_disk->disk_name);
|
||||
mq_rq = mq->mqrq_cur;
|
||||
req = rqc;
|
||||
rqc = NULL;
|
||||
goto cmd_abort;
|
||||
new_req->rq_disk->disk_name);
|
||||
mmc_blk_rw_cmd_abort(card, new_req);
|
||||
return;
|
||||
}
|
||||
|
||||
mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
|
||||
areq = &mq->mqrq_cur->mmc_active;
|
||||
new_areq = &mq->mqrq_cur->areq;
|
||||
} else
|
||||
areq = NULL;
|
||||
areq = mmc_start_req(card->host, areq, &status);
|
||||
if (!areq) {
|
||||
new_areq = NULL;
|
||||
|
||||
old_areq = mmc_start_areq(card->host, new_areq, &status);
|
||||
if (!old_areq) {
|
||||
/*
|
||||
* We have just put the first request into the pipeline
|
||||
* and there is nothing more to do until it is
|
||||
* complete.
|
||||
*/
|
||||
if (status == MMC_BLK_NEW_REQUEST)
|
||||
mq->flags |= MMC_QUEUE_NEW_REQUEST;
|
||||
return 0;
|
||||
mq->new_request = true;
|
||||
return;
|
||||
}
|
||||
|
||||
mq_rq = container_of(areq, struct mmc_queue_req, mmc_active);
|
||||
/*
|
||||
* An asynchronous request has been completed and we proceed
|
||||
* to handle the result of it.
|
||||
*/
|
||||
mq_rq = container_of(old_areq, struct mmc_queue_req, areq);
|
||||
brq = &mq_rq->brq;
|
||||
req = mq_rq->req;
|
||||
type = rq_data_dir(req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE;
|
||||
old_req = mq_rq->req;
|
||||
type = rq_data_dir(old_req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE;
|
||||
mmc_queue_bounce_post(mq_rq);
|
||||
|
||||
switch (status) {
|
||||
|
@ -1648,28 +1683,32 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
|||
*/
|
||||
mmc_blk_reset_success(md, type);
|
||||
|
||||
ret = blk_end_request(req, 0,
|
||||
brq->data.bytes_xfered);
|
||||
|
||||
req_pending = blk_end_request(old_req, 0,
|
||||
brq->data.bytes_xfered);
|
||||
/*
|
||||
* If the blk_end_request function returns non-zero even
|
||||
* though all data has been transferred and no errors
|
||||
* were returned by the host controller, it's a bug.
|
||||
*/
|
||||
if (status == MMC_BLK_SUCCESS && ret) {
|
||||
if (status == MMC_BLK_SUCCESS && req_pending) {
|
||||
pr_err("%s BUG rq_tot %d d_xfer %d\n",
|
||||
__func__, blk_rq_bytes(req),
|
||||
__func__, blk_rq_bytes(old_req),
|
||||
brq->data.bytes_xfered);
|
||||
rqc = NULL;
|
||||
goto cmd_abort;
|
||||
mmc_blk_rw_cmd_abort(card, old_req);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case MMC_BLK_CMD_ERR:
|
||||
ret = mmc_blk_cmd_err(md, card, brq, req, ret);
|
||||
if (mmc_blk_reset(md, card->host, type))
|
||||
goto cmd_abort;
|
||||
if (!ret)
|
||||
goto start_new_req;
|
||||
req_pending = mmc_blk_rw_cmd_err(md, card, brq, old_req, req_pending);
|
||||
if (mmc_blk_reset(md, card->host, type)) {
|
||||
mmc_blk_rw_cmd_abort(card, old_req);
|
||||
mmc_blk_rw_try_restart(mq, new_req);
|
||||
return;
|
||||
}
|
||||
if (!req_pending) {
|
||||
mmc_blk_rw_try_restart(mq, new_req);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case MMC_BLK_RETRY:
|
||||
retune_retry_done = brq->retune_retry_done;
|
||||
|
@ -1679,22 +1718,27 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
|||
case MMC_BLK_ABORT:
|
||||
if (!mmc_blk_reset(md, card->host, type))
|
||||
break;
|
||||
goto cmd_abort;
|
||||
mmc_blk_rw_cmd_abort(card, old_req);
|
||||
mmc_blk_rw_try_restart(mq, new_req);
|
||||
return;
|
||||
case MMC_BLK_DATA_ERR: {
|
||||
int err;
|
||||
|
||||
err = mmc_blk_reset(md, card->host, type);
|
||||
if (!err)
|
||||
break;
|
||||
if (err == -ENODEV)
|
||||
goto cmd_abort;
|
||||
if (err == -ENODEV) {
|
||||
mmc_blk_rw_cmd_abort(card, old_req);
|
||||
mmc_blk_rw_try_restart(mq, new_req);
|
||||
return;
|
||||
}
|
||||
/* Fall through */
|
||||
}
|
||||
case MMC_BLK_ECC_ERR:
|
||||
if (brq->data.blocks > 1) {
|
||||
/* Redo read one sector at a time */
|
||||
pr_warn("%s: retrying using single block read\n",
|
||||
req->rq_disk->disk_name);
|
||||
old_req->rq_disk->disk_name);
|
||||
disable_multi = 1;
|
||||
break;
|
||||
}
|
||||
|
@ -1703,57 +1747,40 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
|||
* time, so we only reach here after trying to
|
||||
* read a single sector.
|
||||
*/
|
||||
ret = blk_end_request(req, -EIO,
|
||||
brq->data.blksz);
|
||||
if (!ret)
|
||||
goto start_new_req;
|
||||
req_pending = blk_end_request(old_req, -EIO,
|
||||
brq->data.blksz);
|
||||
if (!req_pending) {
|
||||
mmc_blk_rw_try_restart(mq, new_req);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case MMC_BLK_NOMEDIUM:
|
||||
goto cmd_abort;
|
||||
mmc_blk_rw_cmd_abort(card, old_req);
|
||||
mmc_blk_rw_try_restart(mq, new_req);
|
||||
return;
|
||||
default:
|
||||
pr_err("%s: Unhandled return value (%d)",
|
||||
req->rq_disk->disk_name, status);
|
||||
goto cmd_abort;
|
||||
old_req->rq_disk->disk_name, status);
|
||||
mmc_blk_rw_cmd_abort(card, old_req);
|
||||
mmc_blk_rw_try_restart(mq, new_req);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
if (req_pending) {
|
||||
/*
|
||||
* In case of a incomplete request
|
||||
* prepare it again and resend.
|
||||
*/
|
||||
mmc_blk_rw_rq_prep(mq_rq, card,
|
||||
disable_multi, mq);
|
||||
mmc_start_req(card->host,
|
||||
&mq_rq->mmc_active, NULL);
|
||||
mmc_start_areq(card->host,
|
||||
&mq_rq->areq, NULL);
|
||||
mq_rq->brq.retune_retry_done = retune_retry_done;
|
||||
}
|
||||
} while (ret);
|
||||
|
||||
return 1;
|
||||
|
||||
cmd_abort:
|
||||
if (mmc_card_removed(card))
|
||||
req->rq_flags |= RQF_QUIET;
|
||||
while (ret)
|
||||
ret = blk_end_request(req, -EIO,
|
||||
blk_rq_cur_bytes(req));
|
||||
|
||||
start_new_req:
|
||||
if (rqc) {
|
||||
if (mmc_card_removed(card)) {
|
||||
rqc->rq_flags |= RQF_QUIET;
|
||||
blk_end_request_all(rqc, -EIO);
|
||||
} else {
|
||||
mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
|
||||
mmc_start_req(card->host,
|
||||
&mq->mqrq_cur->mmc_active, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
} while (req_pending);
|
||||
}
|
||||
|
||||
int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
||||
void mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
||||
{
|
||||
int ret;
|
||||
struct mmc_blk_data *md = mq->blkdata;
|
||||
|
@ -1769,32 +1796,31 @@ int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
|||
if (req) {
|
||||
blk_end_request_all(req, -EIO);
|
||||
}
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
|
||||
mq->new_request = false;
|
||||
if (req && req_op(req) == REQ_OP_DISCARD) {
|
||||
/* complete ongoing async transfer before issuing discard */
|
||||
if (card->host->areq)
|
||||
mmc_blk_issue_rw_rq(mq, NULL);
|
||||
ret = mmc_blk_issue_discard_rq(mq, req);
|
||||
mmc_blk_issue_discard_rq(mq, req);
|
||||
} else if (req && req_op(req) == REQ_OP_SECURE_ERASE) {
|
||||
/* complete ongoing async transfer before issuing secure erase*/
|
||||
if (card->host->areq)
|
||||
mmc_blk_issue_rw_rq(mq, NULL);
|
||||
ret = mmc_blk_issue_secdiscard_rq(mq, req);
|
||||
mmc_blk_issue_secdiscard_rq(mq, req);
|
||||
} else if (req && req_op(req) == REQ_OP_FLUSH) {
|
||||
/* complete ongoing async transfer before issuing flush */
|
||||
if (card->host->areq)
|
||||
mmc_blk_issue_rw_rq(mq, NULL);
|
||||
ret = mmc_blk_issue_flush(mq, req);
|
||||
mmc_blk_issue_flush(mq, req);
|
||||
} else {
|
||||
ret = mmc_blk_issue_rw_rq(mq, req);
|
||||
mmc_blk_issue_rw_rq(mq, req);
|
||||
}
|
||||
|
||||
out:
|
||||
if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) || req_is_special)
|
||||
if ((!req && !mq->new_request) || req_is_special)
|
||||
/*
|
||||
* Release host when there are no more requests
|
||||
* and after special request(discard, flush) is done.
|
||||
|
@ -1802,7 +1828,6 @@ out:
|
|||
* the 'mmc_blk_issue_rq' with 'mqrq_prev->req'.
|
||||
*/
|
||||
mmc_put_card(card);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int mmc_blk_readonly(struct mmc_card *card)
|
||||
|
@ -1821,23 +1846,9 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
|||
struct mmc_blk_data *md;
|
||||
int devidx, ret;
|
||||
|
||||
again:
|
||||
if (!ida_pre_get(&mmc_blk_ida, GFP_KERNEL))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
spin_lock(&mmc_blk_lock);
|
||||
ret = ida_get_new(&mmc_blk_ida, &devidx);
|
||||
spin_unlock(&mmc_blk_lock);
|
||||
|
||||
if (ret == -EAGAIN)
|
||||
goto again;
|
||||
else if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (devidx >= max_devices) {
|
||||
ret = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
devidx = ida_simple_get(&mmc_blk_ida, 0, max_devices, GFP_KERNEL);
|
||||
if (devidx < 0)
|
||||
return ERR_PTR(devidx);
|
||||
|
||||
md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
|
||||
if (!md) {
|
||||
|
@ -1926,9 +1937,7 @@ again:
|
|||
err_kfree:
|
||||
kfree(md);
|
||||
out:
|
||||
spin_lock(&mmc_blk_lock);
|
||||
ida_remove(&mmc_blk_ida, devidx);
|
||||
spin_unlock(&mmc_blk_lock);
|
||||
ida_simple_remove(&mmc_blk_ida, devidx);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
|
@ -2093,80 +2102,6 @@ force_ro_fail:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const struct mmc_fixup blk_fixups[] =
|
||||
{
|
||||
MMC_FIXUP("SEM02G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM04G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM08G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM16G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM32G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
|
||||
/*
|
||||
* Some MMC cards experience performance degradation with CMD23
|
||||
* instead of CMD12-bounded multiblock transfers. For now we'll
|
||||
* black list what's bad...
|
||||
* - Certain Toshiba cards.
|
||||
*
|
||||
* N.B. This doesn't affect SD cards.
|
||||
*/
|
||||
MMC_FIXUP("SDMB-32", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
MMC_FIXUP("SDM032", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
MMC_FIXUP("MMC08G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
MMC_FIXUP("MMC16G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
MMC_FIXUP("MMC32G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
|
||||
/*
|
||||
* Some MMC cards need longer data read timeout than indicated in CSD.
|
||||
*/
|
||||
MMC_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc,
|
||||
MMC_QUIRK_LONG_READ_TIME),
|
||||
MMC_FIXUP("008GE0", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_LONG_READ_TIME),
|
||||
|
||||
/*
|
||||
* On these Samsung MoviNAND parts, performing secure erase or
|
||||
* secure trim can result in unrecoverable corruption due to a
|
||||
* firmware bug.
|
||||
*/
|
||||
MMC_FIXUP("M8G2FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
MMC_FIXUP("MAG4FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
MMC_FIXUP("MBG8FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
MMC_FIXUP("MCGAFA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
MMC_FIXUP("VAL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
MMC_FIXUP("VYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
MMC_FIXUP("KYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
|
||||
/*
|
||||
* On Some Kingston eMMCs, performing trim can result in
|
||||
* unrecoverable data conrruption occasionally due to a firmware bug.
|
||||
*/
|
||||
MMC_FIXUP("V10008", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_TRIM_BROKEN),
|
||||
MMC_FIXUP("V10016", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_TRIM_BROKEN),
|
||||
|
||||
END_FIXUP
|
||||
};
|
||||
|
||||
static int mmc_blk_probe(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_blk_data *md, *part_md;
|
||||
|
@ -2178,7 +2113,7 @@ static int mmc_blk_probe(struct mmc_card *card)
|
|||
if (!(card->csd.cmdclass & CCC_BLOCK_READ))
|
||||
return -ENODEV;
|
||||
|
||||
mmc_fixup_device(card, blk_fixups);
|
||||
mmc_fixup_device(card, mmc_blk_fixups);
|
||||
|
||||
md = mmc_blk_alloc(card);
|
||||
if (IS_ERR(md))
|
||||
|
|
|
@ -1 +1,9 @@
|
|||
int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req);
|
||||
#ifndef _MMC_CORE_BLOCK_H
|
||||
#define _MMC_CORE_BLOCK_H
|
||||
|
||||
struct mmc_queue;
|
||||
struct request;
|
||||
|
||||
void mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include <linux/mmc/host.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
#include "host.h"
|
||||
#include "sdio_cis.h"
|
||||
#include "bus.h"
|
||||
|
||||
|
|
|
@ -11,6 +11,11 @@
|
|||
#ifndef _MMC_CORE_BUS_H
|
||||
#define _MMC_CORE_BUS_H
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
struct mmc_host;
|
||||
struct mmc_card;
|
||||
|
||||
#define MMC_DEV_ATTR(name, fmt, args...) \
|
||||
static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
|
@ -27,5 +32,14 @@ void mmc_remove_card(struct mmc_card *card);
|
|||
int mmc_register_bus(void);
|
||||
void mmc_unregister_bus(void);
|
||||
|
||||
#endif
|
||||
struct mmc_driver {
|
||||
struct device_driver drv;
|
||||
int (*probe)(struct mmc_card *card);
|
||||
void (*remove)(struct mmc_card *card);
|
||||
void (*shutdown)(struct mmc_card *card);
|
||||
};
|
||||
|
||||
int mmc_register_driver(struct mmc_driver *drv);
|
||||
void mmc_unregister_driver(struct mmc_driver *drv);
|
||||
|
||||
#endif
|
||||
|
|
221
drivers/mmc/core/card.h
Normal file
221
drivers/mmc/core/card.h
Normal file
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* Private header for the mmc subsystem
|
||||
*
|
||||
* Copyright (C) 2016 Linaro Ltd
|
||||
*
|
||||
* Author: Ulf Hansson <ulf.hansson@linaro.org>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*/
|
||||
|
||||
#ifndef _MMC_CORE_CARD_H
|
||||
#define _MMC_CORE_CARD_H
|
||||
|
||||
#include <linux/mmc/card.h>
|
||||
|
||||
#define mmc_card_name(c) ((c)->cid.prod_name)
|
||||
#define mmc_card_id(c) (dev_name(&(c)->dev))
|
||||
#define mmc_dev_to_card(d) container_of(d, struct mmc_card, dev)
|
||||
|
||||
/* Card states */
|
||||
#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
|
||||
#define MMC_STATE_READONLY (1<<1) /* card is read-only */
|
||||
#define MMC_STATE_BLOCKADDR (1<<2) /* card uses block-addressing */
|
||||
#define MMC_CARD_SDXC (1<<3) /* card is SDXC */
|
||||
#define MMC_CARD_REMOVED (1<<4) /* card has been removed */
|
||||
#define MMC_STATE_DOING_BKOPS (1<<5) /* card is doing BKOPS */
|
||||
#define MMC_STATE_SUSPENDED (1<<6) /* card is suspended */
|
||||
|
||||
#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
|
||||
#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
|
||||
#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR)
|
||||
#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_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS)
|
||||
#define mmc_card_suspended(c) ((c)->state & MMC_STATE_SUSPENDED)
|
||||
|
||||
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
|
||||
#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
|
||||
#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
|
||||
#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_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_set_suspended(c) ((c)->state |= MMC_STATE_SUSPENDED)
|
||||
#define mmc_card_clr_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED)
|
||||
|
||||
/*
|
||||
* The world is not perfect and supplies us with broken mmc/sdio devices.
|
||||
* For at least some of these bugs we need a work-around.
|
||||
*/
|
||||
struct mmc_fixup {
|
||||
/* CID-specific fields. */
|
||||
const char *name;
|
||||
|
||||
/* Valid revision range */
|
||||
u64 rev_start, rev_end;
|
||||
|
||||
unsigned int manfid;
|
||||
unsigned short oemid;
|
||||
|
||||
/* SDIO-specific fields. You can use SDIO_ANY_ID here of course */
|
||||
u16 cis_vendor, cis_device;
|
||||
|
||||
/* for MMC cards */
|
||||
unsigned int ext_csd_rev;
|
||||
|
||||
void (*vendor_fixup)(struct mmc_card *card, int data);
|
||||
int data;
|
||||
};
|
||||
|
||||
#define CID_MANFID_ANY (-1u)
|
||||
#define CID_OEMID_ANY ((unsigned short) -1)
|
||||
#define CID_NAME_ANY (NULL)
|
||||
|
||||
#define EXT_CSD_REV_ANY (-1u)
|
||||
|
||||
#define CID_MANFID_SANDISK 0x2
|
||||
#define CID_MANFID_TOSHIBA 0x11
|
||||
#define CID_MANFID_MICRON 0x13
|
||||
#define CID_MANFID_SAMSUNG 0x15
|
||||
#define CID_MANFID_KINGSTON 0x70
|
||||
#define CID_MANFID_HYNIX 0x90
|
||||
|
||||
#define END_FIXUP { NULL }
|
||||
|
||||
#define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \
|
||||
_cis_vendor, _cis_device, \
|
||||
_fixup, _data, _ext_csd_rev) \
|
||||
{ \
|
||||
.name = (_name), \
|
||||
.manfid = (_manfid), \
|
||||
.oemid = (_oemid), \
|
||||
.rev_start = (_rev_start), \
|
||||
.rev_end = (_rev_end), \
|
||||
.cis_vendor = (_cis_vendor), \
|
||||
.cis_device = (_cis_device), \
|
||||
.vendor_fixup = (_fixup), \
|
||||
.data = (_data), \
|
||||
.ext_csd_rev = (_ext_csd_rev), \
|
||||
}
|
||||
|
||||
#define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end, \
|
||||
_fixup, _data, _ext_csd_rev) \
|
||||
_FIXUP_EXT(_name, _manfid, \
|
||||
_oemid, _rev_start, _rev_end, \
|
||||
SDIO_ANY_ID, SDIO_ANY_ID, \
|
||||
_fixup, _data, _ext_csd_rev) \
|
||||
|
||||
#define MMC_FIXUP(_name, _manfid, _oemid, _fixup, _data) \
|
||||
MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data, \
|
||||
EXT_CSD_REV_ANY)
|
||||
|
||||
#define MMC_FIXUP_EXT_CSD_REV(_name, _manfid, _oemid, _fixup, _data, \
|
||||
_ext_csd_rev) \
|
||||
MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data, \
|
||||
_ext_csd_rev)
|
||||
|
||||
#define SDIO_FIXUP(_vendor, _device, _fixup, _data) \
|
||||
_FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY, \
|
||||
CID_OEMID_ANY, 0, -1ull, \
|
||||
_vendor, _device, \
|
||||
_fixup, _data, EXT_CSD_REV_ANY) \
|
||||
|
||||
#define cid_rev(hwrev, fwrev, year, month) \
|
||||
(((u64) hwrev) << 40 | \
|
||||
((u64) fwrev) << 32 | \
|
||||
((u64) year) << 16 | \
|
||||
((u64) month))
|
||||
|
||||
#define cid_rev_card(card) \
|
||||
cid_rev(card->cid.hwrev, \
|
||||
card->cid.fwrev, \
|
||||
card->cid.year, \
|
||||
card->cid.month)
|
||||
|
||||
/*
|
||||
* Unconditionally quirk add/remove.
|
||||
*/
|
||||
static inline void __maybe_unused add_quirk(struct mmc_card *card, int data)
|
||||
{
|
||||
card->quirks |= data;
|
||||
}
|
||||
|
||||
static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
|
||||
{
|
||||
card->quirks &= ~data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Quirk add/remove for MMC products.
|
||||
*/
|
||||
static inline void __maybe_unused add_quirk_mmc(struct mmc_card *card, int data)
|
||||
{
|
||||
if (mmc_card_mmc(card))
|
||||
card->quirks |= data;
|
||||
}
|
||||
|
||||
static inline void __maybe_unused remove_quirk_mmc(struct mmc_card *card,
|
||||
int data)
|
||||
{
|
||||
if (mmc_card_mmc(card))
|
||||
card->quirks &= ~data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Quirk add/remove for SD products.
|
||||
*/
|
||||
static inline void __maybe_unused add_quirk_sd(struct mmc_card *card, int data)
|
||||
{
|
||||
if (mmc_card_sd(card))
|
||||
card->quirks |= data;
|
||||
}
|
||||
|
||||
static inline void __maybe_unused remove_quirk_sd(struct mmc_card *card,
|
||||
int data)
|
||||
{
|
||||
if (mmc_card_sd(card))
|
||||
card->quirks &= ~data;
|
||||
}
|
||||
|
||||
static inline int mmc_card_lenient_fn0(const struct mmc_card *c)
|
||||
{
|
||||
return c->quirks & MMC_QUIRK_LENIENT_FN0;
|
||||
}
|
||||
|
||||
static inline int mmc_blksz_for_byte_mode(const struct mmc_card *c)
|
||||
{
|
||||
return c->quirks & MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
|
||||
}
|
||||
|
||||
static inline int mmc_card_disable_cd(const struct mmc_card *c)
|
||||
{
|
||||
return c->quirks & MMC_QUIRK_DISABLE_CD;
|
||||
}
|
||||
|
||||
static inline int mmc_card_nonstd_func_interface(const struct mmc_card *c)
|
||||
{
|
||||
return c->quirks & MMC_QUIRK_NONSTD_FUNC_IF;
|
||||
}
|
||||
|
||||
static inline int mmc_card_broken_byte_mode_512(const struct mmc_card *c)
|
||||
{
|
||||
return c->quirks & MMC_QUIRK_BROKEN_BYTE_MODE_512;
|
||||
}
|
||||
|
||||
static inline int mmc_card_long_read_time(const struct mmc_card *c)
|
||||
{
|
||||
return c->quirks & MMC_QUIRK_LONG_READ_TIME;
|
||||
}
|
||||
|
||||
static inline int mmc_card_broken_irq_polling(const struct mmc_card *c)
|
||||
{
|
||||
return c->quirks & MMC_QUIRK_BROKEN_IRQ_POLLING;
|
||||
}
|
||||
|
||||
static inline int mmc_card_broken_hpi(const struct mmc_card *c)
|
||||
{
|
||||
return c->quirks & MMC_QUIRK_BROKEN_HPI;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -40,6 +40,7 @@
|
|||
#include <trace/events/mmc.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
#include "bus.h"
|
||||
#include "host.h"
|
||||
#include "sdio_bus.h"
|
||||
|
@ -630,10 +631,41 @@ static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq,
|
|||
}
|
||||
|
||||
/**
|
||||
* mmc_start_req - start a non-blocking request
|
||||
* mmc_finalize_areq() - finalize an asynchronous request
|
||||
* @host: MMC host to finalize any ongoing request on
|
||||
*
|
||||
* Returns the status of the ongoing asynchronous request, but
|
||||
* MMC_BLK_SUCCESS if no request was going on.
|
||||
*/
|
||||
static enum mmc_blk_status mmc_finalize_areq(struct mmc_host *host)
|
||||
{
|
||||
enum mmc_blk_status status;
|
||||
|
||||
if (!host->areq)
|
||||
return MMC_BLK_SUCCESS;
|
||||
|
||||
status = mmc_wait_for_data_req_done(host, host->areq->mrq);
|
||||
if (status == MMC_BLK_NEW_REQUEST)
|
||||
return status;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc_start_areq - start an asynchronous request
|
||||
* @host: MMC host to start command
|
||||
* @areq: async request to start
|
||||
* @error: out parameter returns 0 for success, otherwise non zero
|
||||
* @areq: asynchronous request to start
|
||||
* @ret_stat: out parameter for status
|
||||
*
|
||||
* Start a new MMC custom command request for a host.
|
||||
* If there is on ongoing async request wait for completion
|
||||
|
@ -645,11 +677,11 @@ static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq,
|
|||
* return the completed request. If there is no ongoing request, NULL
|
||||
* is returned without waiting. NULL is not an error condition.
|
||||
*/
|
||||
struct mmc_async_req *mmc_start_req(struct mmc_host *host,
|
||||
struct mmc_async_req *areq,
|
||||
enum mmc_blk_status *ret_stat)
|
||||
struct mmc_async_req *mmc_start_areq(struct mmc_host *host,
|
||||
struct mmc_async_req *areq,
|
||||
enum mmc_blk_status *ret_stat)
|
||||
{
|
||||
enum mmc_blk_status status = MMC_BLK_SUCCESS;
|
||||
enum mmc_blk_status status;
|
||||
int start_err = 0;
|
||||
struct mmc_async_req *data = host->areq;
|
||||
|
||||
|
@ -657,44 +689,25 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
|
|||
if (areq)
|
||||
mmc_pre_req(host, areq->mrq);
|
||||
|
||||
if (host->areq) {
|
||||
status = mmc_wait_for_data_req_done(host, host->areq->mrq);
|
||||
if (status == MMC_BLK_NEW_REQUEST) {
|
||||
if (ret_stat)
|
||||
*ret_stat = status;
|
||||
/*
|
||||
* The previous request was not completed,
|
||||
* nothing to return
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* 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)) {
|
||||
/* Finalize previous request */
|
||||
status = mmc_finalize_areq(host);
|
||||
|
||||
/* Cancel the prepared request */
|
||||
if (areq)
|
||||
mmc_post_req(host, areq->mrq, -EINVAL);
|
||||
|
||||
mmc_start_bkops(host->card, true);
|
||||
|
||||
/* prepare the request again */
|
||||
if (areq)
|
||||
mmc_pre_req(host, areq->mrq);
|
||||
}
|
||||
/* The previous request is still going on... */
|
||||
if (status == MMC_BLK_NEW_REQUEST) {
|
||||
if (ret_stat)
|
||||
*ret_stat = status;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Fine so far, start the new request! */
|
||||
if (status == MMC_BLK_SUCCESS && areq)
|
||||
start_err = __mmc_start_data_req(host, areq->mrq);
|
||||
|
||||
/* Postprocess the old request at this point */
|
||||
if (host->areq)
|
||||
mmc_post_req(host, host->areq->mrq, 0);
|
||||
|
||||
/* Cancel a prepared request if it was not started. */
|
||||
/* Cancel a prepared request if it was not started. */
|
||||
if ((status != MMC_BLK_SUCCESS || start_err) && areq)
|
||||
mmc_post_req(host, areq->mrq, -EINVAL);
|
||||
|
||||
|
@ -707,7 +720,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
|
|||
*ret_stat = status;
|
||||
return data;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_start_req);
|
||||
EXPORT_SYMBOL(mmc_start_areq);
|
||||
|
||||
/**
|
||||
* mmc_wait_for_req - start a request and wait for completion
|
||||
|
@ -807,7 +820,7 @@ EXPORT_SYMBOL(mmc_interrupt_hpi);
|
|||
*/
|
||||
int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
|
||||
{
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_request mrq = {};
|
||||
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
|
@ -1630,7 +1643,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
|
|||
return ocr;
|
||||
}
|
||||
|
||||
int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
|
||||
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
|
||||
{
|
||||
int err = 0;
|
||||
int old_signal_voltage = host->ios.signal_voltage;
|
||||
|
@ -1646,19 +1659,12 @@ int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
|
|||
|
||||
}
|
||||
|
||||
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
|
||||
int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
int err = 0;
|
||||
u32 clock;
|
||||
|
||||
/*
|
||||
* Send CMD11 only if the request is to switch the card to
|
||||
* 1.8V signalling.
|
||||
*/
|
||||
if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
|
||||
return __mmc_set_signal_voltage(host, signal_voltage);
|
||||
|
||||
/*
|
||||
* If we cannot switch voltages, return failure so the caller
|
||||
* can continue without UHS mode
|
||||
|
@ -1697,7 +1703,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
|
|||
host->ios.clock = 0;
|
||||
mmc_set_ios(host);
|
||||
|
||||
if (__mmc_set_signal_voltage(host, signal_voltage)) {
|
||||
if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)) {
|
||||
/*
|
||||
* Voltages may not have been switched, but we've already
|
||||
* sent CMD11, so a power cycle is required anyway
|
||||
|
@ -1806,11 +1812,11 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
|
|||
mmc_set_initial_state(host);
|
||||
|
||||
/* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */
|
||||
if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330) == 0)
|
||||
if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330))
|
||||
dev_dbg(mmc_dev(host), "Initial signal voltage of 3.3v\n");
|
||||
else if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180) == 0)
|
||||
else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180))
|
||||
dev_dbg(mmc_dev(host), "Initial signal voltage of 1.8v\n");
|
||||
else if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120) == 0)
|
||||
else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120))
|
||||
dev_dbg(mmc_dev(host), "Initial signal voltage of 1.2v\n");
|
||||
|
||||
/*
|
||||
|
@ -2129,7 +2135,7 @@ static unsigned int mmc_erase_timeout(struct mmc_card *card,
|
|||
static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
||||
unsigned int to, unsigned int arg)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
unsigned int qty = 0, busy_timeout = 0;
|
||||
bool use_r1b_resp = false;
|
||||
unsigned long timeout;
|
||||
|
@ -2551,7 +2557,7 @@ EXPORT_SYMBOL(mmc_calc_max_discard);
|
|||
|
||||
int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
if (mmc_card_blockaddr(card) || mmc_card_ddr52(card) ||
|
||||
mmc_card_hs400(card) || mmc_card_hs400es(card))
|
||||
|
@ -2567,7 +2573,7 @@ EXPORT_SYMBOL(mmc_set_blocklen);
|
|||
int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount,
|
||||
bool is_rel_write)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
cmd.opcode = MMC_SET_BLOCK_COUNT;
|
||||
cmd.arg = blockcount & 0x0000FFFF;
|
||||
|
|
|
@ -12,6 +12,11 @@
|
|||
#define _MMC_CORE_CORE_H
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
struct mmc_host;
|
||||
struct mmc_card;
|
||||
struct mmc_request;
|
||||
|
||||
#define MMC_CMD_RETRIES 3
|
||||
|
||||
|
@ -43,8 +48,8 @@ void mmc_set_clock(struct mmc_host *host, unsigned int hz);
|
|||
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
|
||||
void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
|
||||
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
|
||||
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr);
|
||||
int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
|
||||
int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr);
|
||||
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
|
||||
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
|
||||
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
|
||||
int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
|
||||
|
@ -69,6 +74,7 @@ void mmc_start_host(struct mmc_host *host);
|
|||
void mmc_stop_host(struct mmc_host *host);
|
||||
|
||||
int _mmc_detect_card_removed(struct mmc_host *host);
|
||||
int mmc_detect_card_removed(struct mmc_host *host);
|
||||
|
||||
int mmc_attach_mmc(struct mmc_host *host);
|
||||
int mmc_attach_sd(struct mmc_host *host);
|
||||
|
@ -98,5 +104,38 @@ static inline void mmc_register_pm_notifier(struct mmc_host *host) { }
|
|||
static inline void mmc_unregister_pm_notifier(struct mmc_host *host) { }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq);
|
||||
bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq);
|
||||
|
||||
int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
|
||||
unsigned int arg);
|
||||
int mmc_can_erase(struct mmc_card *card);
|
||||
int mmc_can_trim(struct mmc_card *card);
|
||||
int mmc_can_discard(struct mmc_card *card);
|
||||
int mmc_can_sanitize(struct mmc_card *card);
|
||||
int mmc_can_secure_erase_trim(struct mmc_card *card);
|
||||
int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
|
||||
unsigned int nr);
|
||||
unsigned int mmc_calc_max_discard(struct mmc_card *card);
|
||||
|
||||
int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen);
|
||||
int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount,
|
||||
bool is_rel_write);
|
||||
|
||||
int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
|
||||
void mmc_release_host(struct mmc_host *host);
|
||||
void mmc_get_card(struct mmc_card *card);
|
||||
void mmc_put_card(struct mmc_card *card);
|
||||
|
||||
/**
|
||||
* mmc_claim_host - exclusively claim a host
|
||||
* @host: mmc host to claim
|
||||
*
|
||||
* Claim a host for a set of operations.
|
||||
*/
|
||||
static inline void mmc_claim_host(struct mmc_host *host)
|
||||
{
|
||||
__mmc_claim_host(host, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <linux/mmc/host.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
#include "host.h"
|
||||
#include "mmc_ops.h"
|
||||
|
||||
#ifdef CONFIG_FAIL_MMC_REQUEST
|
||||
|
|
|
@ -34,14 +34,11 @@
|
|||
#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
|
||||
|
||||
static DEFINE_IDA(mmc_host_ida);
|
||||
static DEFINE_SPINLOCK(mmc_host_lock);
|
||||
|
||||
static void mmc_host_classdev_release(struct device *dev)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
spin_lock(&mmc_host_lock);
|
||||
ida_remove(&mmc_host_ida, host->index);
|
||||
spin_unlock(&mmc_host_lock);
|
||||
ida_simple_remove(&mmc_host_ida, host->index);
|
||||
kfree(host);
|
||||
}
|
||||
|
||||
|
@ -301,6 +298,8 @@ int mmc_of_parse(struct mmc_host *host)
|
|||
if (of_property_read_bool(np, "wakeup-source") ||
|
||||
of_property_read_bool(np, "enable-sdio-wakeup")) /* legacy */
|
||||
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
||||
if (of_property_read_bool(np, "mmc-ddr-3_3v"))
|
||||
host->caps |= MMC_CAP_3_3V_DDR;
|
||||
if (of_property_read_bool(np, "mmc-ddr-1_8v"))
|
||||
host->caps |= MMC_CAP_1_8V_DDR;
|
||||
if (of_property_read_bool(np, "mmc-ddr-1_2v"))
|
||||
|
@ -354,22 +353,13 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
|||
/* scanning will be enabled when we're ready */
|
||||
host->rescan_disable = 1;
|
||||
|
||||
again:
|
||||
if (!ida_pre_get(&mmc_host_ida, GFP_KERNEL)) {
|
||||
err = ida_simple_get(&mmc_host_ida, 0, 0, GFP_KERNEL);
|
||||
if (err < 0) {
|
||||
kfree(host);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spin_lock(&mmc_host_lock);
|
||||
err = ida_get_new(&mmc_host_ida, &host->index);
|
||||
spin_unlock(&mmc_host_lock);
|
||||
|
||||
if (err == -EAGAIN) {
|
||||
goto again;
|
||||
} else if (err) {
|
||||
kfree(host);
|
||||
return NULL;
|
||||
}
|
||||
host->index = err;
|
||||
|
||||
dev_set_name(&host->class_dev, "mmc%d", host->index);
|
||||
|
||||
|
@ -381,6 +371,8 @@ again:
|
|||
|
||||
if (mmc_gpio_alloc(host)) {
|
||||
put_device(&host->class_dev);
|
||||
ida_simple_remove(&mmc_host_ida, host->index);
|
||||
kfree(host);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
#ifndef _MMC_CORE_HOST_H
|
||||
#define _MMC_CORE_HOST_H
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
int mmc_register_host_class(void);
|
||||
|
@ -20,6 +21,53 @@ void mmc_retune_disable(struct mmc_host *host);
|
|||
void mmc_retune_hold(struct mmc_host *host);
|
||||
void mmc_retune_release(struct mmc_host *host);
|
||||
int mmc_retune(struct mmc_host *host);
|
||||
void mmc_retune_pause(struct mmc_host *host);
|
||||
void mmc_retune_unpause(struct mmc_host *host);
|
||||
|
||||
static inline void mmc_retune_recheck(struct mmc_host *host)
|
||||
{
|
||||
if (host->hold_retune <= 1)
|
||||
host->retune_now = 1;
|
||||
}
|
||||
|
||||
static inline int mmc_host_cmd23(struct mmc_host *host)
|
||||
{
|
||||
return host->caps & MMC_CAP_CMD23;
|
||||
}
|
||||
|
||||
static inline int mmc_boot_partition_access(struct mmc_host *host)
|
||||
{
|
||||
return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC);
|
||||
}
|
||||
|
||||
static inline int mmc_host_uhs(struct mmc_host *host)
|
||||
{
|
||||
return host->caps &
|
||||
(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
|
||||
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
|
||||
MMC_CAP_UHS_DDR50);
|
||||
}
|
||||
|
||||
static inline bool mmc_card_hs200(struct mmc_card *card)
|
||||
{
|
||||
return card->host->ios.timing == MMC_TIMING_MMC_HS200;
|
||||
}
|
||||
|
||||
static inline bool mmc_card_ddr52(struct mmc_card *card)
|
||||
{
|
||||
return card->host->ios.timing == MMC_TIMING_MMC_DDR52;
|
||||
}
|
||||
|
||||
static inline bool mmc_card_hs400(struct mmc_card *card)
|
||||
{
|
||||
return card->host->ios.timing == MMC_TIMING_MMC_HS400;
|
||||
}
|
||||
|
||||
static inline bool mmc_card_hs400es(struct mmc_card *card)
|
||||
{
|
||||
return card->host->ios.enhanced_strobe;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -21,9 +21,11 @@
|
|||
#include <linux/mmc/mmc.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
#include "host.h"
|
||||
#include "bus.h"
|
||||
#include "mmc_ops.h"
|
||||
#include "quirks.h"
|
||||
#include "sd_ops.h"
|
||||
|
||||
#define DEFAULT_CMD6_TIMEOUT_MS 500
|
||||
|
@ -47,17 +49,6 @@ static const unsigned int tacc_mant[] = {
|
|||
35, 40, 45, 50, 55, 60, 70, 80,
|
||||
};
|
||||
|
||||
static const struct mmc_fixup mmc_ext_csd_fixups[] = {
|
||||
/*
|
||||
* Certain Hynix eMMC 4.41 cards might get broken when HPI feature
|
||||
* is used so disable the HPI feature for such buggy cards.
|
||||
*/
|
||||
MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_HYNIX,
|
||||
0x014a, add_quirk, MMC_QUIRK_BROKEN_HPI, 5),
|
||||
|
||||
END_FIXUP
|
||||
};
|
||||
|
||||
#define UNSTUFF_BITS(resp,start,size) \
|
||||
({ \
|
||||
const int __size = size; \
|
||||
|
@ -212,7 +203,7 @@ static void mmc_select_card_type(struct mmc_card *card)
|
|||
avail_type |= EXT_CSD_CARD_TYPE_HS_52;
|
||||
}
|
||||
|
||||
if (caps & MMC_CAP_1_8V_DDR &&
|
||||
if (caps & (MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR) &&
|
||||
card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) {
|
||||
hs_max_dtr = MMC_HIGH_DDR_MAX_DTR;
|
||||
avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V;
|
||||
|
@ -307,6 +298,18 @@ static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd)
|
|||
}
|
||||
}
|
||||
|
||||
static void mmc_part_add(struct mmc_card *card, unsigned int size,
|
||||
unsigned int part_cfg, char *name, int idx, bool ro,
|
||||
int area_type)
|
||||
{
|
||||
card->part[card->nr_parts].size = size;
|
||||
card->part[card->nr_parts].part_cfg = part_cfg;
|
||||
sprintf(card->part[card->nr_parts].name, name, idx);
|
||||
card->part[card->nr_parts].force_ro = ro;
|
||||
card->part[card->nr_parts].area_type = area_type;
|
||||
card->nr_parts++;
|
||||
}
|
||||
|
||||
static void mmc_manage_gp_partitions(struct mmc_card *card, u8 *ext_csd)
|
||||
{
|
||||
int idx;
|
||||
|
@ -530,8 +533,14 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|||
EXT_CSD_MANUAL_BKOPS_MASK);
|
||||
card->ext_csd.raw_bkops_status =
|
||||
ext_csd[EXT_CSD_BKOPS_STATUS];
|
||||
if (!card->ext_csd.man_bkops_en)
|
||||
pr_debug("%s: MAN_BKOPS_EN bit is not set\n",
|
||||
if (card->ext_csd.man_bkops_en)
|
||||
pr_debug("%s: MAN_BKOPS_EN bit is set\n",
|
||||
mmc_hostname(card->host));
|
||||
card->ext_csd.auto_bkops_en =
|
||||
(ext_csd[EXT_CSD_BKOPS_EN] &
|
||||
EXT_CSD_AUTO_BKOPS_MASK);
|
||||
if (card->ext_csd.auto_bkops_en)
|
||||
pr_debug("%s: AUTO_BKOPS_EN bit is set\n",
|
||||
mmc_hostname(card->host));
|
||||
}
|
||||
|
||||
|
@ -617,6 +626,12 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|||
card->ext_csd.ffu_capable =
|
||||
(ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1) &&
|
||||
!(ext_csd[EXT_CSD_FW_CONFIG] & 0x1);
|
||||
|
||||
card->ext_csd.pre_eol_info = ext_csd[EXT_CSD_PRE_EOL_INFO];
|
||||
card->ext_csd.device_life_time_est_typ_a =
|
||||
ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A];
|
||||
card->ext_csd.device_life_time_est_typ_b =
|
||||
ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B];
|
||||
}
|
||||
|
||||
/* eMMC v5.1 or later */
|
||||
|
@ -764,6 +779,10 @@ MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
|
|||
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
|
||||
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
|
||||
MMC_DEV_ATTR(prv, "0x%x\n", card->cid.prv);
|
||||
MMC_DEV_ATTR(pre_eol_info, "%02x\n", card->ext_csd.pre_eol_info);
|
||||
MMC_DEV_ATTR(life_time, "0x%02x 0x%02x\n",
|
||||
card->ext_csd.device_life_time_est_typ_a,
|
||||
card->ext_csd.device_life_time_est_typ_b);
|
||||
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
|
||||
MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
|
||||
card->ext_csd.enhanced_area_offset);
|
||||
|
@ -817,6 +836,8 @@ static struct attribute *mmc_std_attrs[] = {
|
|||
&dev_attr_name.attr,
|
||||
&dev_attr_oemid.attr,
|
||||
&dev_attr_prv.attr,
|
||||
&dev_attr_pre_eol_info.attr,
|
||||
&dev_attr_life_time.attr,
|
||||
&dev_attr_serial.attr,
|
||||
&dev_attr_enhanced_area_offset.attr,
|
||||
&dev_attr_enhanced_area_size.attr,
|
||||
|
@ -1095,16 +1116,19 @@ static int mmc_select_hs_ddr(struct mmc_card *card)
|
|||
*
|
||||
* WARNING: eMMC rules are NOT the same as SD DDR
|
||||
*/
|
||||
err = -EINVAL;
|
||||
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
|
||||
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
|
||||
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V) {
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
|
||||
if (!err)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (err && (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_8V))
|
||||
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
|
||||
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_8V &&
|
||||
host->caps & MMC_CAP_1_8V_DDR)
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
|
||||
|
||||
/* make sure vccq is 3.3v after switching disaster */
|
||||
if (err)
|
||||
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -1271,10 +1295,10 @@ static int mmc_select_hs400es(struct mmc_card *card)
|
|||
}
|
||||
|
||||
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400_1_2V)
|
||||
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
|
||||
|
||||
if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400_1_8V)
|
||||
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
|
||||
|
||||
/* If fails try again during next card power cycle */
|
||||
if (err)
|
||||
|
@ -1380,10 +1404,10 @@ static int mmc_select_hs200(struct mmc_card *card)
|
|||
|
||||
old_signal_voltage = host->ios.signal_voltage;
|
||||
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V)
|
||||
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
|
||||
|
||||
if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_8V)
|
||||
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
|
||||
|
||||
/* If fails try again during next card power cycle */
|
||||
if (err)
|
||||
|
@ -1425,7 +1449,7 @@ static int mmc_select_hs200(struct mmc_card *card)
|
|||
err:
|
||||
if (err) {
|
||||
/* fall back to the old signal voltage, if fails report error */
|
||||
if (__mmc_set_signal_voltage(host, old_signal_voltage))
|
||||
if (mmc_set_signal_voltage(host, old_signal_voltage))
|
||||
err = -EIO;
|
||||
|
||||
pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host),
|
||||
|
@ -1805,7 +1829,7 @@ static int mmc_can_sleep(struct mmc_card *card)
|
|||
|
||||
static int mmc_sleep(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
struct mmc_card *card = host->card;
|
||||
unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
|
||||
int err;
|
||||
|
|
|
@ -57,7 +57,7 @@ static const u8 tuning_blk_pattern_8bit[] = {
|
|||
int mmc_send_status(struct mmc_card *card, u32 *status)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
cmd.opcode = MMC_SEND_STATUS;
|
||||
if (!mmc_host_is_spi(card->host))
|
||||
|
@ -79,7 +79,7 @@ int mmc_send_status(struct mmc_card *card, u32 *status)
|
|||
|
||||
static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
cmd.opcode = MMC_SELECT_CARD;
|
||||
|
||||
|
@ -115,7 +115,7 @@ int mmc_deselect_cards(struct mmc_host *host)
|
|||
*/
|
||||
int mmc_set_dsr(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
cmd.opcode = MMC_SET_DSR;
|
||||
|
||||
|
@ -128,7 +128,7 @@ int mmc_set_dsr(struct mmc_host *host)
|
|||
int mmc_go_idle(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
/*
|
||||
* Non-SPI hosts need to prevent chipselect going active during
|
||||
|
@ -164,7 +164,7 @@ int mmc_go_idle(struct mmc_host *host)
|
|||
|
||||
int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
int i, err = 0;
|
||||
|
||||
cmd.opcode = MMC_SEND_OP_COND;
|
||||
|
@ -203,7 +203,7 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
|||
int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
cmd.opcode = MMC_ALL_SEND_CID;
|
||||
cmd.arg = 0;
|
||||
|
@ -220,7 +220,7 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
|
|||
|
||||
int mmc_set_relative_addr(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
cmd.opcode = MMC_SET_RELATIVE_ADDR;
|
||||
cmd.arg = card->rca << 16;
|
||||
|
@ -233,7 +233,7 @@ static int
|
|||
mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
cmd.opcode = opcode;
|
||||
cmd.arg = arg;
|
||||
|
@ -256,9 +256,9 @@ static int
|
|||
mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
|
||||
u32 opcode, void *buf, unsigned len)
|
||||
{
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct mmc_request mrq = {};
|
||||
struct mmc_command cmd = {};
|
||||
struct mmc_data data = {};
|
||||
struct scatterlist sg;
|
||||
|
||||
mrq.cmd = &cmd;
|
||||
|
@ -387,7 +387,7 @@ EXPORT_SYMBOL_GPL(mmc_get_ext_csd);
|
|||
|
||||
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
int err;
|
||||
|
||||
cmd.opcode = MMC_SPI_READ_OCR;
|
||||
|
@ -402,7 +402,7 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
|
|||
|
||||
int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
int err;
|
||||
|
||||
cmd.opcode = MMC_SPI_CRC_ON_OFF;
|
||||
|
@ -530,7 +530,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
|||
{
|
||||
struct mmc_host *host = card->host;
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
bool use_r1b_resp = use_busy_signal;
|
||||
unsigned char old_timing = host->ios.timing;
|
||||
|
||||
|
@ -610,9 +610,9 @@ EXPORT_SYMBOL_GPL(mmc_switch);
|
|||
|
||||
int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error)
|
||||
{
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct mmc_request mrq = {};
|
||||
struct mmc_command cmd = {};
|
||||
struct mmc_data data = {};
|
||||
struct scatterlist sg;
|
||||
struct mmc_ios *ios = &host->ios;
|
||||
const u8 *tuning_block_pattern;
|
||||
|
@ -679,7 +679,7 @@ EXPORT_SYMBOL_GPL(mmc_send_tuning);
|
|||
|
||||
int mmc_abort_tuning(struct mmc_host *host, u32 opcode)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
/*
|
||||
* eMMC specification specifies that CMD12 can be used to stop a tuning
|
||||
|
@ -706,9 +706,9 @@ static int
|
|||
mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
|
||||
u8 len)
|
||||
{
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct mmc_request mrq = {};
|
||||
struct mmc_command cmd = {};
|
||||
struct mmc_data data = {};
|
||||
struct scatterlist sg;
|
||||
u8 *data_buf;
|
||||
u8 *test_buf;
|
||||
|
@ -802,7 +802,7 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width)
|
|||
|
||||
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
unsigned int opcode;
|
||||
int err;
|
||||
|
||||
|
|
|
@ -12,6 +12,11 @@
|
|||
#ifndef _MMC_MMC_OPS_H
|
||||
#define _MMC_MMC_OPS_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct mmc_host;
|
||||
struct mmc_card;
|
||||
|
||||
int mmc_select_card(struct mmc_card *card);
|
||||
int mmc_deselect_cards(struct mmc_host *host);
|
||||
int mmc_set_dsr(struct mmc_host *host);
|
||||
|
@ -26,12 +31,21 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
|
|||
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
|
||||
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
|
||||
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
|
||||
int mmc_interrupt_hpi(struct mmc_card *card);
|
||||
int mmc_can_ext_csd(struct mmc_card *card);
|
||||
int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
|
||||
int mmc_switch_status(struct mmc_card *card);
|
||||
int __mmc_switch_status(struct mmc_card *card, bool crc_err_fatal);
|
||||
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
unsigned int timeout_ms, unsigned char timing,
|
||||
bool use_busy_signal, bool send_status, bool retry_crc_err);
|
||||
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
unsigned int timeout_ms);
|
||||
int mmc_stop_bkops(struct mmc_card *card);
|
||||
int mmc_read_bkops_status(struct mmc_card *card);
|
||||
void mmc_start_bkops(struct mmc_card *card, bool from_exception);
|
||||
int mmc_can_reset(struct mmc_card *card);
|
||||
int mmc_flush_cache(struct mmc_card *card);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -22,6 +22,11 @@
|
|||
#include <linux/seq_file.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
#include "host.h"
|
||||
#include "bus.h"
|
||||
|
||||
#define RESULT_OK 0
|
||||
#define RESULT_FAIL 1
|
||||
#define RESULT_UNSUP_HOST 2
|
||||
|
@ -260,7 +265,7 @@ static int mmc_test_busy(struct mmc_command *cmd)
|
|||
static int mmc_test_wait_busy(struct mmc_test_card *test)
|
||||
{
|
||||
int ret, busy;
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
busy = 0;
|
||||
do {
|
||||
|
@ -277,8 +282,7 @@ static int mmc_test_wait_busy(struct mmc_test_card *test)
|
|||
if (!busy && mmc_test_busy(&cmd)) {
|
||||
busy = 1;
|
||||
if (test->card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
|
||||
pr_info("%s: Warning: Host did not "
|
||||
"wait for busy state to end.\n",
|
||||
pr_info("%s: Warning: Host did not wait for busy state to end.\n",
|
||||
mmc_hostname(test->card->host));
|
||||
}
|
||||
} while (mmc_test_busy(&cmd));
|
||||
|
@ -292,10 +296,10 @@ static int mmc_test_wait_busy(struct mmc_test_card *test)
|
|||
static int mmc_test_buffer_transfer(struct mmc_test_card *test,
|
||||
u8 *buffer, unsigned addr, unsigned blksz, int write)
|
||||
{
|
||||
struct mmc_request mrq = {0};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command stop = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct mmc_request mrq = {};
|
||||
struct mmc_command cmd = {};
|
||||
struct mmc_command stop = {};
|
||||
struct mmc_data data = {};
|
||||
|
||||
struct scatterlist sg;
|
||||
|
||||
|
@ -357,12 +361,11 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz,
|
|||
if (max_segs > max_page_cnt)
|
||||
max_segs = max_page_cnt;
|
||||
|
||||
mem = kzalloc(sizeof(struct mmc_test_mem), GFP_KERNEL);
|
||||
mem = kzalloc(sizeof(*mem), GFP_KERNEL);
|
||||
if (!mem)
|
||||
return NULL;
|
||||
|
||||
mem->arr = kzalloc(sizeof(struct mmc_test_pages) * max_segs,
|
||||
GFP_KERNEL);
|
||||
mem->arr = kcalloc(max_segs, sizeof(*mem->arr), GFP_KERNEL);
|
||||
if (!mem->arr)
|
||||
goto out_free;
|
||||
|
||||
|
@ -546,7 +549,7 @@ static void mmc_test_save_transfer_result(struct mmc_test_card *test,
|
|||
if (!test->gr)
|
||||
return;
|
||||
|
||||
tr = kmalloc(sizeof(struct mmc_test_transfer_result), GFP_KERNEL);
|
||||
tr = kmalloc(sizeof(*tr), GFP_KERNEL);
|
||||
if (!tr)
|
||||
return;
|
||||
|
||||
|
@ -641,11 +644,11 @@ static int __mmc_test_prepare(struct mmc_test_card *test, int write)
|
|||
if (write)
|
||||
memset(test->buffer, 0xDF, 512);
|
||||
else {
|
||||
for (i = 0;i < 512;i++)
|
||||
for (i = 0; i < 512; i++)
|
||||
test->buffer[i] = i;
|
||||
}
|
||||
|
||||
for (i = 0;i < BUFFER_SIZE / 512;i++) {
|
||||
for (i = 0; i < BUFFER_SIZE / 512; i++) {
|
||||
ret = mmc_test_buffer_transfer(test, test->buffer, i, 512, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -674,7 +677,7 @@ static int mmc_test_cleanup(struct mmc_test_card *test)
|
|||
|
||||
memset(test->buffer, 0, 512);
|
||||
|
||||
for (i = 0;i < BUFFER_SIZE / 512;i++) {
|
||||
for (i = 0; i < BUFFER_SIZE / 512; i++) {
|
||||
ret = mmc_test_buffer_transfer(test, test->buffer, i, 512, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -850,7 +853,7 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test,
|
|||
for (i = 0; i < count; i++) {
|
||||
mmc_test_prepare_mrq(test, cur_areq->mrq, sg, sg_len, dev_addr,
|
||||
blocks, blksz, write);
|
||||
done_areq = mmc_start_req(test->card->host, cur_areq, &status);
|
||||
done_areq = mmc_start_areq(test->card->host, cur_areq, &status);
|
||||
|
||||
if (status != MMC_BLK_SUCCESS || (!done_areq && i > 0)) {
|
||||
ret = RESULT_FAIL;
|
||||
|
@ -869,7 +872,7 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test,
|
|||
dev_addr += blocks;
|
||||
}
|
||||
|
||||
done_areq = mmc_start_req(test->card->host, NULL, &status);
|
||||
done_areq = mmc_start_areq(test->card->host, NULL, &status);
|
||||
if (status != MMC_BLK_SUCCESS)
|
||||
ret = RESULT_FAIL;
|
||||
|
||||
|
@ -885,10 +888,10 @@ static int mmc_test_simple_transfer(struct mmc_test_card *test,
|
|||
struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
|
||||
unsigned blocks, unsigned blksz, int write)
|
||||
{
|
||||
struct mmc_request mrq = {0};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command stop = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct mmc_request mrq = {};
|
||||
struct mmc_command cmd = {};
|
||||
struct mmc_command stop = {};
|
||||
struct mmc_data data = {};
|
||||
|
||||
mrq.cmd = &cmd;
|
||||
mrq.data = &data;
|
||||
|
@ -910,10 +913,10 @@ static int mmc_test_simple_transfer(struct mmc_test_card *test,
|
|||
static int mmc_test_broken_transfer(struct mmc_test_card *test,
|
||||
unsigned blocks, unsigned blksz, int write)
|
||||
{
|
||||
struct mmc_request mrq = {0};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command stop = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct mmc_request mrq = {};
|
||||
struct mmc_command cmd = {};
|
||||
struct mmc_command stop = {};
|
||||
struct mmc_data data = {};
|
||||
|
||||
struct scatterlist sg;
|
||||
|
||||
|
@ -946,7 +949,7 @@ static int mmc_test_transfer(struct mmc_test_card *test,
|
|||
unsigned long flags;
|
||||
|
||||
if (write) {
|
||||
for (i = 0;i < blocks * blksz;i++)
|
||||
for (i = 0; i < blocks * blksz; i++)
|
||||
test->scratch[i] = i;
|
||||
} else {
|
||||
memset(test->scratch, 0, BUFFER_SIZE);
|
||||
|
@ -980,7 +983,7 @@ static int mmc_test_transfer(struct mmc_test_card *test,
|
|||
|
||||
memset(test->buffer, 0, sectors * 512);
|
||||
|
||||
for (i = 0;i < sectors;i++) {
|
||||
for (i = 0; i < sectors; i++) {
|
||||
ret = mmc_test_buffer_transfer(test,
|
||||
test->buffer + i * 512,
|
||||
dev_addr + i, 512, 0);
|
||||
|
@ -988,12 +991,12 @@ static int mmc_test_transfer(struct mmc_test_card *test,
|
|||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0;i < blocks * blksz;i++) {
|
||||
for (i = 0; i < blocks * blksz; i++) {
|
||||
if (test->buffer[i] != (u8)i)
|
||||
return RESULT_FAIL;
|
||||
}
|
||||
|
||||
for (;i < sectors * 512;i++) {
|
||||
for (; i < sectors * 512; i++) {
|
||||
if (test->buffer[i] != 0xDF)
|
||||
return RESULT_FAIL;
|
||||
}
|
||||
|
@ -1001,7 +1004,7 @@ static int mmc_test_transfer(struct mmc_test_card *test,
|
|||
local_irq_save(flags);
|
||||
sg_copy_to_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
|
||||
local_irq_restore(flags);
|
||||
for (i = 0;i < blocks * blksz;i++) {
|
||||
for (i = 0; i < blocks * blksz; i++) {
|
||||
if (test->scratch[i] != (u8)i)
|
||||
return RESULT_FAIL;
|
||||
}
|
||||
|
@ -1086,7 +1089,7 @@ static int mmc_test_multi_write(struct mmc_test_card *test)
|
|||
|
||||
sg_init_one(&sg, test->buffer, size);
|
||||
|
||||
return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
|
||||
return mmc_test_transfer(test, &sg, 1, 0, size / 512, 512, 1);
|
||||
}
|
||||
|
||||
static int mmc_test_multi_read(struct mmc_test_card *test)
|
||||
|
@ -1107,7 +1110,7 @@ static int mmc_test_multi_read(struct mmc_test_card *test)
|
|||
|
||||
sg_init_one(&sg, test->buffer, size);
|
||||
|
||||
return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
|
||||
return mmc_test_transfer(test, &sg, 1, 0, size / 512, 512, 0);
|
||||
}
|
||||
|
||||
static int mmc_test_pow2_write(struct mmc_test_card *test)
|
||||
|
@ -1118,7 +1121,7 @@ static int mmc_test_pow2_write(struct mmc_test_card *test)
|
|||
if (!test->card->csd.write_partial)
|
||||
return RESULT_UNSUP_CARD;
|
||||
|
||||
for (i = 1; i < 512;i <<= 1) {
|
||||
for (i = 1; i < 512; i <<= 1) {
|
||||
sg_init_one(&sg, test->buffer, i);
|
||||
ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 1);
|
||||
if (ret)
|
||||
|
@ -1136,7 +1139,7 @@ static int mmc_test_pow2_read(struct mmc_test_card *test)
|
|||
if (!test->card->csd.read_partial)
|
||||
return RESULT_UNSUP_CARD;
|
||||
|
||||
for (i = 1; i < 512;i <<= 1) {
|
||||
for (i = 1; i < 512; i <<= 1) {
|
||||
sg_init_one(&sg, test->buffer, i);
|
||||
ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 0);
|
||||
if (ret)
|
||||
|
@ -1154,7 +1157,7 @@ static int mmc_test_weird_write(struct mmc_test_card *test)
|
|||
if (!test->card->csd.write_partial)
|
||||
return RESULT_UNSUP_CARD;
|
||||
|
||||
for (i = 3; i < 512;i += 7) {
|
||||
for (i = 3; i < 512; i += 7) {
|
||||
sg_init_one(&sg, test->buffer, i);
|
||||
ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 1);
|
||||
if (ret)
|
||||
|
@ -1172,7 +1175,7 @@ static int mmc_test_weird_read(struct mmc_test_card *test)
|
|||
if (!test->card->csd.read_partial)
|
||||
return RESULT_UNSUP_CARD;
|
||||
|
||||
for (i = 3; i < 512;i += 7) {
|
||||
for (i = 3; i < 512; i += 7) {
|
||||
sg_init_one(&sg, test->buffer, i);
|
||||
ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 0);
|
||||
if (ret)
|
||||
|
@ -1231,7 +1234,7 @@ static int mmc_test_align_multi_write(struct mmc_test_card *test)
|
|||
|
||||
for (i = 1; i < TEST_ALIGN_END; i++) {
|
||||
sg_init_one(&sg, test->buffer + i, size);
|
||||
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
|
||||
ret = mmc_test_transfer(test, &sg, 1, 0, size / 512, 512, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -1258,7 +1261,7 @@ static int mmc_test_align_multi_read(struct mmc_test_card *test)
|
|||
|
||||
for (i = 1; i < TEST_ALIGN_END; i++) {
|
||||
sg_init_one(&sg, test->buffer + i, size);
|
||||
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
|
||||
ret = mmc_test_transfer(test, &sg, 1, 0, size / 512, 512, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -1357,7 +1360,7 @@ static int mmc_test_multi_write_high(struct mmc_test_card *test)
|
|||
sg_init_table(&sg, 1);
|
||||
sg_set_page(&sg, test->highmem, size, 0);
|
||||
|
||||
return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
|
||||
return mmc_test_transfer(test, &sg, 1, 0, size / 512, 512, 1);
|
||||
}
|
||||
|
||||
static int mmc_test_multi_read_high(struct mmc_test_card *test)
|
||||
|
@ -1379,7 +1382,7 @@ static int mmc_test_multi_read_high(struct mmc_test_card *test)
|
|||
sg_init_table(&sg, 1);
|
||||
sg_set_page(&sg, test->highmem, size, 0);
|
||||
|
||||
return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
|
||||
return mmc_test_transfer(test, &sg, 1, 0, size / 512, 512, 0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -1533,7 +1536,7 @@ static int mmc_test_area_cleanup(struct mmc_test_card *test)
|
|||
|
||||
/*
|
||||
* Initialize an area for testing large transfers. The test area is set to the
|
||||
* middle of the card because cards may have different charateristics at the
|
||||
* middle of the card because cards may have different characteristics at the
|
||||
* front (for FAT file system optimization). Optionally, the area is erased
|
||||
* (if the card supports it) which may improve write performance. Optionally,
|
||||
* the area is filled with data for subsequent read tests.
|
||||
|
@ -1579,7 +1582,7 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
|
|||
if (!t->mem)
|
||||
return -ENOMEM;
|
||||
|
||||
t->sg = kmalloc(sizeof(struct scatterlist) * t->max_segs, GFP_KERNEL);
|
||||
t->sg = kmalloc_array(t->max_segs, sizeof(*t->sg), GFP_KERNEL);
|
||||
if (!t->sg) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free;
|
||||
|
@ -2147,7 +2150,7 @@ static int mmc_test_rw_multiple_sg_len(struct mmc_test_card *test,
|
|||
int i;
|
||||
|
||||
for (i = 0 ; i < rw->len && ret == 0; i++) {
|
||||
ret = mmc_test_rw_multiple(test, rw, 512*1024, rw->size,
|
||||
ret = mmc_test_rw_multiple(test, rw, 512 * 1024, rw->size,
|
||||
rw->sg_len[i]);
|
||||
if (ret)
|
||||
break;
|
||||
|
@ -2399,7 +2402,7 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
|
|||
|
||||
/* Start ongoing data request */
|
||||
if (use_areq) {
|
||||
mmc_start_req(host, &test_areq.areq, &blkstat);
|
||||
mmc_start_areq(host, &test_areq.areq, &blkstat);
|
||||
if (blkstat != MMC_BLK_SUCCESS) {
|
||||
ret = RESULT_FAIL;
|
||||
goto out_free;
|
||||
|
@ -2437,7 +2440,7 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
|
|||
|
||||
/* Wait for data request to complete */
|
||||
if (use_areq) {
|
||||
mmc_start_req(host, NULL, &blkstat);
|
||||
mmc_start_areq(host, NULL, &blkstat);
|
||||
if (blkstat != MMC_BLK_SUCCESS)
|
||||
ret = RESULT_FAIL;
|
||||
} else {
|
||||
|
@ -2954,7 +2957,7 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
|||
|
||||
mmc_claim_host(test->card->host);
|
||||
|
||||
for (i = 0;i < ARRAY_SIZE(mmc_test_cases);i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(mmc_test_cases); i++) {
|
||||
struct mmc_test_general_result *gr;
|
||||
|
||||
if (testcase && ((i + 1) != testcase))
|
||||
|
@ -2967,16 +2970,14 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
|||
if (mmc_test_cases[i].prepare) {
|
||||
ret = mmc_test_cases[i].prepare(test);
|
||||
if (ret) {
|
||||
pr_info("%s: Result: Prepare "
|
||||
"stage failed! (%d)\n",
|
||||
pr_info("%s: Result: Prepare stage failed! (%d)\n",
|
||||
mmc_hostname(test->card->host),
|
||||
ret);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
gr = kzalloc(sizeof(struct mmc_test_general_result),
|
||||
GFP_KERNEL);
|
||||
gr = kzalloc(sizeof(*gr), GFP_KERNEL);
|
||||
if (gr) {
|
||||
INIT_LIST_HEAD(&gr->tr_lst);
|
||||
|
||||
|
@ -3005,13 +3006,11 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
|||
mmc_hostname(test->card->host));
|
||||
break;
|
||||
case RESULT_UNSUP_HOST:
|
||||
pr_info("%s: Result: UNSUPPORTED "
|
||||
"(by host)\n",
|
||||
pr_info("%s: Result: UNSUPPORTED (by host)\n",
|
||||
mmc_hostname(test->card->host));
|
||||
break;
|
||||
case RESULT_UNSUP_CARD:
|
||||
pr_info("%s: Result: UNSUPPORTED "
|
||||
"(by card)\n",
|
||||
pr_info("%s: Result: UNSUPPORTED (by card)\n",
|
||||
mmc_hostname(test->card->host));
|
||||
break;
|
||||
default:
|
||||
|
@ -3026,8 +3025,7 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
|
|||
if (mmc_test_cases[i].cleanup) {
|
||||
ret = mmc_test_cases[i].cleanup(test);
|
||||
if (ret) {
|
||||
pr_info("%s: Warning: Cleanup "
|
||||
"stage failed! (%d)\n",
|
||||
pr_info("%s: Warning: Cleanup stage failed! (%d)\n",
|
||||
mmc_hostname(test->card->host),
|
||||
ret);
|
||||
}
|
||||
|
@ -3113,7 +3111,7 @@ static ssize_t mtf_test_write(struct file *file, const char __user *buf,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
test = kzalloc(sizeof(struct mmc_test_card), GFP_KERNEL);
|
||||
test = kzalloc(sizeof(*test), GFP_KERNEL);
|
||||
if (!test)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -3163,9 +3161,9 @@ static int mtf_testlist_show(struct seq_file *sf, void *data)
|
|||
|
||||
mutex_lock(&mmc_test_lock);
|
||||
|
||||
seq_printf(sf, "0:\tRun all tests\n");
|
||||
seq_puts(sf, "0:\tRun all tests\n");
|
||||
for (i = 0; i < ARRAY_SIZE(mmc_test_cases); i++)
|
||||
seq_printf(sf, "%d:\t%s\n", i+1, mmc_test_cases[i].name);
|
||||
seq_printf(sf, "%d:\t%s\n", i + 1, mmc_test_cases[i].name);
|
||||
|
||||
mutex_unlock(&mmc_test_lock);
|
||||
|
||||
|
@ -3218,7 +3216,7 @@ static int __mmc_test_register_dbgfs_file(struct mmc_card *card,
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
df = kmalloc(sizeof(struct mmc_test_dbgfs_file), GFP_KERNEL);
|
||||
df = kmalloc(sizeof(*df), GFP_KERNEL);
|
||||
if (!df) {
|
||||
debugfs_remove(file);
|
||||
dev_err(&card->dev,
|
||||
|
|
|
@ -8,7 +8,11 @@
|
|||
#ifndef _MMC_CORE_PWRSEQ_H
|
||||
#define _MMC_CORE_PWRSEQ_H
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct mmc_host;
|
||||
struct device;
|
||||
struct module;
|
||||
|
||||
struct mmc_pwrseq_ops {
|
||||
void (*pre_power_on)(struct mmc_host *host);
|
||||
|
|
117
drivers/mmc/core/pwrseq_sd8787.c
Normal file
117
drivers/mmc/core/pwrseq_sd8787.c
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* pwrseq_sd8787.c - power sequence support for Marvell SD8787 BT + Wifi chip
|
||||
*
|
||||
* Copyright (C) 2016 Matt Ranostay <matt@ranostay.consulting>
|
||||
*
|
||||
* Based on the original work pwrseq_simple.c
|
||||
* Copyright (C) 2014 Linaro Ltd
|
||||
* Author: Ulf Hansson <ulf.hansson@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
#include "pwrseq.h"
|
||||
|
||||
struct mmc_pwrseq_sd8787 {
|
||||
struct mmc_pwrseq pwrseq;
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct gpio_desc *pwrdn_gpio;
|
||||
};
|
||||
|
||||
#define to_pwrseq_sd8787(p) container_of(p, struct mmc_pwrseq_sd8787, pwrseq)
|
||||
|
||||
static void mmc_pwrseq_sd8787_pre_power_on(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_pwrseq_sd8787 *pwrseq = to_pwrseq_sd8787(host->pwrseq);
|
||||
|
||||
gpiod_set_value_cansleep(pwrseq->reset_gpio, 1);
|
||||
|
||||
msleep(300);
|
||||
gpiod_set_value_cansleep(pwrseq->pwrdn_gpio, 1);
|
||||
}
|
||||
|
||||
static void mmc_pwrseq_sd8787_power_off(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_pwrseq_sd8787 *pwrseq = to_pwrseq_sd8787(host->pwrseq);
|
||||
|
||||
gpiod_set_value_cansleep(pwrseq->pwrdn_gpio, 0);
|
||||
gpiod_set_value_cansleep(pwrseq->reset_gpio, 0);
|
||||
}
|
||||
|
||||
static const struct mmc_pwrseq_ops mmc_pwrseq_sd8787_ops = {
|
||||
.pre_power_on = mmc_pwrseq_sd8787_pre_power_on,
|
||||
.power_off = mmc_pwrseq_sd8787_power_off,
|
||||
};
|
||||
|
||||
static const struct of_device_id mmc_pwrseq_sd8787_of_match[] = {
|
||||
{ .compatible = "mmc-pwrseq-sd8787",},
|
||||
{/* sentinel */},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mmc_pwrseq_sd8787_of_match);
|
||||
|
||||
static int mmc_pwrseq_sd8787_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mmc_pwrseq_sd8787 *pwrseq;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL);
|
||||
if (!pwrseq)
|
||||
return -ENOMEM;
|
||||
|
||||
pwrseq->pwrdn_gpio = devm_gpiod_get(dev, "powerdown", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(pwrseq->pwrdn_gpio))
|
||||
return PTR_ERR(pwrseq->pwrdn_gpio);
|
||||
|
||||
pwrseq->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(pwrseq->reset_gpio))
|
||||
return PTR_ERR(pwrseq->reset_gpio);
|
||||
|
||||
pwrseq->pwrseq.dev = dev;
|
||||
pwrseq->pwrseq.ops = &mmc_pwrseq_sd8787_ops;
|
||||
pwrseq->pwrseq.owner = THIS_MODULE;
|
||||
platform_set_drvdata(pdev, pwrseq);
|
||||
|
||||
return mmc_pwrseq_register(&pwrseq->pwrseq);
|
||||
}
|
||||
|
||||
static int mmc_pwrseq_sd8787_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mmc_pwrseq_sd8787 *pwrseq = platform_get_drvdata(pdev);
|
||||
|
||||
mmc_pwrseq_unregister(&pwrseq->pwrseq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mmc_pwrseq_sd8787_driver = {
|
||||
.probe = mmc_pwrseq_sd8787_probe,
|
||||
.remove = mmc_pwrseq_sd8787_remove,
|
||||
.driver = {
|
||||
.name = "pwrseq_sd8787",
|
||||
.of_match_table = mmc_pwrseq_sd8787_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(mmc_pwrseq_sd8787_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include "queue.h"
|
||||
#include "block.h"
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
|
||||
#define MMC_QUEUE_BOUNCESZ 65536
|
||||
|
||||
|
@ -75,8 +77,8 @@ static int mmc_queue_thread(void *d)
|
|||
set_current_state(TASK_RUNNING);
|
||||
mmc_blk_issue_rq(mq, req);
|
||||
cond_resched();
|
||||
if (mq->flags & MMC_QUEUE_NEW_REQUEST) {
|
||||
mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
|
||||
if (mq->new_request) {
|
||||
mq->new_request = false;
|
||||
continue; /* fetch again */
|
||||
}
|
||||
|
||||
|
@ -143,7 +145,7 @@ static struct scatterlist *mmc_alloc_sg(int sg_len, int *err)
|
|||
{
|
||||
struct scatterlist *sg;
|
||||
|
||||
sg = kmalloc(sizeof(struct scatterlist)*sg_len, GFP_KERNEL);
|
||||
sg = kmalloc_array(sg_len, sizeof(*sg), GFP_KERNEL);
|
||||
if (!sg)
|
||||
*err = -ENOMEM;
|
||||
else {
|
||||
|
@ -390,8 +392,8 @@ void mmc_queue_suspend(struct mmc_queue *mq)
|
|||
struct request_queue *q = mq->queue;
|
||||
unsigned long flags;
|
||||
|
||||
if (!(mq->flags & MMC_QUEUE_SUSPENDED)) {
|
||||
mq->flags |= MMC_QUEUE_SUSPENDED;
|
||||
if (!mq->suspended) {
|
||||
mq->suspended |= true;
|
||||
|
||||
spin_lock_irqsave(q->queue_lock, flags);
|
||||
blk_stop_queue(q);
|
||||
|
@ -410,8 +412,8 @@ void mmc_queue_resume(struct mmc_queue *mq)
|
|||
struct request_queue *q = mq->queue;
|
||||
unsigned long flags;
|
||||
|
||||
if (mq->flags & MMC_QUEUE_SUSPENDED) {
|
||||
mq->flags &= ~MMC_QUEUE_SUSPENDED;
|
||||
if (mq->suspended) {
|
||||
mq->suspended = false;
|
||||
|
||||
up(&mq->thread_sem);
|
||||
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
#ifndef MMC_QUEUE_H
|
||||
#define MMC_QUEUE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/mmc/core.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
static inline bool mmc_req_is_special(struct request *req)
|
||||
{
|
||||
return req &&
|
||||
|
@ -9,7 +14,6 @@ static inline bool mmc_req_is_special(struct request *req)
|
|||
req_op(req) == REQ_OP_SECURE_ERASE);
|
||||
}
|
||||
|
||||
struct request;
|
||||
struct task_struct;
|
||||
struct mmc_blk_data;
|
||||
|
||||
|
@ -29,16 +33,15 @@ struct mmc_queue_req {
|
|||
char *bounce_buf;
|
||||
struct scatterlist *bounce_sg;
|
||||
unsigned int bounce_sg_len;
|
||||
struct mmc_async_req mmc_active;
|
||||
struct mmc_async_req areq;
|
||||
};
|
||||
|
||||
struct mmc_queue {
|
||||
struct mmc_card *card;
|
||||
struct task_struct *thread;
|
||||
struct semaphore thread_sem;
|
||||
unsigned int flags;
|
||||
#define MMC_QUEUE_SUSPENDED (1 << 0)
|
||||
#define MMC_QUEUE_NEW_REQUEST (1 << 1)
|
||||
bool new_request;
|
||||
bool suspended;
|
||||
bool asleep;
|
||||
struct mmc_blk_data *blkdata;
|
||||
struct request_queue *queue;
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* This file contains work-arounds for many known SD/MMC
|
||||
* and SDIO hardware bugs.
|
||||
*
|
||||
* Copyright (c) 2011 Andrei Warkentin <andreiw@motorola.com>
|
||||
* Copyright (c) 2011 Pierre Tardy <tardyp@gmail.com>
|
||||
* Inspired from pci fixup code:
|
||||
* Copyright (c) 1999 Martin Mares <mj@ucw.cz>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/sdio_ids.h>
|
||||
|
||||
#ifndef SDIO_VENDOR_ID_TI
|
||||
#define SDIO_VENDOR_ID_TI 0x0097
|
||||
#endif
|
||||
|
||||
#ifndef SDIO_DEVICE_ID_TI_WL1271
|
||||
#define SDIO_DEVICE_ID_TI_WL1271 0x4076
|
||||
#endif
|
||||
|
||||
#ifndef SDIO_VENDOR_ID_STE
|
||||
#define SDIO_VENDOR_ID_STE 0x0020
|
||||
#endif
|
||||
|
||||
#ifndef SDIO_DEVICE_ID_STE_CW1200
|
||||
#define SDIO_DEVICE_ID_STE_CW1200 0x2280
|
||||
#endif
|
||||
|
||||
#ifndef SDIO_DEVICE_ID_MARVELL_8797_F0
|
||||
#define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128
|
||||
#endif
|
||||
|
||||
static const struct mmc_fixup mmc_fixup_methods[] = {
|
||||
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
|
||||
add_quirk, MMC_QUIRK_NONSTD_FUNC_IF),
|
||||
|
||||
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
|
||||
add_quirk, MMC_QUIRK_DISABLE_CD),
|
||||
|
||||
SDIO_FIXUP(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200,
|
||||
add_quirk, MMC_QUIRK_BROKEN_BYTE_MODE_512),
|
||||
|
||||
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0,
|
||||
add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING),
|
||||
|
||||
END_FIXUP
|
||||
};
|
||||
|
||||
void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table)
|
||||
{
|
||||
const struct mmc_fixup *f;
|
||||
u64 rev = cid_rev_card(card);
|
||||
|
||||
/* Non-core specific workarounds. */
|
||||
if (!table)
|
||||
table = mmc_fixup_methods;
|
||||
|
||||
for (f = table; f->vendor_fixup; f++) {
|
||||
if ((f->manfid == CID_MANFID_ANY ||
|
||||
f->manfid == card->cid.manfid) &&
|
||||
(f->oemid == CID_OEMID_ANY ||
|
||||
f->oemid == card->cid.oemid) &&
|
||||
(f->name == CID_NAME_ANY ||
|
||||
!strncmp(f->name, card->cid.prod_name,
|
||||
sizeof(card->cid.prod_name))) &&
|
||||
(f->cis_vendor == card->cis.vendor ||
|
||||
f->cis_vendor == (u16) SDIO_ANY_ID) &&
|
||||
(f->cis_device == card->cis.device ||
|
||||
f->cis_device == (u16) SDIO_ANY_ID) &&
|
||||
(f->ext_csd_rev == EXT_CSD_REV_ANY ||
|
||||
f->ext_csd_rev == card->ext_csd.rev) &&
|
||||
rev >= f->rev_start && rev <= f->rev_end) {
|
||||
dev_dbg(&card->dev, "calling %pf\n", f->vendor_fixup);
|
||||
f->vendor_fixup(card, f->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_fixup_device);
|
148
drivers/mmc/core/quirks.h
Normal file
148
drivers/mmc/core/quirks.h
Normal file
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* This file contains work-arounds for many known SD/MMC
|
||||
* and SDIO hardware bugs.
|
||||
*
|
||||
* Copyright (c) 2011 Andrei Warkentin <andreiw@motorola.com>
|
||||
* Copyright (c) 2011 Pierre Tardy <tardyp@gmail.com>
|
||||
* Inspired from pci fixup code:
|
||||
* Copyright (c) 1999 Martin Mares <mj@ucw.cz>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/mmc/sdio_ids.h>
|
||||
|
||||
#include "card.h"
|
||||
|
||||
static const struct mmc_fixup mmc_blk_fixups[] = {
|
||||
#define INAND_CMD38_ARG_EXT_CSD 113
|
||||
#define INAND_CMD38_ARG_ERASE 0x00
|
||||
#define INAND_CMD38_ARG_TRIM 0x01
|
||||
#define INAND_CMD38_ARG_SECERASE 0x80
|
||||
#define INAND_CMD38_ARG_SECTRIM1 0x81
|
||||
#define INAND_CMD38_ARG_SECTRIM2 0x88
|
||||
/* CMD38 argument is passed through EXT_CSD[113] */
|
||||
MMC_FIXUP("SEM02G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM04G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM08G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM16G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM32G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
|
||||
/*
|
||||
* Some MMC cards experience performance degradation with CMD23
|
||||
* instead of CMD12-bounded multiblock transfers. For now we'll
|
||||
* black list what's bad...
|
||||
* - Certain Toshiba cards.
|
||||
*
|
||||
* N.B. This doesn't affect SD cards.
|
||||
*/
|
||||
MMC_FIXUP("SDMB-32", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
MMC_FIXUP("SDM032", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
MMC_FIXUP("MMC08G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
MMC_FIXUP("MMC16G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
MMC_FIXUP("MMC32G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
|
||||
/*
|
||||
* Some MMC cards need longer data read timeout than indicated in CSD.
|
||||
*/
|
||||
MMC_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc,
|
||||
MMC_QUIRK_LONG_READ_TIME),
|
||||
MMC_FIXUP("008GE0", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_LONG_READ_TIME),
|
||||
|
||||
/*
|
||||
* On these Samsung MoviNAND parts, performing secure erase or
|
||||
* secure trim can result in unrecoverable corruption due to a
|
||||
* firmware bug.
|
||||
*/
|
||||
MMC_FIXUP("M8G2FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
MMC_FIXUP("MAG4FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
MMC_FIXUP("MBG8FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
MMC_FIXUP("MCGAFA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
MMC_FIXUP("VAL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
MMC_FIXUP("VYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
MMC_FIXUP("KYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
|
||||
/*
|
||||
* On Some Kingston eMMCs, performing trim can result in
|
||||
* unrecoverable data conrruption occasionally due to a firmware bug.
|
||||
*/
|
||||
MMC_FIXUP("V10008", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_TRIM_BROKEN),
|
||||
MMC_FIXUP("V10016", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_TRIM_BROKEN),
|
||||
|
||||
END_FIXUP
|
||||
};
|
||||
|
||||
static const struct mmc_fixup mmc_ext_csd_fixups[] = {
|
||||
/*
|
||||
* Certain Hynix eMMC 4.41 cards might get broken when HPI feature
|
||||
* is used so disable the HPI feature for such buggy cards.
|
||||
*/
|
||||
MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_HYNIX,
|
||||
0x014a, add_quirk, MMC_QUIRK_BROKEN_HPI, 5),
|
||||
|
||||
END_FIXUP
|
||||
};
|
||||
|
||||
static const struct mmc_fixup sdio_fixup_methods[] = {
|
||||
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
|
||||
add_quirk, MMC_QUIRK_NONSTD_FUNC_IF),
|
||||
|
||||
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
|
||||
add_quirk, MMC_QUIRK_DISABLE_CD),
|
||||
|
||||
SDIO_FIXUP(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200,
|
||||
add_quirk, MMC_QUIRK_BROKEN_BYTE_MODE_512),
|
||||
|
||||
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0,
|
||||
add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING),
|
||||
|
||||
END_FIXUP
|
||||
};
|
||||
|
||||
static inline void mmc_fixup_device(struct mmc_card *card,
|
||||
const struct mmc_fixup *table)
|
||||
{
|
||||
const struct mmc_fixup *f;
|
||||
u64 rev = cid_rev_card(card);
|
||||
|
||||
for (f = table; f->vendor_fixup; f++) {
|
||||
if ((f->manfid == CID_MANFID_ANY ||
|
||||
f->manfid == card->cid.manfid) &&
|
||||
(f->oemid == CID_OEMID_ANY ||
|
||||
f->oemid == card->cid.oemid) &&
|
||||
(f->name == CID_NAME_ANY ||
|
||||
!strncmp(f->name, card->cid.prod_name,
|
||||
sizeof(card->cid.prod_name))) &&
|
||||
(f->cis_vendor == card->cis.vendor ||
|
||||
f->cis_vendor == (u16) SDIO_ANY_ID) &&
|
||||
(f->cis_device == card->cis.device ||
|
||||
f->cis_device == (u16) SDIO_ANY_ID) &&
|
||||
(f->ext_csd_rev == EXT_CSD_REV_ANY ||
|
||||
f->ext_csd_rev == card->ext_csd.rev) &&
|
||||
rev >= f->rev_start && rev <= f->rev_end) {
|
||||
dev_dbg(&card->dev, "calling %pf\n", f->vendor_fixup);
|
||||
f->vendor_fixup(card, f->data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,6 +22,8 @@
|
|||
#include <linux/mmc/sd.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
#include "host.h"
|
||||
#include "bus.h"
|
||||
#include "mmc_ops.h"
|
||||
#include "sd.h"
|
||||
|
@ -786,8 +788,7 @@ try_again:
|
|||
*/
|
||||
if (!mmc_host_is_spi(host) && rocr &&
|
||||
((*rocr & 0x41000000) == 0x41000000)) {
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
|
||||
pocr);
|
||||
err = mmc_set_uhs_voltage(host, pocr);
|
||||
if (err == -EAGAIN) {
|
||||
retries--;
|
||||
goto try_again;
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
#ifndef _MMC_CORE_SD_H
|
||||
#define _MMC_CORE_SD_H
|
||||
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
extern struct device_type sd_type;
|
||||
|
||||
struct mmc_host;
|
||||
struct mmc_card;
|
||||
|
||||
int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr);
|
||||
int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card);
|
||||
void mmc_decode_cid(struct mmc_card *card);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
if (WARN_ON(card && card->host != host))
|
||||
return -EINVAL;
|
||||
|
@ -68,7 +68,7 @@ EXPORT_SYMBOL_GPL(mmc_app_cmd);
|
|||
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
|
||||
struct mmc_command *cmd, int retries)
|
||||
{
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_request mrq = {};
|
||||
|
||||
int i, err;
|
||||
|
||||
|
@ -120,7 +120,7 @@ EXPORT_SYMBOL(mmc_wait_for_app_cmd);
|
|||
|
||||
int mmc_app_set_bus_width(struct mmc_card *card, int width)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
cmd.opcode = SD_APP_SET_BUS_WIDTH;
|
||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||
|
@ -141,7 +141,7 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
|
|||
|
||||
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
int i, err = 0;
|
||||
|
||||
cmd.opcode = SD_APP_OP_COND;
|
||||
|
@ -185,7 +185,7 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
|||
|
||||
int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
int err;
|
||||
static const u8 test_pattern = 0xAA;
|
||||
u8 result_pattern;
|
||||
|
@ -217,7 +217,7 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
|
|||
int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
cmd.opcode = SD_SEND_RELATIVE_ADDR;
|
||||
cmd.arg = 0;
|
||||
|
@ -235,9 +235,9 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
|
|||
int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
|
||||
{
|
||||
int err;
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct mmc_request mrq = {};
|
||||
struct mmc_command cmd = {};
|
||||
struct mmc_data data = {};
|
||||
struct scatterlist sg;
|
||||
void *data_buf;
|
||||
|
||||
|
@ -290,9 +290,9 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
|
|||
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
|
||||
u8 value, u8 *resp)
|
||||
{
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct mmc_request mrq = {};
|
||||
struct mmc_command cmd = {};
|
||||
struct mmc_data data = {};
|
||||
struct scatterlist sg;
|
||||
|
||||
/* NOTE: caller guarantees resp is heap-allocated */
|
||||
|
@ -332,9 +332,9 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
|
|||
int mmc_app_sd_status(struct mmc_card *card, void *ssr)
|
||||
{
|
||||
int err;
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct mmc_request mrq = {};
|
||||
struct mmc_command cmd = {};
|
||||
struct mmc_data data = {};
|
||||
struct scatterlist sg;
|
||||
|
||||
/* NOTE: caller guarantees ssr is heap-allocated */
|
||||
|
|
|
@ -12,6 +12,12 @@
|
|||
#ifndef _MMC_SD_OPS_H
|
||||
#define _MMC_SD_OPS_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct mmc_card;
|
||||
struct mmc_host;
|
||||
struct mmc_command;
|
||||
|
||||
int mmc_app_set_bus_width(struct mmc_card *card, int width);
|
||||
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
|
||||
int mmc_send_if_cond(struct mmc_host *host, u32 ocr);
|
||||
|
@ -20,6 +26,9 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
|
|||
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
|
||||
u8 value, u8 *resp);
|
||||
int mmc_app_sd_status(struct mmc_card *card, void *ssr);
|
||||
int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card);
|
||||
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
|
||||
struct mmc_command *cmd, int retries);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -20,7 +20,10 @@
|
|||
#include <linux/mmc/sdio_ids.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
#include "host.h"
|
||||
#include "bus.h"
|
||||
#include "quirks.h"
|
||||
#include "sd.h"
|
||||
#include "sdio_bus.h"
|
||||
#include "mmc_ops.h"
|
||||
|
@ -541,6 +544,15 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static void mmc_sdio_resend_if_cond(struct mmc_host *host,
|
||||
struct mmc_card *card)
|
||||
{
|
||||
sdio_reset(host);
|
||||
mmc_go_idle(host);
|
||||
mmc_send_if_cond(host, host->ocr_avail);
|
||||
mmc_remove_card(card);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the detection and initialisation of a card.
|
||||
*
|
||||
|
@ -624,24 +636,21 @@ try_again:
|
|||
* to switch to 1.8V signaling level. No 1.8v signalling if
|
||||
* UHS mode is not enabled to maintain compatibility and some
|
||||
* systems that claim 1.8v signalling in fact do not support
|
||||
* it.
|
||||
* it. Per SDIO spec v3, section 3.1.2, if the voltage is already
|
||||
* 1.8v, the card sets S18A to 0 in the R4 response. So it will
|
||||
* fails to check rocr & R4_18V_PRESENT, but we still need to
|
||||
* try to init uhs card. sdio_read_cccr will take over this task
|
||||
* to make sure which speed mode should work.
|
||||
*/
|
||||
if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) {
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
|
||||
ocr_card);
|
||||
err = mmc_set_uhs_voltage(host, ocr_card);
|
||||
if (err == -EAGAIN) {
|
||||
sdio_reset(host);
|
||||
mmc_go_idle(host);
|
||||
mmc_send_if_cond(host, host->ocr_avail);
|
||||
mmc_remove_card(card);
|
||||
mmc_sdio_resend_if_cond(host, card);
|
||||
retries--;
|
||||
goto try_again;
|
||||
} else if (err) {
|
||||
ocr &= ~R4_18V_PRESENT;
|
||||
}
|
||||
err = 0;
|
||||
} else {
|
||||
ocr &= ~R4_18V_PRESENT;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -698,11 +707,20 @@ try_again:
|
|||
}
|
||||
|
||||
/*
|
||||
* Read the common registers.
|
||||
* Read the common registers. Note that we should try to
|
||||
* validate whether UHS would work or not.
|
||||
*/
|
||||
err = sdio_read_cccr(card, ocr);
|
||||
if (err)
|
||||
goto remove;
|
||||
if (err) {
|
||||
mmc_sdio_resend_if_cond(host, card);
|
||||
if (ocr & R4_18V_PRESENT) {
|
||||
/* Retry init sequence, but without R4_18V_PRESENT. */
|
||||
retries = 0;
|
||||
goto try_again;
|
||||
} else {
|
||||
goto remove;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the common CIS tuples.
|
||||
|
@ -721,7 +739,7 @@ try_again:
|
|||
card = oldcard;
|
||||
}
|
||||
card->ocr = ocr_card;
|
||||
mmc_fixup_device(card, NULL);
|
||||
mmc_fixup_device(card, sdio_fixup_methods);
|
||||
|
||||
if (card->type == MMC_TYPE_SD_COMBO) {
|
||||
err = mmc_sd_setup_card(host, card, oldcard != NULL);
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/of.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
#include "sdio_cis.h"
|
||||
#include "sdio_bus.h"
|
||||
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
#ifndef _MMC_CORE_SDIO_BUS_H
|
||||
#define _MMC_CORE_SDIO_BUS_H
|
||||
|
||||
struct mmc_card;
|
||||
struct sdio_func;
|
||||
|
||||
struct sdio_func *sdio_alloc_func(struct mmc_card *card);
|
||||
int sdio_add_func(struct sdio_func *func);
|
||||
void sdio_remove_func(struct sdio_func *func);
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
#ifndef _MMC_SDIO_CIS_H
|
||||
#define _MMC_SDIO_CIS_H
|
||||
|
||||
struct mmc_card;
|
||||
struct sdio_func;
|
||||
|
||||
int sdio_read_common_cis(struct mmc_card *card);
|
||||
void sdio_free_common_cis(struct mmc_card *card);
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include <linux/mmc/sdio_func.h>
|
||||
|
||||
#include "sdio_ops.h"
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
|
||||
/**
|
||||
* sdio_claim_host - exclusively claim a bus for a certain SDIO function
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include <linux/mmc/sdio_func.h>
|
||||
|
||||
#include "sdio_ops.h"
|
||||
#include "core.h"
|
||||
#include "card.h"
|
||||
|
||||
static int process_sdio_pending_irqs(struct mmc_host *host)
|
||||
{
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
int i, err = 0;
|
||||
|
||||
cmd.opcode = SD_IO_SEND_OP_COND;
|
||||
|
@ -66,7 +66,7 @@ int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
|||
static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn,
|
||||
unsigned addr, u8 in, u8 *out)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
int err;
|
||||
|
||||
if (fn > 7)
|
||||
|
@ -118,9 +118,9 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
|
|||
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
|
||||
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
|
||||
{
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct mmc_request mrq = {};
|
||||
struct mmc_command cmd = {};
|
||||
struct mmc_data data = {};
|
||||
struct scatterlist sg, *sg_ptr;
|
||||
struct sg_table sgtable;
|
||||
unsigned int nents, left_size, i;
|
||||
|
|
|
@ -12,14 +12,19 @@
|
|||
#ifndef _MMC_SDIO_OPS_H
|
||||
#define _MMC_SDIO_OPS_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
|
||||
struct mmc_host;
|
||||
struct mmc_card;
|
||||
|
||||
int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
|
||||
int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
|
||||
unsigned addr, u8 in, u8* out);
|
||||
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
|
||||
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz);
|
||||
int sdio_reset(struct mmc_host *host);
|
||||
unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz);
|
||||
|
||||
static inline bool mmc_is_io_op(u32 opcode)
|
||||
{
|
||||
|
|
|
@ -235,9 +235,6 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
|||
struct gpio_desc *desc;
|
||||
int ret;
|
||||
|
||||
if (!con_id)
|
||||
con_id = ctx->cd_label;
|
||||
|
||||
desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN);
|
||||
if (IS_ERR(desc))
|
||||
return PTR_ERR(desc);
|
||||
|
@ -289,9 +286,6 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
|||
struct gpio_desc *desc;
|
||||
int ret;
|
||||
|
||||
if (!con_id)
|
||||
con_id = ctx->ro_label;
|
||||
|
||||
desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN);
|
||||
if (IS_ERR(desc))
|
||||
return PTR_ERR(desc);
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#ifndef _MMC_CORE_SLOTGPIO_H
|
||||
#define _MMC_CORE_SLOTGPIO_H
|
||||
|
||||
struct mmc_host;
|
||||
|
||||
int mmc_gpio_alloc(struct mmc_host *host);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -683,6 +683,15 @@ config MMC_DW_ROCKCHIP
|
|||
Synopsys DesignWare Memory Card Interface driver. Select this option
|
||||
for platforms based on RK3066, RK3188 and RK3288 SoC's.
|
||||
|
||||
config MMC_DW_ZX
|
||||
tristate "ZTE specific extensions for Synopsys DW Memory Card Interface"
|
||||
depends on MMC_DW && ARCH_ZX
|
||||
select MMC_DW_PLTFM
|
||||
help
|
||||
This selects support for ZTE SoC specific extensions to the
|
||||
Synopsys DesignWare Memory Card Interface driver. Select this option
|
||||
for platforms based on ZX296718 SoC's.
|
||||
|
||||
config MMC_SH_MMCIF
|
||||
tristate "SuperH Internal MMCIF support"
|
||||
depends on HAS_DMA
|
||||
|
|
|
@ -48,6 +48,7 @@ obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
|
|||
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
|
||||
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
|
||||
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
|
||||
obj-$(CONFIG_MMC_DW_ZX) += dw_mmc-zx.o
|
||||
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
|
||||
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
|
||||
obj-$(CONFIG_MMC_VUB300) += vub300.o
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/platform_data/mmc-davinci.h>
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/dw_mmc.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/dw_mmc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/dw_mmc.h>
|
||||
#include "dw_mmc.h"
|
||||
|
||||
#define PCI_BAR_NO 2
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/dw_mmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/dw_mmc.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
|
241
drivers/mmc/host/dw_mmc-zx.c
Normal file
241
drivers/mmc/host/dw_mmc-zx.c
Normal file
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* ZX Specific Extensions for Synopsys DW Multimedia Card Interface driver
|
||||
*
|
||||
* Copyright (C) 2016, Linaro Ltd.
|
||||
* Copyright (C) 2016, ZTE Corp.
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "dw_mmc.h"
|
||||
#include "dw_mmc-pltfm.h"
|
||||
#include "dw_mmc-zx.h"
|
||||
|
||||
struct dw_mci_zx_priv_data {
|
||||
struct regmap *sysc_base;
|
||||
};
|
||||
|
||||
enum delay_type {
|
||||
DELAY_TYPE_READ, /* read dqs delay */
|
||||
DELAY_TYPE_CLK, /* clk sample delay */
|
||||
};
|
||||
|
||||
static int dw_mci_zx_emmc_set_delay(struct dw_mci *host, unsigned int delay,
|
||||
enum delay_type dflag)
|
||||
{
|
||||
struct dw_mci_zx_priv_data *priv = host->priv;
|
||||
struct regmap *sysc_base = priv->sysc_base;
|
||||
unsigned int clksel;
|
||||
unsigned int loop = 1000;
|
||||
int ret;
|
||||
|
||||
if (!sysc_base)
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0,
|
||||
PARA_HALF_CLK_MODE | PARA_DLL_BYPASS_MODE |
|
||||
PARA_PHASE_DET_SEL_MASK |
|
||||
PARA_DLL_LOCK_NUM_MASK |
|
||||
DLL_REG_SET | PARA_DLL_START_MASK,
|
||||
PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG1, &clksel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (dflag == DELAY_TYPE_CLK) {
|
||||
clksel &= ~CLK_SAMP_DELAY_MASK;
|
||||
clksel |= CLK_SAMP_DELAY(delay);
|
||||
} else {
|
||||
clksel &= ~READ_DQS_DELAY_MASK;
|
||||
clksel |= READ_DQS_DELAY(delay);
|
||||
}
|
||||
|
||||
regmap_write(sysc_base, LB_AON_EMMC_CFG_REG1, clksel);
|
||||
regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0,
|
||||
PARA_DLL_START_MASK | PARA_DLL_LOCK_NUM_MASK |
|
||||
DLL_REG_SET,
|
||||
PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4) |
|
||||
DLL_REG_SET);
|
||||
|
||||
do {
|
||||
ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG2, &clksel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
} while (--loop && !(clksel & ZX_DLL_LOCKED));
|
||||
|
||||
if (!loop) {
|
||||
dev_err(host->dev, "Error: %s dll lock fail\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_zx_emmc_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
|
||||
{
|
||||
struct dw_mci *host = slot->host;
|
||||
struct mmc_host *mmc = slot->mmc;
|
||||
int ret, len = 0, start = 0, end = 0, delay, best = 0;
|
||||
|
||||
for (delay = 1; delay < 128; delay++) {
|
||||
ret = dw_mci_zx_emmc_set_delay(host, delay, DELAY_TYPE_CLK);
|
||||
if (!ret && mmc_send_tuning(mmc, opcode, NULL)) {
|
||||
if (start >= 0) {
|
||||
end = delay - 1;
|
||||
/* check and update longest good range */
|
||||
if ((end - start) > len) {
|
||||
best = (start + end) >> 1;
|
||||
len = end - start;
|
||||
}
|
||||
}
|
||||
start = -1;
|
||||
end = 0;
|
||||
continue;
|
||||
}
|
||||
if (start < 0)
|
||||
start = delay;
|
||||
}
|
||||
|
||||
if (start >= 0) {
|
||||
end = delay - 1;
|
||||
if ((end - start) > len) {
|
||||
best = (start + end) >> 1;
|
||||
len = end - start;
|
||||
}
|
||||
}
|
||||
if (best < 0)
|
||||
return -EIO;
|
||||
|
||||
dev_info(host->dev, "%s best range: start %d end %d\n", __func__,
|
||||
start, end);
|
||||
return dw_mci_zx_emmc_set_delay(host, best, DELAY_TYPE_CLK);
|
||||
}
|
||||
|
||||
static int dw_mci_zx_prepare_hs400_tuning(struct dw_mci *host,
|
||||
struct mmc_ios *ios)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* config phase shift as 90 degree */
|
||||
ret = dw_mci_zx_emmc_set_delay(host, 32, DELAY_TYPE_READ);
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_zx_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
|
||||
{
|
||||
struct dw_mci *host = slot->host;
|
||||
|
||||
if (host->verid == 0x290a) /* only for emmc */
|
||||
return dw_mci_zx_emmc_execute_tuning(slot, opcode);
|
||||
/* TODO: Add 0x210a dedicated tuning for sd/sdio */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_zx_parse_dt(struct dw_mci *host)
|
||||
{
|
||||
struct device_node *np = host->dev->of_node;
|
||||
struct device_node *node;
|
||||
struct dw_mci_zx_priv_data *priv;
|
||||
struct regmap *sysc_base;
|
||||
int ret;
|
||||
|
||||
/* syscon is needed only by emmc */
|
||||
node = of_parse_phandle(np, "zte,aon-syscon", 0);
|
||||
if (node) {
|
||||
sysc_base = syscon_node_to_regmap(node);
|
||||
of_node_put(node);
|
||||
|
||||
if (IS_ERR(sysc_base)) {
|
||||
ret = PTR_ERR(sysc_base);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(host->dev, "Can't get syscon: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
priv->sysc_base = sysc_base;
|
||||
host->priv = priv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long zx_dwmmc_caps[3] = {
|
||||
MMC_CAP_CMD23,
|
||||
MMC_CAP_CMD23,
|
||||
MMC_CAP_CMD23,
|
||||
};
|
||||
|
||||
static const struct dw_mci_drv_data zx_drv_data = {
|
||||
.caps = zx_dwmmc_caps,
|
||||
.execute_tuning = dw_mci_zx_execute_tuning,
|
||||
.prepare_hs400_tuning = dw_mci_zx_prepare_hs400_tuning,
|
||||
.parse_dt = dw_mci_zx_parse_dt,
|
||||
};
|
||||
|
||||
static const struct of_device_id dw_mci_zx_match[] = {
|
||||
{ .compatible = "zte,zx296718-dw-mshc", .data = &zx_drv_data},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_mci_zx_match);
|
||||
|
||||
static int dw_mci_zx_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct dw_mci_drv_data *drv_data;
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_node(dw_mci_zx_match, pdev->dev.of_node);
|
||||
drv_data = match->data;
|
||||
|
||||
return dw_mci_pltfm_register(pdev, drv_data);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dw_mci_zx_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
|
||||
dw_mci_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver dw_mci_zx_pltfm_driver = {
|
||||
.probe = dw_mci_zx_probe,
|
||||
.remove = dw_mci_pltfm_remove,
|
||||
.driver = {
|
||||
.name = "dwmmc_zx",
|
||||
.of_match_table = dw_mci_zx_match,
|
||||
.pm = &dw_mci_zx_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dw_mci_zx_pltfm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ZTE emmc/sd driver");
|
||||
MODULE_LICENSE("GPL v2");
|
31
drivers/mmc/host/dw_mmc-zx.h
Normal file
31
drivers/mmc/host/dw_mmc-zx.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef _DW_MMC_ZX_H_
|
||||
#define _DW_MMC_ZX_H_
|
||||
|
||||
/* ZX296718 SoC specific DLL register offset. */
|
||||
#define LB_AON_EMMC_CFG_REG0 0x1B0
|
||||
#define LB_AON_EMMC_CFG_REG1 0x1B4
|
||||
#define LB_AON_EMMC_CFG_REG2 0x1B8
|
||||
|
||||
/* LB_AON_EMMC_CFG_REG0 register defines */
|
||||
#define PARA_DLL_START(x) ((x) & 0xFF)
|
||||
#define PARA_DLL_START_MASK 0xFF
|
||||
#define DLL_REG_SET BIT(8)
|
||||
#define PARA_DLL_LOCK_NUM(x) (((x) & 7) << 16)
|
||||
#define PARA_DLL_LOCK_NUM_MASK (7 << 16)
|
||||
#define PARA_PHASE_DET_SEL(x) (((x) & 7) << 20)
|
||||
#define PARA_PHASE_DET_SEL_MASK (7 << 20)
|
||||
#define PARA_DLL_BYPASS_MODE BIT(23)
|
||||
#define PARA_HALF_CLK_MODE BIT(24)
|
||||
|
||||
/* LB_AON_EMMC_CFG_REG1 register defines */
|
||||
#define READ_DQS_DELAY(x) ((x) & 0x7F)
|
||||
#define READ_DQS_DELAY_MASK (0x7F)
|
||||
#define READ_DQS_BYPASS_MODE BIT(7)
|
||||
#define CLK_SAMP_DELAY(x) (((x) & 0x7F) << 8)
|
||||
#define CLK_SAMP_DELAY_MASK (0x7F << 8)
|
||||
#define CLK_SAMP_BYPASS_MODE BIT(15)
|
||||
|
||||
/* LB_AON_EMMC_CFG_REG2 register defines */
|
||||
#define ZX_DLL_LOCKED BIT(2)
|
||||
|
||||
#endif /* _DW_MMC_ZX_H_ */
|
|
@ -32,7 +32,6 @@
|
|||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/sd.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
#include <linux/mmc/dw_mmc.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
|
@ -1113,11 +1112,15 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
|
|||
mci_writel(host, CTRL, temp);
|
||||
|
||||
/*
|
||||
* Use the initial fifoth_val for PIO mode.
|
||||
* Use the initial fifoth_val for PIO mode. If wm_algined
|
||||
* is set, we set watermark same as data size.
|
||||
* If next issued data may be transfered by DMA mode,
|
||||
* prev_blksz should be invalidated.
|
||||
*/
|
||||
mci_writel(host, FIFOTH, host->fifoth_val);
|
||||
if (host->wm_aligned)
|
||||
dw_mci_adjust_fifoth(host, data);
|
||||
else
|
||||
mci_writel(host, FIFOTH, host->fifoth_val);
|
||||
host->prev_blksz = 0;
|
||||
} else {
|
||||
/*
|
||||
|
@ -1179,11 +1182,13 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
|
|||
if ((clock != slot->__clk_old &&
|
||||
!test_bit(DW_MMC_CARD_NEEDS_POLL, &slot->flags)) ||
|
||||
force_clkinit) {
|
||||
dev_info(&slot->mmc->class_dev,
|
||||
"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
|
||||
slot->id, host->bus_hz, clock,
|
||||
div ? ((host->bus_hz / div) >> 1) :
|
||||
host->bus_hz, div);
|
||||
/* Silent the verbose log if calling from PM context */
|
||||
if (!force_clkinit)
|
||||
dev_info(&slot->mmc->class_dev,
|
||||
"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
|
||||
slot->id, host->bus_hz, clock,
|
||||
div ? ((host->bus_hz / div) >> 1) :
|
||||
host->bus_hz, div);
|
||||
|
||||
/*
|
||||
* If card is polling, display the message only
|
||||
|
@ -2977,6 +2982,11 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
|||
|
||||
of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);
|
||||
|
||||
of_property_read_u32(np, "data-addr", &host->data_addr_override);
|
||||
|
||||
if (of_get_property(np, "fifo-watermark-aligned", NULL))
|
||||
host->wm_aligned = true;
|
||||
|
||||
if (!of_property_read_u32(np, "clock-frequency", &clock_frequency))
|
||||
pdata->bus_hz = clock_frequency;
|
||||
|
||||
|
@ -3180,7 +3190,9 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
host->verid = SDMMC_GET_VERID(mci_readl(host, VERID));
|
||||
dev_info(host->dev, "Version ID is %04x\n", host->verid);
|
||||
|
||||
if (host->verid < DW_MMC_240A)
|
||||
if (host->data_addr_override)
|
||||
host->fifo_reg = host->regs + host->data_addr_override;
|
||||
else if (host->verid < DW_MMC_240A)
|
||||
host->fifo_reg = host->regs + DATA_OFFSET;
|
||||
else
|
||||
host->fifo_reg = host->regs + DATA_240A_OFFSET;
|
||||
|
|
|
@ -14,6 +14,269 @@
|
|||
#ifndef _DW_MMC_H_
|
||||
#define _DW_MMC_H_
|
||||
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/mmc/core.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#define MAX_MCI_SLOTS 2
|
||||
|
||||
enum dw_mci_state {
|
||||
STATE_IDLE = 0,
|
||||
STATE_SENDING_CMD,
|
||||
STATE_SENDING_DATA,
|
||||
STATE_DATA_BUSY,
|
||||
STATE_SENDING_STOP,
|
||||
STATE_DATA_ERROR,
|
||||
STATE_SENDING_CMD11,
|
||||
STATE_WAITING_CMD11_DONE,
|
||||
};
|
||||
|
||||
enum {
|
||||
EVENT_CMD_COMPLETE = 0,
|
||||
EVENT_XFER_COMPLETE,
|
||||
EVENT_DATA_COMPLETE,
|
||||
EVENT_DATA_ERROR,
|
||||
};
|
||||
|
||||
enum dw_mci_cookie {
|
||||
COOKIE_UNMAPPED,
|
||||
COOKIE_PRE_MAPPED, /* mapped by pre_req() of dwmmc */
|
||||
COOKIE_MAPPED, /* mapped by prepare_data() of dwmmc */
|
||||
};
|
||||
|
||||
struct mmc_data;
|
||||
|
||||
enum {
|
||||
TRANS_MODE_PIO = 0,
|
||||
TRANS_MODE_IDMAC,
|
||||
TRANS_MODE_EDMAC
|
||||
};
|
||||
|
||||
struct dw_mci_dma_slave {
|
||||
struct dma_chan *ch;
|
||||
enum dma_transfer_direction direction;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dw_mci - MMC controller state shared between all slots
|
||||
* @lock: Spinlock protecting the queue and associated data.
|
||||
* @irq_lock: Spinlock protecting the INTMASK setting.
|
||||
* @regs: Pointer to MMIO registers.
|
||||
* @fifo_reg: Pointer to MMIO registers for data FIFO
|
||||
* @sg: Scatterlist entry currently being processed by PIO code, if any.
|
||||
* @sg_miter: PIO mapping scatterlist iterator.
|
||||
* @cur_slot: The slot which is currently using the controller.
|
||||
* @mrq: The request currently being processed on @cur_slot,
|
||||
* or NULL if the controller is idle.
|
||||
* @cmd: The command currently being sent to the card, or NULL.
|
||||
* @data: The data currently being transferred, or NULL if no data
|
||||
* transfer is in progress.
|
||||
* @stop_abort: The command currently prepared for stoping transfer.
|
||||
* @prev_blksz: The former transfer blksz record.
|
||||
* @timing: Record of current ios timing.
|
||||
* @use_dma: Whether DMA channel is initialized or not.
|
||||
* @using_dma: Whether DMA is in use for the current transfer.
|
||||
* @dma_64bit_address: Whether DMA supports 64-bit address mode or not.
|
||||
* @sg_dma: Bus address of DMA buffer.
|
||||
* @sg_cpu: Virtual address of DMA buffer.
|
||||
* @dma_ops: Pointer to platform-specific DMA callbacks.
|
||||
* @cmd_status: Snapshot of SR taken upon completion of the current
|
||||
* @ring_size: Buffer size for idma descriptors.
|
||||
* command. Only valid when EVENT_CMD_COMPLETE is pending.
|
||||
* @dms: structure of slave-dma private data.
|
||||
* @phy_regs: physical address of controller's register map
|
||||
* @data_status: Snapshot of SR taken upon completion of the current
|
||||
* data transfer. Only valid when EVENT_DATA_COMPLETE or
|
||||
* EVENT_DATA_ERROR is pending.
|
||||
* @stop_cmdr: Value to be loaded into CMDR when the stop command is
|
||||
* to be sent.
|
||||
* @dir_status: Direction of current transfer.
|
||||
* @tasklet: Tasklet running the request state machine.
|
||||
* @pending_events: Bitmask of events flagged by the interrupt handler
|
||||
* to be processed by the tasklet.
|
||||
* @completed_events: Bitmask of events which the state machine has
|
||||
* processed.
|
||||
* @state: Tasklet state.
|
||||
* @queue: List of slots waiting for access to the controller.
|
||||
* @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
|
||||
* rate and timeout calculations.
|
||||
* @current_speed: Configured rate of the controller.
|
||||
* @num_slots: Number of slots available.
|
||||
* @fifoth_val: The value of FIFOTH register.
|
||||
* @verid: Denote Version ID.
|
||||
* @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_addr_override: override fifo reg offset with this value.
|
||||
* @wm_aligned: force fifo watermark equal with data length in PIO mode.
|
||||
* Set as true if alignment is needed.
|
||||
* @data_shift: log2 of FIFO item size.
|
||||
* @part_buf_start: Start index in part_buf.
|
||||
* @part_buf_count: Bytes of partial data in part_buf.
|
||||
* @part_buf: Simple buffer for partial fifo reads/writes.
|
||||
* @push_data: Pointer to FIFO push function.
|
||||
* @pull_data: Pointer to FIFO pull function.
|
||||
* @vqmmc_enabled: Status of vqmmc, should be true or false.
|
||||
* @irq_flags: The flags to be passed to request_irq.
|
||||
* @irq: The irq value to be passed to request_irq.
|
||||
* @sdio_id0: Number of slot0 in the SDIO interrupt registers.
|
||||
* @cmd11_timer: Timer for SD3.0 voltage switch over scheme.
|
||||
* @dto_timer: Timer for broken data transfer over scheme.
|
||||
*
|
||||
* Locking
|
||||
* =======
|
||||
*
|
||||
* @lock is a softirq-safe spinlock protecting @queue as well as
|
||||
* @cur_slot, @mrq and @state. These must always be updated
|
||||
* at the same time while holding @lock.
|
||||
*
|
||||
* @irq_lock is an irq-safe spinlock protecting the INTMASK register
|
||||
* to allow the interrupt handler to modify it directly. Held for only long
|
||||
* enough to read-modify-write INTMASK and no other locks are grabbed when
|
||||
* holding this one.
|
||||
*
|
||||
* The @mrq field of struct dw_mci_slot is also protected by @lock,
|
||||
* and must always be written at the same time as the slot is added to
|
||||
* @queue.
|
||||
*
|
||||
* @pending_events and @completed_events are accessed using atomic bit
|
||||
* operations, so they don't need any locking.
|
||||
*
|
||||
* None of the fields touched by the interrupt handler need any
|
||||
* locking. However, ordering is important: Before EVENT_DATA_ERROR or
|
||||
* EVENT_DATA_COMPLETE is set in @pending_events, all data-related
|
||||
* interrupts must be disabled and @data_status updated with a
|
||||
* snapshot of SR. Similarly, before EVENT_CMD_COMPLETE is set, the
|
||||
* CMDRDY interrupt must be disabled and @cmd_status updated with a
|
||||
* snapshot of SR, and before EVENT_XFER_COMPLETE can be set, the
|
||||
* bytes_xfered field of @data must be written. This is ensured by
|
||||
* using barriers.
|
||||
*/
|
||||
struct dw_mci {
|
||||
spinlock_t lock;
|
||||
spinlock_t irq_lock;
|
||||
void __iomem *regs;
|
||||
void __iomem *fifo_reg;
|
||||
u32 data_addr_override;
|
||||
bool wm_aligned;
|
||||
|
||||
struct scatterlist *sg;
|
||||
struct sg_mapping_iter sg_miter;
|
||||
|
||||
struct dw_mci_slot *cur_slot;
|
||||
struct mmc_request *mrq;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
struct mmc_command stop_abort;
|
||||
unsigned int prev_blksz;
|
||||
unsigned char timing;
|
||||
|
||||
/* DMA interface members*/
|
||||
int use_dma;
|
||||
int using_dma;
|
||||
int dma_64bit_address;
|
||||
|
||||
dma_addr_t sg_dma;
|
||||
void *sg_cpu;
|
||||
const struct dw_mci_dma_ops *dma_ops;
|
||||
/* For idmac */
|
||||
unsigned int ring_size;
|
||||
|
||||
/* For edmac */
|
||||
struct dw_mci_dma_slave *dms;
|
||||
/* Registers's physical base address */
|
||||
resource_size_t phy_regs;
|
||||
|
||||
u32 cmd_status;
|
||||
u32 data_status;
|
||||
u32 stop_cmdr;
|
||||
u32 dir_status;
|
||||
struct tasklet_struct tasklet;
|
||||
unsigned long pending_events;
|
||||
unsigned long completed_events;
|
||||
enum dw_mci_state state;
|
||||
struct list_head queue;
|
||||
|
||||
u32 bus_hz;
|
||||
u32 current_speed;
|
||||
u32 num_slots;
|
||||
u32 fifoth_val;
|
||||
u16 verid;
|
||||
struct device *dev;
|
||||
struct dw_mci_board *pdata;
|
||||
const 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 */
|
||||
int fifo_depth;
|
||||
int data_shift;
|
||||
u8 part_buf_start;
|
||||
u8 part_buf_count;
|
||||
union {
|
||||
u16 part_buf16;
|
||||
u32 part_buf32;
|
||||
u64 part_buf;
|
||||
};
|
||||
void (*push_data)(struct dw_mci *host, void *buf, int cnt);
|
||||
void (*pull_data)(struct dw_mci *host, void *buf, int cnt);
|
||||
|
||||
bool vqmmc_enabled;
|
||||
unsigned long irq_flags; /* IRQ flags */
|
||||
int irq;
|
||||
|
||||
int sdio_id0;
|
||||
|
||||
struct timer_list cmd11_timer;
|
||||
struct timer_list dto_timer;
|
||||
};
|
||||
|
||||
/* DMA ops for Internal/External DMAC interface */
|
||||
struct dw_mci_dma_ops {
|
||||
/* DMA Ops */
|
||||
int (*init)(struct dw_mci *host);
|
||||
int (*start)(struct dw_mci *host, unsigned int sg_len);
|
||||
void (*complete)(void *host);
|
||||
void (*stop)(struct dw_mci *host);
|
||||
void (*cleanup)(struct dw_mci *host);
|
||||
void (*exit)(struct dw_mci *host);
|
||||
};
|
||||
|
||||
struct dma_pdata;
|
||||
|
||||
/* Board platform data */
|
||||
struct dw_mci_board {
|
||||
u32 num_slots;
|
||||
|
||||
unsigned int bus_hz; /* Clock speed at the cclk_in pad */
|
||||
|
||||
u32 caps; /* Capabilities */
|
||||
u32 caps2; /* More capabilities */
|
||||
u32 pm_caps; /* PM capabilities */
|
||||
/*
|
||||
* Override fifo depth. If 0, autodetect it from the FIFOTH register,
|
||||
* but note that this may not be reliable after a bootloader has used
|
||||
* it.
|
||||
*/
|
||||
unsigned int fifo_depth;
|
||||
|
||||
/* delay in mS before detecting cards after interrupt */
|
||||
u32 detect_delay_ms;
|
||||
|
||||
struct reset_control *rstc;
|
||||
struct dw_mci_dma_ops *dma_ops;
|
||||
struct dma_pdata *data;
|
||||
};
|
||||
|
||||
#define DW_MMC_240A 0x240a
|
||||
#define DW_MMC_280A 0x280a
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#define DRIVER_NAME "meson-gx-mmc"
|
||||
|
||||
|
@ -82,6 +83,7 @@
|
|||
#define CFG_RC_CC_MASK 0xf
|
||||
#define CFG_STOP_CLOCK BIT(22)
|
||||
#define CFG_CLK_ALWAYS_ON BIT(18)
|
||||
#define CFG_CHK_DS BIT(20)
|
||||
#define CFG_AUTO_CLK BIT(23)
|
||||
|
||||
#define SD_EMMC_STATUS 0x48
|
||||
|
@ -131,7 +133,7 @@ struct meson_host {
|
|||
struct clk_mux mux;
|
||||
struct clk *mux_clk;
|
||||
struct clk *mux_parent[MUX_CLK_NUM_PARENTS];
|
||||
unsigned long mux_parent_rate[MUX_CLK_NUM_PARENTS];
|
||||
unsigned long current_clock;
|
||||
|
||||
struct clk_divider cfg_div;
|
||||
struct clk *cfg_div_clk;
|
||||
|
@ -178,7 +180,7 @@ struct sd_emmc_desc {
|
|||
static int meson_mmc_clk_set(struct meson_host *host, unsigned long clk_rate)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
u32 cfg;
|
||||
|
||||
if (clk_rate) {
|
||||
|
@ -188,7 +190,7 @@ static int meson_mmc_clk_set(struct meson_host *host, unsigned long clk_rate)
|
|||
clk_rate = mmc->f_min;
|
||||
}
|
||||
|
||||
if (clk_rate == mmc->actual_clock)
|
||||
if (clk_rate == host->current_clock)
|
||||
return 0;
|
||||
|
||||
/* stop clock */
|
||||
|
@ -201,29 +203,34 @@ static int meson_mmc_clk_set(struct meson_host *host, unsigned long clk_rate)
|
|||
dev_dbg(host->dev, "change clock rate %u -> %lu\n",
|
||||
mmc->actual_clock, clk_rate);
|
||||
|
||||
if (clk_rate == 0) {
|
||||
if (!clk_rate) {
|
||||
mmc->actual_clock = 0;
|
||||
host->current_clock = 0;
|
||||
/* return with clock being stopped */
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(host->cfg_div_clk, clk_rate);
|
||||
if (ret)
|
||||
dev_warn(host->dev, "Unable to set cfg_div_clk to %lu. ret=%d\n",
|
||||
clk_rate, ret);
|
||||
else if (clk_rate && clk_rate != clk_get_rate(host->cfg_div_clk))
|
||||
dev_warn(host->dev, "divider requested rate %lu != actual rate %lu: ret=%d\n",
|
||||
clk_rate, clk_get_rate(host->cfg_div_clk), ret);
|
||||
else
|
||||
mmc->actual_clock = clk_rate;
|
||||
|
||||
/* (re)start clock, if non-zero */
|
||||
if (!ret && clk_rate) {
|
||||
cfg = readl(host->regs + SD_EMMC_CFG);
|
||||
cfg &= ~CFG_STOP_CLOCK;
|
||||
writel(cfg, host->regs + SD_EMMC_CFG);
|
||||
if (ret) {
|
||||
dev_err(host->dev, "Unable to set cfg_div_clk to %lu. ret=%d\n",
|
||||
clk_rate, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
mmc->actual_clock = clk_get_rate(host->cfg_div_clk);
|
||||
host->current_clock = clk_rate;
|
||||
|
||||
if (clk_rate != mmc->actual_clock)
|
||||
dev_dbg(host->dev,
|
||||
"divider requested rate %lu != actual rate %u\n",
|
||||
clk_rate, mmc->actual_clock);
|
||||
|
||||
/* (re)start clock */
|
||||
cfg = readl(host->regs + SD_EMMC_CFG);
|
||||
cfg &= ~CFG_STOP_CLOCK;
|
||||
writel(cfg, host->regs + SD_EMMC_CFG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -239,7 +246,6 @@ static int meson_mmc_clk_init(struct meson_host *host)
|
|||
const char *mux_parent_names[MUX_CLK_NUM_PARENTS];
|
||||
unsigned int mux_parent_count = 0;
|
||||
const char *clk_div_parents[1];
|
||||
unsigned int f_min = UINT_MAX;
|
||||
u32 clk_reg, cfg;
|
||||
|
||||
/* get the mux parents */
|
||||
|
@ -256,20 +262,10 @@ static int meson_mmc_clk_init(struct meson_host *host)
|
|||
return ret;
|
||||
}
|
||||
|
||||
host->mux_parent_rate[i] = clk_get_rate(host->mux_parent[i]);
|
||||
mux_parent_names[i] = __clk_get_name(host->mux_parent[i]);
|
||||
mux_parent_count++;
|
||||
if (host->mux_parent_rate[i] < f_min)
|
||||
f_min = host->mux_parent_rate[i];
|
||||
}
|
||||
|
||||
/* cacluate f_min based on input clocks, and max divider value */
|
||||
if (f_min != UINT_MAX)
|
||||
f_min = DIV_ROUND_UP(CLK_SRC_XTAL_RATE, CLK_DIV_MAX);
|
||||
else
|
||||
f_min = 4000000; /* default min: 400 MHz */
|
||||
host->mmc->f_min = f_min;
|
||||
|
||||
/* create the mux */
|
||||
snprintf(clk_name, sizeof(clk_name), "%s#mux", dev_name(host->dev));
|
||||
init.name = clk_name;
|
||||
|
@ -324,9 +320,13 @@ static int meson_mmc_clk_init(struct meson_host *host)
|
|||
writel(cfg, host->regs + SD_EMMC_CFG);
|
||||
|
||||
ret = clk_prepare_enable(host->cfg_div_clk);
|
||||
if (!ret)
|
||||
ret = meson_mmc_clk_set(host, f_min);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Get the nearest minimum clock to 400KHz */
|
||||
host->mmc->f_min = clk_round_rate(host->cfg_div_clk, 400000);
|
||||
|
||||
ret = meson_mmc_clk_set(host, host->mmc->f_min);
|
||||
if (!ret)
|
||||
clk_disable_unprepare(host->cfg_div_clk);
|
||||
|
||||
|
@ -378,7 +378,6 @@ static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
meson_mmc_clk_set(host, ios->clock);
|
||||
|
||||
/* Bus width */
|
||||
val = readl(host->regs + SD_EMMC_CFG);
|
||||
switch (ios->bus_width) {
|
||||
case MMC_BUS_WIDTH_1:
|
||||
bus_width = CFG_BUS_WIDTH_1;
|
||||
|
@ -393,7 +392,6 @@ static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
dev_err(host->dev, "Invalid ios->bus_width: %u. Setting to 4.\n",
|
||||
ios->bus_width);
|
||||
bus_width = CFG_BUS_WIDTH_4;
|
||||
return;
|
||||
}
|
||||
|
||||
val = readl(host->regs + SD_EMMC_CFG);
|
||||
|
@ -411,6 +409,16 @@ static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
val &= ~(CFG_RC_CC_MASK << CFG_RC_CC_SHIFT);
|
||||
val |= ilog2(SD_EMMC_CFG_CMD_GAP) << CFG_RC_CC_SHIFT;
|
||||
|
||||
val &= ~CFG_DDR;
|
||||
if (ios->timing == MMC_TIMING_UHS_DDR50 ||
|
||||
ios->timing == MMC_TIMING_MMC_DDR52 ||
|
||||
ios->timing == MMC_TIMING_MMC_HS400)
|
||||
val |= CFG_DDR;
|
||||
|
||||
val &= ~CFG_CHK_DS;
|
||||
if (ios->timing == MMC_TIMING_MMC_HS400)
|
||||
val |= CFG_CHK_DS;
|
||||
|
||||
writel(val, host->regs + SD_EMMC_CFG);
|
||||
|
||||
if (val != orig)
|
||||
|
@ -480,9 +488,9 @@ static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd)
|
|||
blk_len = cfg & (CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT);
|
||||
blk_len >>= CFG_BLK_LEN_SHIFT;
|
||||
if (blk_len != ilog2(cmd->data->blksz)) {
|
||||
dev_warn(host->dev, "%s: update blk_len %d -> %d\n",
|
||||
dev_dbg(host->dev, "%s: update blk_len %d -> %d\n",
|
||||
__func__, blk_len,
|
||||
ilog2(cmd->data->blksz));
|
||||
ilog2(cmd->data->blksz));
|
||||
blk_len = ilog2(cmd->data->blksz);
|
||||
cfg &= ~(CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT);
|
||||
cfg |= blk_len << CFG_BLK_LEN_SHIFT;
|
||||
|
@ -545,11 +553,6 @@ static void meson_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
/* Stop execution */
|
||||
writel(0, host->regs + SD_EMMC_START);
|
||||
|
||||
/* clear, ack, enable all interrupts */
|
||||
writel(0, host->regs + SD_EMMC_IRQ_EN);
|
||||
writel(IRQ_EN_MASK, host->regs + SD_EMMC_STATUS);
|
||||
writel(IRQ_EN_MASK, host->regs + SD_EMMC_IRQ_EN);
|
||||
|
||||
host->mrq = mrq;
|
||||
|
||||
if (mrq->sbc)
|
||||
|
@ -669,7 +672,6 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
|
|||
struct mmc_command *cmd = host->cmd;
|
||||
struct mmc_data *data;
|
||||
unsigned int xfer_bytes;
|
||||
int ret = IRQ_HANDLED;
|
||||
|
||||
if (WARN_ON(!mrq))
|
||||
return IRQ_NONE;
|
||||
|
@ -678,14 +680,12 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
|
|||
return IRQ_NONE;
|
||||
|
||||
data = cmd->data;
|
||||
if (data) {
|
||||
if (data && data->flags & MMC_DATA_READ) {
|
||||
xfer_bytes = data->blksz * data->blocks;
|
||||
if (data->flags & MMC_DATA_READ) {
|
||||
WARN_ON(xfer_bytes > host->bounce_buf_size);
|
||||
sg_copy_from_buffer(data->sg, data->sg_len,
|
||||
host->bounce_buf, xfer_bytes);
|
||||
data->bytes_xfered = xfer_bytes;
|
||||
}
|
||||
WARN_ON(xfer_bytes > host->bounce_buf_size);
|
||||
sg_copy_from_buffer(data->sg, data->sg_len,
|
||||
host->bounce_buf, xfer_bytes);
|
||||
data->bytes_xfered = xfer_bytes;
|
||||
}
|
||||
|
||||
meson_mmc_read_resp(host->mmc, cmd);
|
||||
|
@ -694,7 +694,7 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
|
|||
else
|
||||
meson_mmc_start_cmd(host->mmc, data->stop);
|
||||
|
||||
return ret;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -742,7 +742,8 @@ static int meson_mmc_probe(struct platform_device *pdev)
|
|||
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret) {
|
||||
dev_warn(&pdev->dev, "error parsing DT: %d\n", ret);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_warn(&pdev->dev, "error parsing DT: %d\n", ret);
|
||||
goto free_host;
|
||||
}
|
||||
|
||||
|
@ -780,6 +781,7 @@ static int meson_mmc_probe(struct platform_device *pdev)
|
|||
/* clear, ack, enable all interrupts */
|
||||
writel(0, host->regs + SD_EMMC_IRQ_EN);
|
||||
writel(IRQ_EN_MASK, host->regs + SD_EMMC_STATUS);
|
||||
writel(IRQ_EN_MASK, host->regs + SD_EMMC_IRQ_EN);
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, host->irq,
|
||||
meson_mmc_irq, meson_mmc_irq_thread,
|
||||
|
@ -787,8 +789,11 @@ static int meson_mmc_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto free_host;
|
||||
|
||||
mmc->max_blk_count = CMD_CFG_LENGTH_MASK;
|
||||
mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size;
|
||||
|
||||
/* data bounce buffer */
|
||||
host->bounce_buf_size = SZ_512K;
|
||||
host->bounce_buf_size = mmc->max_req_size;
|
||||
host->bounce_buf =
|
||||
dma_alloc_coherent(host->dev, host->bounce_buf_size,
|
||||
&host->bounce_dma_addr, GFP_KERNEL);
|
||||
|
@ -814,12 +819,11 @@ static int meson_mmc_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct meson_host *host = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (WARN_ON(!host))
|
||||
return 0;
|
||||
/* disable interrupts */
|
||||
writel(0, host->regs + SD_EMMC_IRQ_EN);
|
||||
|
||||
if (host->bounce_buf)
|
||||
dma_free_coherent(host->dev, host->bounce_buf_size,
|
||||
host->bounce_buf, host->bounce_dma_addr);
|
||||
dma_free_coherent(host->dev, host->bounce_buf_size,
|
||||
host->bounce_buf, host->bounce_dma_addr);
|
||||
|
||||
clk_disable_unprepare(host->cfg_div_clk);
|
||||
clk_disable_unprepare(host->core_clk);
|
||||
|
|
|
@ -507,6 +507,7 @@ static void mmci_dma_data_error(struct mmci_host *host)
|
|||
{
|
||||
dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n");
|
||||
dmaengine_terminate_all(host->dma_current);
|
||||
host->dma_in_progress = false;
|
||||
host->dma_current = NULL;
|
||||
host->dma_desc_current = NULL;
|
||||
host->data->host_cookie = 0;
|
||||
|
@ -565,6 +566,7 @@ static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data)
|
|||
mmci_dma_release(host);
|
||||
}
|
||||
|
||||
host->dma_in_progress = false;
|
||||
host->dma_current = NULL;
|
||||
host->dma_desc_current = NULL;
|
||||
}
|
||||
|
@ -665,6 +667,7 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
|
|||
dev_vdbg(mmc_dev(host->mmc),
|
||||
"Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n",
|
||||
data->sg_len, data->blksz, data->blocks, data->flags);
|
||||
host->dma_in_progress = true;
|
||||
dmaengine_submit(host->dma_desc_current);
|
||||
dma_async_issue_pending(host->dma_current);
|
||||
|
||||
|
@ -740,8 +743,10 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq,
|
|||
if (host->dma_desc_current == next->dma_desc)
|
||||
host->dma_desc_current = NULL;
|
||||
|
||||
if (host->dma_current == next->dma_chan)
|
||||
if (host->dma_current == next->dma_chan) {
|
||||
host->dma_in_progress = false;
|
||||
host->dma_current = NULL;
|
||||
}
|
||||
|
||||
next->dma_desc = NULL;
|
||||
next->dma_chan = NULL;
|
||||
|
|
|
@ -245,8 +245,9 @@ struct mmci_host {
|
|||
struct dma_chan *dma_tx_channel;
|
||||
struct dma_async_tx_descriptor *dma_desc_current;
|
||||
struct mmci_host_next next_data;
|
||||
bool dma_in_progress;
|
||||
|
||||
#define dma_inprogress(host) ((host)->dma_current)
|
||||
#define dma_inprogress(host) ((host)->dma_in_progress)
|
||||
#else
|
||||
#define dma_inprogress(host) (0)
|
||||
#endif
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/core.h>
|
||||
|
@ -1074,11 +1075,8 @@ static int msdc_card_busy(struct mmc_host *mmc)
|
|||
struct msdc_host *host = mmc_priv(mmc);
|
||||
u32 status = readl(host->base + MSDC_PS);
|
||||
|
||||
/* check if any pin between dat[0:3] is low */
|
||||
if (((status >> 16) & 0xf) != 0xf)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
/* only check if data0 is low */
|
||||
return !(status & BIT(16));
|
||||
}
|
||||
|
||||
static void msdc_request_timeout(struct work_struct *work)
|
||||
|
|
|
@ -153,7 +153,11 @@ static void mxs_mmc_request_done(struct mxs_mmc_host *host)
|
|||
}
|
||||
}
|
||||
|
||||
if (data) {
|
||||
if (cmd == mrq->sbc) {
|
||||
/* Finished CMD23, now send actual command. */
|
||||
mxs_mmc_start_cmd(host, mrq->cmd);
|
||||
return;
|
||||
} else if (data) {
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
||||
data->sg_len, ssp->dma_dir);
|
||||
/*
|
||||
|
@ -166,7 +170,7 @@ static void mxs_mmc_request_done(struct mxs_mmc_host *host)
|
|||
data->bytes_xfered = 0;
|
||||
|
||||
host->data = NULL;
|
||||
if (mrq->stop) {
|
||||
if (data->stop && (data->error || !mrq->sbc)) {
|
||||
mxs_mmc_start_cmd(host, mrq->stop);
|
||||
return;
|
||||
}
|
||||
|
@ -495,7 +499,11 @@ static void mxs_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
|
||||
WARN_ON(host->mrq != NULL);
|
||||
host->mrq = mrq;
|
||||
mxs_mmc_start_cmd(host, mrq->cmd);
|
||||
|
||||
if (mrq->sbc)
|
||||
mxs_mmc_start_cmd(host, mrq->sbc);
|
||||
else
|
||||
mxs_mmc_start_cmd(host, mrq->cmd);
|
||||
}
|
||||
|
||||
static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
|
@ -642,7 +650,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||
/* set mmc core parameters */
|
||||
mmc->ops = &mxs_mmc_ops;
|
||||
mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
|
||||
MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL;
|
||||
MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23;
|
||||
|
||||
host->broken_cd = of_property_read_bool(np, "broken-cd");
|
||||
|
||||
|
|
|
@ -893,7 +893,7 @@ static void mmc_omap_cover_handler(unsigned long param)
|
|||
* If no card is inserted, we postpone polling until
|
||||
* the cover has been closed.
|
||||
*/
|
||||
if (slot->mmc->card == NULL || !mmc_card_present(slot->mmc->card))
|
||||
if (slot->mmc->card == NULL)
|
||||
return;
|
||||
|
||||
mod_timer(&slot->cover_timer,
|
||||
|
|
|
@ -1162,7 +1162,7 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
|
|||
if (status & ERR_EN) {
|
||||
omap_hsmmc_dbg_report_irq(host, status);
|
||||
|
||||
if (status & (CTO_EN | CCRC_EN))
|
||||
if (status & (CTO_EN | CCRC_EN | CEB_EN))
|
||||
end_cmd = 1;
|
||||
if (host->data || host->response_busy) {
|
||||
end_trans = !end_cmd;
|
||||
|
@ -1469,10 +1469,11 @@ static int omap_hsmmc_setup_dma_transfer(struct omap_hsmmc_host *host,
|
|||
}
|
||||
|
||||
static void set_data_timeout(struct omap_hsmmc_host *host,
|
||||
unsigned int timeout_ns,
|
||||
unsigned long long timeout_ns,
|
||||
unsigned int timeout_clks)
|
||||
{
|
||||
unsigned int timeout, cycle_ns;
|
||||
unsigned long long timeout = timeout_ns;
|
||||
unsigned int cycle_ns;
|
||||
uint32_t reg, clkd, dto = 0;
|
||||
|
||||
reg = OMAP_HSMMC_READ(host->base, SYSCTL);
|
||||
|
@ -1481,7 +1482,7 @@ static void set_data_timeout(struct omap_hsmmc_host *host,
|
|||
clkd = 1;
|
||||
|
||||
cycle_ns = 1000000000 / (host->clk_rate / clkd);
|
||||
timeout = timeout_ns / cycle_ns;
|
||||
do_div(timeout, cycle_ns);
|
||||
timeout += timeout_clks;
|
||||
if (timeout) {
|
||||
while ((timeout & 0x80000000) == 0) {
|
||||
|
@ -1527,16 +1528,24 @@ static int
|
|||
omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
|
||||
{
|
||||
int ret;
|
||||
unsigned long long timeout;
|
||||
|
||||
host->data = req->data;
|
||||
|
||||
if (req->data == NULL) {
|
||||
OMAP_HSMMC_WRITE(host->base, BLK, 0);
|
||||
/*
|
||||
* Set an arbitrary 100ms data timeout for commands with
|
||||
* busy signal.
|
||||
*/
|
||||
if (req->cmd->flags & MMC_RSP_BUSY)
|
||||
set_data_timeout(host, 100000000U, 0);
|
||||
if (req->cmd->flags & MMC_RSP_BUSY) {
|
||||
timeout = req->cmd->busy_timeout * NSEC_PER_MSEC;
|
||||
|
||||
/*
|
||||
* Set an arbitrary 100ms data timeout for commands with
|
||||
* busy signal and no indication of busy_timeout.
|
||||
*/
|
||||
if (!timeout)
|
||||
timeout = 100000000U;
|
||||
|
||||
set_data_timeout(host, timeout, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -707,7 +707,7 @@ static int sd_tuning_rx_cmd(struct realtek_pci_sdmmc *host,
|
|||
u8 opcode, u8 sample_point)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
err = sd_change_phase(host, sample_point, true);
|
||||
if (err < 0)
|
||||
|
|
|
@ -682,7 +682,7 @@ static int sd_tuning_rx_cmd(struct rtsx_usb_sdmmc *host,
|
|||
u8 opcode, u8 sample_point)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command cmd = {};
|
||||
|
||||
err = sd_change_phase(host, sample_point, 0);
|
||||
if (err)
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
|
|
|
@ -467,7 +467,10 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
|
|||
if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) {
|
||||
bool v = sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL);
|
||||
|
||||
if (mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0, NULL)) {
|
||||
err = mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0, NULL);
|
||||
if (err) {
|
||||
if (err == -EPROBE_DEFER)
|
||||
goto err_free;
|
||||
dev_warn(dev, "failed to setup card detect gpio\n");
|
||||
c->use_runtime_pm = false;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
|
@ -25,7 +26,7 @@
|
|||
#define SDHCI_CDNS_HRS04_ACK BIT(26)
|
||||
#define SDHCI_CDNS_HRS04_RD BIT(25)
|
||||
#define SDHCI_CDNS_HRS04_WR BIT(24)
|
||||
#define SDHCI_CDNS_HRS04_RDATA_SHIFT 12
|
||||
#define SDHCI_CDNS_HRS04_RDATA_SHIFT 16
|
||||
#define SDHCI_CDNS_HRS04_WDATA_SHIFT 8
|
||||
#define SDHCI_CDNS_HRS04_ADDR_SHIFT 0
|
||||
|
||||
|
|
|
@ -24,30 +24,36 @@
|
|||
SDHCI_QUIRK_PIO_NEEDS_DELAY | \
|
||||
SDHCI_QUIRK_NO_HISPD_BIT)
|
||||
|
||||
#define ESDHC_PROCTL 0x28
|
||||
|
||||
#define ESDHC_SYSTEM_CONTROL 0x2c
|
||||
#define ESDHC_CLOCK_MASK 0x0000fff0
|
||||
#define ESDHC_PREDIV_SHIFT 8
|
||||
#define ESDHC_DIVIDER_SHIFT 4
|
||||
#define ESDHC_CLOCK_PEREN 0x00000004
|
||||
#define ESDHC_CLOCK_HCKEN 0x00000002
|
||||
#define ESDHC_CLOCK_IPGEN 0x00000001
|
||||
|
||||
/* pltfm-specific */
|
||||
#define ESDHC_HOST_CONTROL_LE 0x20
|
||||
|
||||
/*
|
||||
* P2020 interpretation of the SDHCI_HOST_CONTROL register
|
||||
* eSDHC register definition
|
||||
*/
|
||||
#define ESDHC_CTRL_4BITBUS (0x1 << 1)
|
||||
#define ESDHC_CTRL_8BITBUS (0x2 << 1)
|
||||
#define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1)
|
||||
|
||||
/* OF-specific */
|
||||
#define ESDHC_DMA_SYSCTL 0x40c
|
||||
#define ESDHC_DMA_SNOOP 0x00000040
|
||||
/* Present State Register */
|
||||
#define ESDHC_PRSSTAT 0x24
|
||||
#define ESDHC_CLOCK_STABLE 0x00000008
|
||||
|
||||
#define ESDHC_HOST_CONTROL_RES 0x01
|
||||
/* Protocol Control Register */
|
||||
#define ESDHC_PROCTL 0x28
|
||||
#define ESDHC_CTRL_4BITBUS (0x1 << 1)
|
||||
#define ESDHC_CTRL_8BITBUS (0x2 << 1)
|
||||
#define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1)
|
||||
#define ESDHC_HOST_CONTROL_RES 0x01
|
||||
|
||||
/* System Control Register */
|
||||
#define ESDHC_SYSTEM_CONTROL 0x2c
|
||||
#define ESDHC_CLOCK_MASK 0x0000fff0
|
||||
#define ESDHC_PREDIV_SHIFT 8
|
||||
#define ESDHC_DIVIDER_SHIFT 4
|
||||
#define ESDHC_CLOCK_SDCLKEN 0x00000008
|
||||
#define ESDHC_CLOCK_PEREN 0x00000004
|
||||
#define ESDHC_CLOCK_HCKEN 0x00000002
|
||||
#define ESDHC_CLOCK_IPGEN 0x00000001
|
||||
|
||||
/* Control Register for DMA transfer */
|
||||
#define ESDHC_DMA_SYSCTL 0x40c
|
||||
#define ESDHC_DMA_SNOOP 0x00000040
|
||||
|
||||
#endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */
|
||||
|
|
|
@ -211,14 +211,19 @@ static const struct sdhci_iproc_data iproc_data = {
|
|||
static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
SDHCI_QUIRK_MISSING_CAPS,
|
||||
SDHCI_QUIRK_MISSING_CAPS |
|
||||
SDHCI_QUIRK_NO_HISPD_BIT,
|
||||
.ops = &sdhci_iproc_32only_ops,
|
||||
};
|
||||
|
||||
static const struct sdhci_iproc_data bcm2835_data = {
|
||||
.pdata = &sdhci_bcm2835_pltfm_data,
|
||||
.caps = SDHCI_CAN_VDD_330,
|
||||
.caps1 = 0x00000000,
|
||||
.caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT)
|
||||
& SDHCI_MAX_BLOCK_MASK) |
|
||||
SDHCI_CAN_VDD_330 |
|
||||
SDHCI_CAN_DO_HISPD,
|
||||
.caps1 = SDHCI_DRIVER_TYPE_A |
|
||||
SDHCI_DRIVER_TYPE_C,
|
||||
.mmc_caps = 0x00000000,
|
||||
};
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
#define CORE_DLL_CLOCK_DISABLE BIT(21)
|
||||
|
||||
#define CORE_VENDOR_SPEC 0x10c
|
||||
#define CORE_VENDOR_SPEC_POR_VAL 0xa1c
|
||||
#define CORE_CLK_PWRSAVE BIT(1)
|
||||
#define CORE_HC_MCLK_SEL_DFLT (2 << 8)
|
||||
#define CORE_HC_MCLK_SEL_HS400 (3 << 8)
|
||||
|
@ -102,6 +103,7 @@
|
|||
|
||||
#define CORE_DDR_200_CFG 0x184
|
||||
#define CORE_CDC_T4_DLY_SEL BIT(0)
|
||||
#define CORE_CMDIN_RCLK_EN BIT(1)
|
||||
#define CORE_START_CDC_TRAFFIC BIT(6)
|
||||
#define CORE_VENDOR_SPEC3 0x1b0
|
||||
#define CORE_PWRSAVE_DLL BIT(3)
|
||||
|
@ -138,6 +140,46 @@ struct sdhci_msm_host {
|
|||
bool use_cdclp533;
|
||||
};
|
||||
|
||||
static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host,
|
||||
unsigned int clock)
|
||||
{
|
||||
struct mmc_ios ios = host->mmc->ios;
|
||||
/*
|
||||
* The SDHC requires internal clock frequency to be double the
|
||||
* actual clock that will be set for DDR mode. The controller
|
||||
* uses the faster clock(100/400MHz) for some of its parts and
|
||||
* send the actual required clock (50/200MHz) to the card.
|
||||
*/
|
||||
if (ios.timing == MMC_TIMING_UHS_DDR50 ||
|
||||
ios.timing == MMC_TIMING_MMC_DDR52 ||
|
||||
ios.timing == MMC_TIMING_MMC_HS400 ||
|
||||
host->flags & SDHCI_HS400_TUNING)
|
||||
clock *= 2;
|
||||
return clock;
|
||||
}
|
||||
|
||||
static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
|
||||
unsigned int clock)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
struct mmc_ios curr_ios = host->mmc->ios;
|
||||
int rc;
|
||||
|
||||
clock = msm_get_clock_rate_for_bus_mode(host, clock);
|
||||
rc = clk_set_rate(msm_host->clk, clock);
|
||||
if (rc) {
|
||||
pr_err("%s: Failed to set clock at rate %u at timing %d\n",
|
||||
mmc_hostname(host->mmc), clock,
|
||||
curr_ios.timing);
|
||||
return;
|
||||
}
|
||||
msm_host->clk_rate = clock;
|
||||
pr_debug("%s: Setting clock at rate %lu at timing %d\n",
|
||||
mmc_hostname(host->mmc), clk_get_rate(msm_host->clk),
|
||||
curr_ios.timing);
|
||||
}
|
||||
|
||||
/* Platform specific tuning */
|
||||
static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll)
|
||||
{
|
||||
|
@ -464,6 +506,122 @@ static int msm_init_cm_dll(struct sdhci_host *host)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void msm_hc_select_default(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 config;
|
||||
|
||||
if (!msm_host->use_cdclp533) {
|
||||
config = readl_relaxed(host->ioaddr +
|
||||
CORE_VENDOR_SPEC3);
|
||||
config &= ~CORE_PWRSAVE_DLL;
|
||||
writel_relaxed(config, host->ioaddr +
|
||||
CORE_VENDOR_SPEC3);
|
||||
}
|
||||
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config &= ~CORE_HC_MCLK_SEL_MASK;
|
||||
config |= CORE_HC_MCLK_SEL_DFLT;
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
|
||||
/*
|
||||
* Disable HC_SELECT_IN to be able to use the UHS mode select
|
||||
* configuration from Host Control2 register for all other
|
||||
* modes.
|
||||
* Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field
|
||||
* in VENDOR_SPEC_FUNC
|
||||
*/
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config &= ~CORE_HC_SELECT_IN_EN;
|
||||
config &= ~CORE_HC_SELECT_IN_MASK;
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
|
||||
/*
|
||||
* Make sure above writes impacting free running MCLK are completed
|
||||
* before changing the clk_rate at GCC.
|
||||
*/
|
||||
wmb();
|
||||
}
|
||||
|
||||
static void msm_hc_select_hs400(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
struct mmc_ios ios = host->mmc->ios;
|
||||
u32 config, dll_lock;
|
||||
int rc;
|
||||
|
||||
/* Select the divided clock (free running MCLK/2) */
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config &= ~CORE_HC_MCLK_SEL_MASK;
|
||||
config |= CORE_HC_MCLK_SEL_HS400;
|
||||
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
/*
|
||||
* Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC
|
||||
* register
|
||||
*/
|
||||
if ((msm_host->tuning_done || ios.enhanced_strobe) &&
|
||||
!msm_host->calibration_done) {
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config |= CORE_HC_SELECT_IN_HS400;
|
||||
config |= CORE_HC_SELECT_IN_EN;
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
}
|
||||
if (!msm_host->clk_rate && !msm_host->use_cdclp533) {
|
||||
/*
|
||||
* Poll on DLL_LOCK or DDR_DLL_LOCK bits in
|
||||
* CORE_DLL_STATUS to be set. This should get set
|
||||
* within 15 us at 200 MHz.
|
||||
*/
|
||||
rc = readl_relaxed_poll_timeout(host->ioaddr +
|
||||
CORE_DLL_STATUS,
|
||||
dll_lock,
|
||||
(dll_lock &
|
||||
(CORE_DLL_LOCK |
|
||||
CORE_DDR_DLL_LOCK)), 10,
|
||||
1000);
|
||||
if (rc == -ETIMEDOUT)
|
||||
pr_err("%s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x\n",
|
||||
mmc_hostname(host->mmc), dll_lock);
|
||||
}
|
||||
/*
|
||||
* Make sure above writes impacting free running MCLK are completed
|
||||
* before changing the clk_rate at GCC.
|
||||
*/
|
||||
wmb();
|
||||
}
|
||||
|
||||
/*
|
||||
* sdhci_msm_hc_select_mode :- In general all timing modes are
|
||||
* controlled via UHS mode select in Host Control2 register.
|
||||
* eMMC specific HS200/HS400 doesn't have their respective modes
|
||||
* defined here, hence we use these values.
|
||||
*
|
||||
* HS200 - SDR104 (Since they both are equivalent in functionality)
|
||||
* HS400 - This involves multiple configurations
|
||||
* Initially SDR104 - when tuning is required as HS200
|
||||
* Then when switching to DDR @ 400MHz (HS400) we use
|
||||
* the vendor specific HC_SELECT_IN to control the mode.
|
||||
*
|
||||
* In addition to controlling the modes we also need to select the
|
||||
* correct input clock for DLL depending on the mode.
|
||||
*
|
||||
* HS400 - divided clock (free running MCLK/2)
|
||||
* All other modes - default (free running MCLK)
|
||||
*/
|
||||
void sdhci_msm_hc_select_mode(struct sdhci_host *host)
|
||||
{
|
||||
struct mmc_ios ios = host->mmc->ios;
|
||||
|
||||
if (ios.timing == MMC_TIMING_MMC_HS400 ||
|
||||
host->flags & SDHCI_HS400_TUNING)
|
||||
msm_hc_select_hs400(host);
|
||||
else
|
||||
msm_hc_select_default(host);
|
||||
}
|
||||
|
||||
static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
@ -506,19 +664,7 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
|
|||
config &= ~CORE_START_CDC_TRAFFIC;
|
||||
writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG);
|
||||
|
||||
/*
|
||||
* Perform CDC Register Initialization Sequence
|
||||
*
|
||||
* CORE_CSR_CDC_CTLR_CFG0 0x11800EC
|
||||
* CORE_CSR_CDC_CTLR_CFG1 0x3011111
|
||||
* CORE_CSR_CDC_CAL_TIMER_CFG0 0x1201000
|
||||
* CORE_CSR_CDC_CAL_TIMER_CFG1 0x4
|
||||
* CORE_CSR_CDC_REFCOUNT_CFG 0xCB732020
|
||||
* CORE_CSR_CDC_COARSE_CAL_CFG 0xB19
|
||||
* CORE_CSR_CDC_DELAY_CFG 0x3AC
|
||||
* CORE_CDC_OFFSET_CFG 0x0
|
||||
* CORE_CDC_SLAVE_DDA_CFG 0x16334
|
||||
*/
|
||||
/* Perform CDC Register Initialization Sequence */
|
||||
|
||||
writel_relaxed(0x11800EC, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
|
||||
writel_relaxed(0x3011111, host->ioaddr + CORE_CSR_CDC_CTLR_CFG1);
|
||||
|
@ -526,7 +672,7 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
|
|||
writel_relaxed(0x4, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG1);
|
||||
writel_relaxed(0xCB732020, host->ioaddr + CORE_CSR_CDC_REFCOUNT_CFG);
|
||||
writel_relaxed(0xB19, host->ioaddr + CORE_CSR_CDC_COARSE_CAL_CFG);
|
||||
writel_relaxed(0x3AC, host->ioaddr + CORE_CSR_CDC_DELAY_CFG);
|
||||
writel_relaxed(0x4E2, host->ioaddr + CORE_CSR_CDC_DELAY_CFG);
|
||||
writel_relaxed(0x0, host->ioaddr + CORE_CDC_OFFSET_CFG);
|
||||
writel_relaxed(0x16334, host->ioaddr + CORE_CDC_SLAVE_DDA_CFG);
|
||||
|
||||
|
@ -579,6 +725,7 @@ out:
|
|||
|
||||
static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
u32 dll_status, config;
|
||||
int ret;
|
||||
|
||||
|
@ -593,6 +740,12 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host)
|
|||
*/
|
||||
writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + CORE_DDR_CONFIG);
|
||||
|
||||
if (mmc->ios.enhanced_strobe) {
|
||||
config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG);
|
||||
config |= CORE_CMDIN_RCLK_EN;
|
||||
writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG);
|
||||
}
|
||||
|
||||
config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2);
|
||||
config |= CORE_DDR_CAL_EN;
|
||||
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2);
|
||||
|
@ -627,6 +780,7 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host)
|
|||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
int ret;
|
||||
u32 config;
|
||||
|
||||
|
@ -640,14 +794,17 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host)
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* Set the selected phase in delay line hw block */
|
||||
ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (!mmc->ios.enhanced_strobe) {
|
||||
/* Set the selected phase in delay line hw block */
|
||||
ret = msm_config_cm_dll_phase(host,
|
||||
msm_host->saved_tuning_phase);
|
||||
if (ret)
|
||||
goto out;
|
||||
config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
|
||||
config |= CORE_CMD_DAT_TRACK_SEL;
|
||||
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
|
||||
}
|
||||
|
||||
config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
|
||||
config |= CORE_CMD_DAT_TRACK_SEL;
|
||||
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
|
||||
if (msm_host->use_cdclp533)
|
||||
ret = sdhci_msm_cdclp533_calibration(host);
|
||||
else
|
||||
|
@ -658,12 +815,12 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
|
||||
static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
int tuning_seq_cnt = 3;
|
||||
u8 phase, tuned_phases[16], tuned_phase_cnt = 0;
|
||||
int rc;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
struct mmc_ios ios = host->mmc->ios;
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
|
@ -678,6 +835,17 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
|
|||
ios.timing == MMC_TIMING_UHS_SDR104))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* For HS400 tuning in HS200 timing requires:
|
||||
* - select MCLK/2 in VENDOR_SPEC
|
||||
* - program MCLK to 400MHz (or nearest supported) in GCC
|
||||
*/
|
||||
if (host->flags & SDHCI_HS400_TUNING) {
|
||||
sdhci_msm_hc_select_mode(host);
|
||||
msm_set_clock_rate_for_bus_mode(host, ios.clock);
|
||||
host->flags &= ~SDHCI_HS400_TUNING;
|
||||
}
|
||||
|
||||
retry:
|
||||
/* First of all reset the tuning block */
|
||||
rc = msm_init_cm_dll(host);
|
||||
|
@ -732,6 +900,30 @@ retry:
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* sdhci_msm_hs400 - Calibrate the DLL for HS400 bus speed mode operation.
|
||||
* This needs to be done for both tuning and enhanced_strobe mode.
|
||||
* DLL operation is only needed for clock > 100MHz. For clock <= 100MHz
|
||||
* fixed feedback clock is used.
|
||||
*/
|
||||
static void sdhci_msm_hs400(struct sdhci_host *host, struct mmc_ios *ios)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
if (host->clock > CORE_FREQ_100MHZ &&
|
||||
(msm_host->tuning_done || ios->enhanced_strobe) &&
|
||||
!msm_host->calibration_done) {
|
||||
ret = sdhci_msm_hs400_dll_calibration(host);
|
||||
if (!ret)
|
||||
msm_host->calibration_done = true;
|
||||
else
|
||||
pr_err("%s: Failed to calibrate DLL for hs400 mode (%d)\n",
|
||||
mmc_hostname(host->mmc), ret);
|
||||
}
|
||||
}
|
||||
|
||||
static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
|
||||
unsigned int uhs)
|
||||
{
|
||||
|
@ -800,12 +992,10 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
|
|||
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
||||
|
||||
spin_unlock_irq(&host->lock);
|
||||
/* CDCLP533 HW calibration is only required for HS400 mode*/
|
||||
if (host->clock > CORE_FREQ_100MHZ &&
|
||||
msm_host->tuning_done && !msm_host->calibration_done &&
|
||||
mmc->ios.timing == MMC_TIMING_MMC_HS400)
|
||||
if (!sdhci_msm_hs400_dll_calibration(host))
|
||||
msm_host->calibration_done = true;
|
||||
|
||||
if (mmc->ios.timing == MMC_TIMING_MMC_HS400)
|
||||
sdhci_msm_hs400(host, &mmc->ios);
|
||||
|
||||
spin_lock_irq(&host->lock);
|
||||
}
|
||||
|
||||
|
@ -893,9 +1083,6 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
struct mmc_ios curr_ios = host->mmc->ios;
|
||||
u32 config, dll_lock;
|
||||
int rc;
|
||||
|
||||
if (!clock) {
|
||||
msm_host->clk_rate = clock;
|
||||
|
@ -903,117 +1090,11 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
}
|
||||
|
||||
spin_unlock_irq(&host->lock);
|
||||
/*
|
||||
* The SDHC requires internal clock frequency to be double the
|
||||
* actual clock that will be set for DDR mode. The controller
|
||||
* uses the faster clock(100/400MHz) for some of its parts and
|
||||
* send the actual required clock (50/200MHz) to the card.
|
||||
*/
|
||||
if (curr_ios.timing == MMC_TIMING_UHS_DDR50 ||
|
||||
curr_ios.timing == MMC_TIMING_MMC_DDR52 ||
|
||||
curr_ios.timing == MMC_TIMING_MMC_HS400)
|
||||
clock *= 2;
|
||||
/*
|
||||
* In general all timing modes are controlled via UHS mode select in
|
||||
* Host Control2 register. eMMC specific HS200/HS400 doesn't have
|
||||
* their respective modes defined here, hence we use these values.
|
||||
*
|
||||
* HS200 - SDR104 (Since they both are equivalent in functionality)
|
||||
* HS400 - This involves multiple configurations
|
||||
* Initially SDR104 - when tuning is required as HS200
|
||||
* Then when switching to DDR @ 400MHz (HS400) we use
|
||||
* the vendor specific HC_SELECT_IN to control the mode.
|
||||
*
|
||||
* In addition to controlling the modes we also need to select the
|
||||
* correct input clock for DLL depending on the mode.
|
||||
*
|
||||
* HS400 - divided clock (free running MCLK/2)
|
||||
* All other modes - default (free running MCLK)
|
||||
*/
|
||||
if (curr_ios.timing == MMC_TIMING_MMC_HS400) {
|
||||
/* Select the divided clock (free running MCLK/2) */
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config &= ~CORE_HC_MCLK_SEL_MASK;
|
||||
config |= CORE_HC_MCLK_SEL_HS400;
|
||||
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
/*
|
||||
* Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC
|
||||
* register
|
||||
*/
|
||||
if (msm_host->tuning_done && !msm_host->calibration_done) {
|
||||
/*
|
||||
* Write 0x6 to HC_SELECT_IN and 1 to HC_SELECT_IN_EN
|
||||
* field in VENDOR_SPEC_FUNC
|
||||
*/
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config |= CORE_HC_SELECT_IN_HS400;
|
||||
config |= CORE_HC_SELECT_IN_EN;
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
}
|
||||
if (!msm_host->clk_rate && !msm_host->use_cdclp533) {
|
||||
/*
|
||||
* Poll on DLL_LOCK or DDR_DLL_LOCK bits in
|
||||
* CORE_DLL_STATUS to be set. This should get set
|
||||
* within 15 us at 200 MHz.
|
||||
*/
|
||||
rc = readl_relaxed_poll_timeout(host->ioaddr +
|
||||
CORE_DLL_STATUS,
|
||||
dll_lock,
|
||||
(dll_lock &
|
||||
(CORE_DLL_LOCK |
|
||||
CORE_DDR_DLL_LOCK)), 10,
|
||||
1000);
|
||||
if (rc == -ETIMEDOUT)
|
||||
pr_err("%s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x\n",
|
||||
mmc_hostname(host->mmc), dll_lock);
|
||||
}
|
||||
} else {
|
||||
if (!msm_host->use_cdclp533) {
|
||||
config = readl_relaxed(host->ioaddr +
|
||||
CORE_VENDOR_SPEC3);
|
||||
config &= ~CORE_PWRSAVE_DLL;
|
||||
writel_relaxed(config, host->ioaddr +
|
||||
CORE_VENDOR_SPEC3);
|
||||
}
|
||||
sdhci_msm_hc_select_mode(host);
|
||||
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config &= ~CORE_HC_MCLK_SEL_MASK;
|
||||
config |= CORE_HC_MCLK_SEL_DFLT;
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
msm_set_clock_rate_for_bus_mode(host, clock);
|
||||
|
||||
/*
|
||||
* Disable HC_SELECT_IN to be able to use the UHS mode select
|
||||
* configuration from Host Control2 register for all other
|
||||
* modes.
|
||||
* Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field
|
||||
* in VENDOR_SPEC_FUNC
|
||||
*/
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config &= ~CORE_HC_SELECT_IN_EN;
|
||||
config &= ~CORE_HC_SELECT_IN_MASK;
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure above writes impacting free running MCLK are completed
|
||||
* before changing the clk_rate at GCC.
|
||||
*/
|
||||
wmb();
|
||||
|
||||
rc = clk_set_rate(msm_host->clk, clock);
|
||||
if (rc) {
|
||||
pr_err("%s: Failed to set clock at rate %u at timing %d\n",
|
||||
mmc_hostname(host->mmc), clock,
|
||||
curr_ios.timing);
|
||||
goto out_lock;
|
||||
}
|
||||
msm_host->clk_rate = clock;
|
||||
pr_debug("%s: Setting clock at rate %lu at timing %d\n",
|
||||
mmc_hostname(host->mmc), clk_get_rate(msm_host->clk),
|
||||
curr_ios.timing);
|
||||
|
||||
out_lock:
|
||||
spin_lock_irq(&host->lock);
|
||||
out:
|
||||
__sdhci_msm_set_clock(host, clock);
|
||||
|
@ -1027,7 +1108,6 @@ static const struct of_device_id sdhci_msm_dt_match[] = {
|
|||
MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
|
||||
|
||||
static const struct sdhci_ops sdhci_msm_ops = {
|
||||
.platform_execute_tuning = sdhci_msm_execute_tuning,
|
||||
.reset = sdhci_reset,
|
||||
.set_clock = sdhci_msm_set_clock,
|
||||
.get_min_clock = sdhci_msm_get_min_clock,
|
||||
|
@ -1134,17 +1214,9 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
goto clk_disable;
|
||||
}
|
||||
|
||||
config = readl_relaxed(msm_host->core_mem + CORE_POWER);
|
||||
config |= CORE_SW_RST;
|
||||
writel_relaxed(config, msm_host->core_mem + CORE_POWER);
|
||||
|
||||
/* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
|
||||
usleep_range(1000, 5000);
|
||||
if (readl(msm_host->core_mem + CORE_POWER) & CORE_SW_RST) {
|
||||
dev_err(&pdev->dev, "Stuck in reset\n");
|
||||
ret = -ETIMEDOUT;
|
||||
goto clk_disable;
|
||||
}
|
||||
/* Reset the vendor spec register to power on reset state */
|
||||
writel_relaxed(CORE_VENDOR_SPEC_POR_VAL,
|
||||
host->ioaddr + CORE_VENDOR_SPEC);
|
||||
|
||||
/* Set HC_MODE_EN bit in HC_MODE register */
|
||||
writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
|
||||
|
@ -1210,6 +1282,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
MSM_MMC_AUTOSUSPEND_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
|
||||
host->mmc_host_ops.execute_tuning = sdhci_msm_execute_tuning;
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto pm_runtime_disable;
|
||||
|
|
|
@ -431,6 +431,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
int pre_div = 1;
|
||||
int div = 1;
|
||||
u32 timeout;
|
||||
u32 temp;
|
||||
|
||||
host->mmc->actual_clock = 0;
|
||||
|
@ -451,8 +452,8 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
}
|
||||
|
||||
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
|
||||
temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
|
||||
| ESDHC_CLOCK_MASK);
|
||||
temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
|
||||
ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
|
||||
while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
|
||||
|
@ -472,7 +473,21 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
| (div << ESDHC_DIVIDER_SHIFT)
|
||||
| (pre_div << ESDHC_PREDIV_SHIFT));
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
mdelay(1);
|
||||
|
||||
/* Wait max 20 ms */
|
||||
timeout = 20;
|
||||
while (!(sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)) {
|
||||
if (timeout == 0) {
|
||||
pr_err("%s: Internal clock never stabilised.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
return;
|
||||
}
|
||||
timeout--;
|
||||
mdelay(1);
|
||||
}
|
||||
|
||||
temp |= ESDHC_CLOCK_SDCLKEN;
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
}
|
||||
|
||||
static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
|
||||
|
@ -569,16 +584,19 @@ static const struct sdhci_ops sdhci_esdhc_le_ops = {
|
|||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = {
|
||||
.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION
|
||||
| SDHCI_QUIRK_NO_CARD_NO_RESET
|
||||
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.quirks = ESDHC_DEFAULT_QUIRKS |
|
||||
#ifdef CONFIG_PPC
|
||||
SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||
#endif
|
||||
SDHCI_QUIRK_NO_CARD_NO_RESET |
|
||||
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.ops = &sdhci_esdhc_be_ops,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_esdhc_le_pdata = {
|
||||
.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION
|
||||
| SDHCI_QUIRK_NO_CARD_NO_RESET
|
||||
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.quirks = ESDHC_DEFAULT_QUIRKS |
|
||||
SDHCI_QUIRK_NO_CARD_NO_RESET |
|
||||
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.ops = &sdhci_esdhc_le_ops,
|
||||
};
|
||||
|
||||
|
@ -643,8 +661,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
|
|||
of_device_is_compatible(np, "fsl,p5020-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,p4080-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,p1020-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,t1040-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,ls1021a-esdhc"))
|
||||
of_device_is_compatible(np, "fsl,t1040-esdhc"))
|
||||
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,ls1021a-esdhc"))
|
||||
|
|
|
@ -424,7 +424,6 @@ static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
|
|||
static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
slot->host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
|
||||
slot->cd_con_id = NULL;
|
||||
slot->cd_idx = 0;
|
||||
slot->cd_override_level = true;
|
||||
if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BXT_SD ||
|
||||
|
@ -866,6 +865,86 @@ enum amd_chipset_gen {
|
|||
AMD_CHIPSET_UNKNOWN,
|
||||
};
|
||||
|
||||
/* AMD registers */
|
||||
#define AMD_SD_AUTO_PATTERN 0xB8
|
||||
#define AMD_MSLEEP_DURATION 4
|
||||
#define AMD_SD_MISC_CONTROL 0xD0
|
||||
#define AMD_MAX_TUNE_VALUE 0x0B
|
||||
#define AMD_AUTO_TUNE_SEL 0x10800
|
||||
#define AMD_FIFO_PTR 0x30
|
||||
#define AMD_BIT_MASK 0x1F
|
||||
|
||||
static void amd_tuning_reset(struct sdhci_host *host)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
val |= SDHCI_CTRL_PRESET_VAL_ENABLE | SDHCI_CTRL_EXEC_TUNING;
|
||||
sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
|
||||
|
||||
val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
val &= ~SDHCI_CTRL_EXEC_TUNING;
|
||||
sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
|
||||
}
|
||||
|
||||
static void amd_config_tuning_phase(struct pci_dev *pdev, u8 phase)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
pci_read_config_dword(pdev, AMD_SD_AUTO_PATTERN, &val);
|
||||
val &= ~AMD_BIT_MASK;
|
||||
val |= (AMD_AUTO_TUNE_SEL | (phase << 1));
|
||||
pci_write_config_dword(pdev, AMD_SD_AUTO_PATTERN, val);
|
||||
}
|
||||
|
||||
static void amd_enable_manual_tuning(struct pci_dev *pdev)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
pci_read_config_dword(pdev, AMD_SD_MISC_CONTROL, &val);
|
||||
val |= AMD_FIFO_PTR;
|
||||
pci_write_config_dword(pdev, AMD_SD_MISC_CONTROL, val);
|
||||
}
|
||||
|
||||
static int amd_execute_tuning(struct sdhci_host *host, u32 opcode)
|
||||
{
|
||||
struct sdhci_pci_slot *slot = sdhci_priv(host);
|
||||
struct pci_dev *pdev = slot->chip->pdev;
|
||||
u8 valid_win = 0;
|
||||
u8 valid_win_max = 0;
|
||||
u8 valid_win_end = 0;
|
||||
u8 ctrl, tune_around;
|
||||
|
||||
amd_tuning_reset(host);
|
||||
|
||||
for (tune_around = 0; tune_around < 12; tune_around++) {
|
||||
amd_config_tuning_phase(pdev, tune_around);
|
||||
|
||||
if (mmc_send_tuning(host->mmc, opcode, NULL)) {
|
||||
valid_win = 0;
|
||||
msleep(AMD_MSLEEP_DURATION);
|
||||
ctrl = SDHCI_RESET_CMD | SDHCI_RESET_DATA;
|
||||
sdhci_writeb(host, ctrl, SDHCI_SOFTWARE_RESET);
|
||||
} else if (++valid_win > valid_win_max) {
|
||||
valid_win_max = valid_win;
|
||||
valid_win_end = tune_around;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid_win_max) {
|
||||
dev_err(&pdev->dev, "no tuning point found\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
amd_config_tuning_phase(pdev, valid_win_end - valid_win_max / 2);
|
||||
|
||||
amd_enable_manual_tuning(pdev);
|
||||
|
||||
host->mmc->retune_period = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_probe(struct sdhci_pci_chip *chip)
|
||||
{
|
||||
struct pci_dev *smbus_dev;
|
||||
|
@ -888,16 +967,24 @@ static int amd_probe(struct sdhci_pci_chip *chip)
|
|||
}
|
||||
}
|
||||
|
||||
if ((gen == AMD_CHIPSET_BEFORE_ML) || (gen == AMD_CHIPSET_CZ)) {
|
||||
if (gen == AMD_CHIPSET_BEFORE_ML || gen == AMD_CHIPSET_CZ)
|
||||
chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD;
|
||||
chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops amd_sdhci_pci_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.enable_dma = sdhci_pci_enable_dma,
|
||||
.set_bus_width = sdhci_pci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
.platform_execute_tuning = amd_execute_tuning,
|
||||
};
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_amd = {
|
||||
.probe = amd_probe,
|
||||
.ops = &amd_sdhci_pci_ops,
|
||||
};
|
||||
|
||||
static const struct pci_device_id pci_ids[] = {
|
||||
|
@ -1817,7 +1904,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
|
|||
host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
|
||||
|
||||
if (slot->cd_idx >= 0) {
|
||||
ret = mmc_gpiod_request_cd(host->mmc, slot->cd_con_id, slot->cd_idx,
|
||||
ret = mmc_gpiod_request_cd(host->mmc, NULL, slot->cd_idx,
|
||||
slot->cd_override_level, 0, NULL);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto remove;
|
||||
|
|
|
@ -81,7 +81,6 @@ struct sdhci_pci_slot {
|
|||
int cd_gpio;
|
||||
int cd_irq;
|
||||
|
||||
char *cd_con_id;
|
||||
int cd_idx;
|
||||
bool cd_override_level;
|
||||
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
/* linux/arch/arm/plat-s3c/include/plat/regs-sdhci.h
|
||||
*
|
||||
* Copyright 2008 Openmoko, Inc.
|
||||
* Copyright 2008 Simtec Electronics
|
||||
* http://armlinux.simtec.co.uk/
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
* S3C Platform - SDHCI (HSMMC) register definitions
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __PLAT_S3C_SDHCI_REGS_H
|
||||
#define __PLAT_S3C_SDHCI_REGS_H __FILE__
|
||||
|
||||
#define S3C_SDHCI_CONTROL2 (0x80)
|
||||
#define S3C_SDHCI_CONTROL3 (0x84)
|
||||
#define S3C64XX_SDHCI_CONTROL4 (0x8C)
|
||||
|
||||
#define S3C64XX_SDHCI_CTRL2_ENSTAASYNCCLR (1 << 31)
|
||||
#define S3C64XX_SDHCI_CTRL2_ENCMDCNFMSK (1 << 30)
|
||||
#define S3C_SDHCI_CTRL2_CDINVRXD3 (1 << 29)
|
||||
#define S3C_SDHCI_CTRL2_SLCARDOUT (1 << 28)
|
||||
|
||||
#define S3C_SDHCI_CTRL2_FLTCLKSEL_MASK (0xf << 24)
|
||||
#define S3C_SDHCI_CTRL2_FLTCLKSEL_SHIFT (24)
|
||||
#define S3C_SDHCI_CTRL2_FLTCLKSEL(_x) ((_x) << 24)
|
||||
|
||||
#define S3C_SDHCI_CTRL2_LVLDAT_MASK (0xff << 16)
|
||||
#define S3C_SDHCI_CTRL2_LVLDAT_SHIFT (16)
|
||||
#define S3C_SDHCI_CTRL2_LVLDAT(_x) ((_x) << 16)
|
||||
|
||||
#define S3C_SDHCI_CTRL2_ENFBCLKTX (1 << 15)
|
||||
#define S3C_SDHCI_CTRL2_ENFBCLKRX (1 << 14)
|
||||
#define S3C_SDHCI_CTRL2_SDCDSEL (1 << 13)
|
||||
#define S3C_SDHCI_CTRL2_SDSIGPC (1 << 12)
|
||||
#define S3C_SDHCI_CTRL2_ENBUSYCHKTXSTART (1 << 11)
|
||||
|
||||
#define S3C_SDHCI_CTRL2_DFCNT_MASK (0x3 << 9)
|
||||
#define S3C_SDHCI_CTRL2_DFCNT_SHIFT (9)
|
||||
#define S3C_SDHCI_CTRL2_DFCNT_NONE (0x0 << 9)
|
||||
#define S3C_SDHCI_CTRL2_DFCNT_4SDCLK (0x1 << 9)
|
||||
#define S3C_SDHCI_CTRL2_DFCNT_16SDCLK (0x2 << 9)
|
||||
#define S3C_SDHCI_CTRL2_DFCNT_64SDCLK (0x3 << 9)
|
||||
|
||||
#define S3C_SDHCI_CTRL2_ENCLKOUTHOLD (1 << 8)
|
||||
#define S3C_SDHCI_CTRL2_RWAITMODE (1 << 7)
|
||||
#define S3C_SDHCI_CTRL2_DISBUFRD (1 << 6)
|
||||
#define S3C_SDHCI_CTRL2_SELBASECLK_MASK (0x3 << 4)
|
||||
#define S3C_SDHCI_CTRL2_SELBASECLK_SHIFT (4)
|
||||
#define S3C_SDHCI_CTRL2_PWRSYNC (1 << 3)
|
||||
#define S3C_SDHCI_CTRL2_ENCLKOUTMSKCON (1 << 1)
|
||||
#define S3C_SDHCI_CTRL2_HWINITFIN (1 << 0)
|
||||
|
||||
#define S3C_SDHCI_CTRL3_FCSEL3 (1 << 31)
|
||||
#define S3C_SDHCI_CTRL3_FCSEL2 (1 << 23)
|
||||
#define S3C_SDHCI_CTRL3_FCSEL1 (1 << 15)
|
||||
#define S3C_SDHCI_CTRL3_FCSEL0 (1 << 7)
|
||||
|
||||
#define S3C_SDHCI_CTRL3_FIA3_MASK (0x7f << 24)
|
||||
#define S3C_SDHCI_CTRL3_FIA3_SHIFT (24)
|
||||
#define S3C_SDHCI_CTRL3_FIA3(_x) ((_x) << 24)
|
||||
|
||||
#define S3C_SDHCI_CTRL3_FIA2_MASK (0x7f << 16)
|
||||
#define S3C_SDHCI_CTRL3_FIA2_SHIFT (16)
|
||||
#define S3C_SDHCI_CTRL3_FIA2(_x) ((_x) << 16)
|
||||
|
||||
#define S3C_SDHCI_CTRL3_FIA1_MASK (0x7f << 8)
|
||||
#define S3C_SDHCI_CTRL3_FIA1_SHIFT (8)
|
||||
#define S3C_SDHCI_CTRL3_FIA1(_x) ((_x) << 8)
|
||||
|
||||
#define S3C_SDHCI_CTRL3_FIA0_MASK (0x7f << 0)
|
||||
#define S3C_SDHCI_CTRL3_FIA0_SHIFT (0)
|
||||
#define S3C_SDHCI_CTRL3_FIA0(_x) ((_x) << 0)
|
||||
|
||||
#define S3C64XX_SDHCI_CONTROL4_DRIVE_MASK (0x3 << 16)
|
||||
#define S3C64XX_SDHCI_CONTROL4_DRIVE_SHIFT (16)
|
||||
#define S3C64XX_SDHCI_CONTROL4_DRIVE_2mA (0x0 << 16)
|
||||
#define S3C64XX_SDHCI_CONTROL4_DRIVE_4mA (0x1 << 16)
|
||||
#define S3C64XX_SDHCI_CONTROL4_DRIVE_7mA (0x2 << 16)
|
||||
#define S3C64XX_SDHCI_CONTROL4_DRIVE_9mA (0x3 << 16)
|
||||
|
||||
#define S3C64XX_SDHCI_CONTROL4_BUSY (1)
|
||||
|
||||
#endif /* __PLAT_S3C_SDHCI_REGS_H */
|
|
@ -29,11 +29,80 @@
|
|||
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
#include "sdhci-s3c-regs.h"
|
||||
#include "sdhci.h"
|
||||
|
||||
#define MAX_BUS_CLK (4)
|
||||
|
||||
#define S3C_SDHCI_CONTROL2 (0x80)
|
||||
#define S3C_SDHCI_CONTROL3 (0x84)
|
||||
#define S3C64XX_SDHCI_CONTROL4 (0x8C)
|
||||
|
||||
#define S3C64XX_SDHCI_CTRL2_ENSTAASYNCCLR BIT(31)
|
||||
#define S3C64XX_SDHCI_CTRL2_ENCMDCNFMSK BIT(30)
|
||||
#define S3C_SDHCI_CTRL2_CDINVRXD3 BIT(29)
|
||||
#define S3C_SDHCI_CTRL2_SLCARDOUT BIT(28)
|
||||
|
||||
#define S3C_SDHCI_CTRL2_FLTCLKSEL_MASK (0xf << 24)
|
||||
#define S3C_SDHCI_CTRL2_FLTCLKSEL_SHIFT (24)
|
||||
#define S3C_SDHCI_CTRL2_FLTCLKSEL(_x) ((_x) << 24)
|
||||
|
||||
#define S3C_SDHCI_CTRL2_LVLDAT_MASK (0xff << 16)
|
||||
#define S3C_SDHCI_CTRL2_LVLDAT_SHIFT (16)
|
||||
#define S3C_SDHCI_CTRL2_LVLDAT(_x) ((_x) << 16)
|
||||
|
||||
#define S3C_SDHCI_CTRL2_ENFBCLKTX BIT(15)
|
||||
#define S3C_SDHCI_CTRL2_ENFBCLKRX BIT(14)
|
||||
#define S3C_SDHCI_CTRL2_SDCDSEL BIT(13)
|
||||
#define S3C_SDHCI_CTRL2_SDSIGPC BIT(12)
|
||||
#define S3C_SDHCI_CTRL2_ENBUSYCHKTXSTART BIT(11)
|
||||
|
||||
#define S3C_SDHCI_CTRL2_DFCNT_MASK (0x3 << 9)
|
||||
#define S3C_SDHCI_CTRL2_DFCNT_SHIFT (9)
|
||||
#define S3C_SDHCI_CTRL2_DFCNT_NONE (0x0 << 9)
|
||||
#define S3C_SDHCI_CTRL2_DFCNT_4SDCLK (0x1 << 9)
|
||||
#define S3C_SDHCI_CTRL2_DFCNT_16SDCLK (0x2 << 9)
|
||||
#define S3C_SDHCI_CTRL2_DFCNT_64SDCLK (0x3 << 9)
|
||||
|
||||
#define S3C_SDHCI_CTRL2_ENCLKOUTHOLD BIT(8)
|
||||
#define S3C_SDHCI_CTRL2_RWAITMODE BIT(7)
|
||||
#define S3C_SDHCI_CTRL2_DISBUFRD BIT(6)
|
||||
|
||||
#define S3C_SDHCI_CTRL2_SELBASECLK_MASK (0x3 << 4)
|
||||
#define S3C_SDHCI_CTRL2_SELBASECLK_SHIFT (4)
|
||||
#define S3C_SDHCI_CTRL2_PWRSYNC BIT(3)
|
||||
#define S3C_SDHCI_CTRL2_ENCLKOUTMSKCON BIT(1)
|
||||
#define S3C_SDHCI_CTRL2_HWINITFIN BIT(0)
|
||||
|
||||
#define S3C_SDHCI_CTRL3_FCSEL3 BIT(31)
|
||||
#define S3C_SDHCI_CTRL3_FCSEL2 BIT(23)
|
||||
#define S3C_SDHCI_CTRL3_FCSEL1 BIT(15)
|
||||
#define S3C_SDHCI_CTRL3_FCSEL0 BIT(7)
|
||||
|
||||
#define S3C_SDHCI_CTRL3_FIA3_MASK (0x7f << 24)
|
||||
#define S3C_SDHCI_CTRL3_FIA3_SHIFT (24)
|
||||
#define S3C_SDHCI_CTRL3_FIA3(_x) ((_x) << 24)
|
||||
|
||||
#define S3C_SDHCI_CTRL3_FIA2_MASK (0x7f << 16)
|
||||
#define S3C_SDHCI_CTRL3_FIA2_SHIFT (16)
|
||||
#define S3C_SDHCI_CTRL3_FIA2(_x) ((_x) << 16)
|
||||
|
||||
#define S3C_SDHCI_CTRL3_FIA1_MASK (0x7f << 8)
|
||||
#define S3C_SDHCI_CTRL3_FIA1_SHIFT (8)
|
||||
#define S3C_SDHCI_CTRL3_FIA1(_x) ((_x) << 8)
|
||||
|
||||
#define S3C_SDHCI_CTRL3_FIA0_MASK (0x7f << 0)
|
||||
#define S3C_SDHCI_CTRL3_FIA0_SHIFT (0)
|
||||
#define S3C_SDHCI_CTRL3_FIA0(_x) ((_x) << 0)
|
||||
|
||||
#define S3C64XX_SDHCI_CONTROL4_DRIVE_MASK (0x3 << 16)
|
||||
#define S3C64XX_SDHCI_CONTROL4_DRIVE_SHIFT (16)
|
||||
#define S3C64XX_SDHCI_CONTROL4_DRIVE_2mA (0x0 << 16)
|
||||
#define S3C64XX_SDHCI_CONTROL4_DRIVE_4mA (0x1 << 16)
|
||||
#define S3C64XX_SDHCI_CONTROL4_DRIVE_7mA (0x2 << 16)
|
||||
#define S3C64XX_SDHCI_CONTROL4_DRIVE_9mA (0x3 << 16)
|
||||
|
||||
#define S3C64XX_SDHCI_CONTROL4_BUSY (1)
|
||||
|
||||
/**
|
||||
* struct sdhci_s3c - S3C SDHCI instance
|
||||
* @host: The SDHCI host created
|
||||
|
|
|
@ -2021,8 +2021,8 @@ static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode,
|
|||
unsigned long flags)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_command cmd = {};
|
||||
struct mmc_request mrq = {};
|
||||
|
||||
cmd.opcode = opcode;
|
||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
|
||||
|
@ -2114,7 +2114,6 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
hs400_tuning = host->flags & SDHCI_HS400_TUNING;
|
||||
host->flags &= ~SDHCI_HS400_TUNING;
|
||||
|
||||
if (host->tuning_mode == SDHCI_TUNING_MODE_1)
|
||||
tuning_count = host->tuning_count;
|
||||
|
@ -2156,7 +2155,9 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
|
||||
if (host->ops->platform_execute_tuning) {
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
return host->ops->platform_execute_tuning(host, opcode);
|
||||
err = host->ops->platform_execute_tuning(host, opcode);
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
host->mmc->retune_period = tuning_count;
|
||||
|
@ -2167,6 +2168,7 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
|
||||
sdhci_end_tuning(host);
|
||||
out_unlock:
|
||||
host->flags &= ~SDHCI_HS400_TUNING;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
return err;
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <linux/compiler.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
|
|
|
@ -1079,26 +1079,10 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
host->state = STATE_IDLE;
|
||||
}
|
||||
|
||||
static int sh_mmcif_get_cd(struct mmc_host *mmc)
|
||||
{
|
||||
struct sh_mmcif_host *host = mmc_priv(mmc);
|
||||
struct device *dev = sh_mmcif_host_to_dev(host);
|
||||
struct sh_mmcif_plat_data *p = dev->platform_data;
|
||||
int ret = mmc_gpio_get_cd(mmc);
|
||||
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
|
||||
if (!p || !p->get_cd)
|
||||
return -ENOSYS;
|
||||
else
|
||||
return p->get_cd(host->pd);
|
||||
}
|
||||
|
||||
static struct mmc_host_ops sh_mmcif_ops = {
|
||||
.request = sh_mmcif_request,
|
||||
.set_ios = sh_mmcif_set_ios,
|
||||
.get_cd = sh_mmcif_get_cd,
|
||||
.get_cd = mmc_gpio_get_cd,
|
||||
};
|
||||
|
||||
static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host)
|
||||
|
@ -1443,8 +1427,8 @@ static int sh_mmcif_probe(struct platform_device *pdev)
|
|||
host->mmc = mmc;
|
||||
host->addr = reg;
|
||||
host->timeout = msecs_to_jiffies(10000);
|
||||
host->ccs_enable = !pd || !pd->ccs_unsupported;
|
||||
host->clk_ctrl2_enable = pd && pd->clk_ctrl2_present;
|
||||
host->ccs_enable = true;
|
||||
host->clk_ctrl2_enable = false;
|
||||
|
||||
host->pd = pdev;
|
||||
|
||||
|
@ -1509,12 +1493,6 @@ static int sh_mmcif_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
if (pd && pd->use_cd_gpio) {
|
||||
ret = mmc_gpio_request_cd(mmc, pd->cd_gpio, 0);
|
||||
if (ret < 0)
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
mutex_init(&host->thread_lock);
|
||||
|
||||
ret = mmc_add_host(mmc);
|
||||
|
|
|
@ -143,6 +143,7 @@ MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
|
|||
|
||||
struct sh_mobile_sdhi {
|
||||
struct clk *clk;
|
||||
struct clk *clk_cd;
|
||||
struct tmio_mmc_data mmc_data;
|
||||
struct tmio_mmc_dma dma_priv;
|
||||
struct pinctrl *pinctrl;
|
||||
|
@ -190,6 +191,12 @@ static int sh_mobile_sdhi_clk_enable(struct tmio_mmc_host *host)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(priv->clk_cd);
|
||||
if (ret < 0) {
|
||||
clk_disable_unprepare(priv->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The clock driver may not know what maximum frequency
|
||||
* actually works, so it should be set with the max-frequency
|
||||
|
@ -255,6 +262,7 @@ static void sh_mobile_sdhi_clk_disable(struct tmio_mmc_host *host)
|
|||
struct sh_mobile_sdhi *priv = host_to_priv(host);
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
clk_disable_unprepare(priv->clk_cd);
|
||||
}
|
||||
|
||||
static int sh_mobile_sdhi_card_busy(struct mmc_host *mmc)
|
||||
|
@ -335,9 +343,6 @@ static unsigned int sh_mobile_sdhi_init_tuning(struct tmio_mmc_host *host)
|
|||
{
|
||||
struct sh_mobile_sdhi *priv;
|
||||
|
||||
if (!(host->mmc->caps & MMC_CAP_UHS_SDR104))
|
||||
return 0;
|
||||
|
||||
priv = host_to_priv(host);
|
||||
|
||||
/* set sampling clock selection range */
|
||||
|
@ -444,12 +449,7 @@ static int sh_mobile_sdhi_select_tuning(struct tmio_mmc_host *host)
|
|||
|
||||
static bool sh_mobile_sdhi_check_scc_error(struct tmio_mmc_host *host)
|
||||
{
|
||||
struct sh_mobile_sdhi *priv;
|
||||
|
||||
if (!(host->mmc->caps & MMC_CAP_UHS_SDR104))
|
||||
return 0;
|
||||
|
||||
priv = host_to_priv(host);
|
||||
struct sh_mobile_sdhi *priv = host_to_priv(host);
|
||||
|
||||
/* Check SCC error */
|
||||
if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL) &
|
||||
|
@ -468,9 +468,6 @@ static void sh_mobile_sdhi_hw_reset(struct tmio_mmc_host *host)
|
|||
{
|
||||
struct sh_mobile_sdhi *priv;
|
||||
|
||||
if (!(host->mmc->caps & MMC_CAP_UHS_SDR104))
|
||||
return;
|
||||
|
||||
priv = host_to_priv(host);
|
||||
|
||||
/* Reset SCC */
|
||||
|
@ -556,8 +553,7 @@ static void sh_mobile_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable)
|
|||
|
||||
static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(sh_mobile_sdhi_of_match, &pdev->dev);
|
||||
const struct sh_mobile_sdhi_of_data *of_data = of_device_get_match_data(&pdev->dev);
|
||||
struct sh_mobile_sdhi *priv;
|
||||
struct tmio_mmc_data *mmc_data;
|
||||
struct tmio_mmc_data *mmd = pdev->dev.platform_data;
|
||||
|
@ -584,6 +580,21 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
goto eprobe;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some controllers provide a 2nd clock just to run the internal card
|
||||
* detection logic. Unfortunately, the existing driver architecture does
|
||||
* not support a separation of clocks for runtime PM usage. When
|
||||
* native hotplug is used, the tmio driver assumes that the core
|
||||
* must continue to run for card detect to stay active, so we cannot
|
||||
* disable it.
|
||||
* Additionally, it is prohibited to supply a clock to the core but not
|
||||
* to the card detect circuit. That leaves us with if separate clocks
|
||||
* are presented, we must treat them both as virtually 1 clock.
|
||||
*/
|
||||
priv->clk_cd = devm_clk_get(&pdev->dev, "cd");
|
||||
if (IS_ERR(priv->clk_cd))
|
||||
priv->clk_cd = NULL;
|
||||
|
||||
priv->pinctrl = devm_pinctrl_get(&pdev->dev);
|
||||
if (!IS_ERR(priv->pinctrl)) {
|
||||
priv->pins_default = pinctrl_lookup_state(priv->pinctrl,
|
||||
|
@ -598,9 +609,8 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
goto eprobe;
|
||||
}
|
||||
|
||||
if (of_id && of_id->data) {
|
||||
const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
|
||||
|
||||
if (of_data) {
|
||||
mmc_data->flags |= of_data->tmio_flags;
|
||||
mmc_data->ocr_mask = of_data->tmio_ocr_mask;
|
||||
mmc_data->capabilities |= of_data->capabilities;
|
||||
|
@ -623,11 +633,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
host->card_busy = sh_mobile_sdhi_card_busy;
|
||||
host->start_signal_voltage_switch =
|
||||
sh_mobile_sdhi_start_signal_voltage_switch;
|
||||
host->init_tuning = sh_mobile_sdhi_init_tuning;
|
||||
host->prepare_tuning = sh_mobile_sdhi_prepare_tuning;
|
||||
host->select_tuning = sh_mobile_sdhi_select_tuning;
|
||||
host->check_scc_error = sh_mobile_sdhi_check_scc_error;
|
||||
host->hw_reset = sh_mobile_sdhi_hw_reset;
|
||||
}
|
||||
|
||||
/* Orginally registers were 16 bit apart, could be 32 or 64 nowadays */
|
||||
|
@ -659,40 +664,40 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
*/
|
||||
mmc_data->flags |= TMIO_MMC_HAVE_CMD12_CTRL;
|
||||
|
||||
/*
|
||||
* All SDHI need SDIO_INFO1 reserved bit
|
||||
*/
|
||||
mmc_data->flags |= TMIO_MMC_SDIO_STATUS_QUIRK;
|
||||
/* All SDHI have SDIO status bits which must be 1 */
|
||||
mmc_data->flags |= TMIO_MMC_SDIO_STATUS_SETBITS;
|
||||
|
||||
ret = tmio_mmc_host_probe(host, mmc_data);
|
||||
if (ret < 0)
|
||||
goto efree;
|
||||
|
||||
if (host->mmc->caps & MMC_CAP_UHS_SDR104) {
|
||||
/* Enable tuning iff we have an SCC and a supported mode */
|
||||
if (of_data && of_data->scc_offset &&
|
||||
(host->mmc->caps & MMC_CAP_UHS_SDR104 ||
|
||||
host->mmc->caps2 & MMC_CAP2_HS200_1_8V_SDR)) {
|
||||
const struct sh_mobile_sdhi_scc *taps = of_data->taps;
|
||||
bool hit = false;
|
||||
|
||||
host->mmc->caps |= MMC_CAP_HW_RESET;
|
||||
|
||||
if (of_id && of_id->data) {
|
||||
const struct sh_mobile_sdhi_of_data *of_data;
|
||||
const struct sh_mobile_sdhi_scc *taps;
|
||||
bool hit = false;
|
||||
|
||||
of_data = of_id->data;
|
||||
taps = of_data->taps;
|
||||
|
||||
for (i = 0; i < of_data->taps_num; i++) {
|
||||
if (taps[i].clk_rate == 0 ||
|
||||
taps[i].clk_rate == host->mmc->f_max) {
|
||||
host->scc_tappos = taps->tap;
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < of_data->taps_num; i++) {
|
||||
if (taps[i].clk_rate == 0 ||
|
||||
taps[i].clk_rate == host->mmc->f_max) {
|
||||
host->scc_tappos = taps->tap;
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hit)
|
||||
dev_warn(&host->pdev->dev, "Unknown clock rate for SDR104\n");
|
||||
|
||||
priv->scc_ctl = host->ctl + of_data->scc_offset;
|
||||
}
|
||||
|
||||
if (!hit)
|
||||
dev_warn(&host->pdev->dev, "Unknown clock rate for SDR104\n");
|
||||
|
||||
priv->scc_ctl = host->ctl + of_data->scc_offset;
|
||||
host->init_tuning = sh_mobile_sdhi_init_tuning;
|
||||
host->prepare_tuning = sh_mobile_sdhi_prepare_tuning;
|
||||
host->select_tuning = sh_mobile_sdhi_select_tuning;
|
||||
host->check_scc_error = sh_mobile_sdhi_check_scc_error;
|
||||
host->hw_reset = sh_mobile_sdhi_hw_reset;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* (C) Copyright 2013-2014 O2S GmbH <www.o2s.ch>
|
||||
* (C) Copyright 2013-2014 David Lanzend<EFBFBD>rfer <david.lanzendoerfer@o2s.ch>
|
||||
* (C) Copyright 2013-2014 Hans de Goede <hdegoede@redhat.com>
|
||||
* (C) Copyright 2017 Sootech SA
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
|
@ -101,6 +102,7 @@
|
|||
(SDXC_SOFT_RESET | SDXC_FIFO_RESET | SDXC_DMA_RESET)
|
||||
|
||||
/* clock control bits */
|
||||
#define SDXC_MASK_DATA0 BIT(31)
|
||||
#define SDXC_CARD_CLOCK_ON BIT(16)
|
||||
#define SDXC_LOW_POWER_ON BIT(17)
|
||||
|
||||
|
@ -253,6 +255,11 @@ struct sunxi_mmc_cfg {
|
|||
|
||||
/* does the IP block support autocalibration? */
|
||||
bool can_calibrate;
|
||||
|
||||
/* Does DATA0 needs to be masked while the clock is updated */
|
||||
bool mask_data0;
|
||||
|
||||
bool needs_new_timings;
|
||||
};
|
||||
|
||||
struct sunxi_mmc_host {
|
||||
|
@ -654,11 +661,16 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
|
|||
unsigned long expire = jiffies + msecs_to_jiffies(750);
|
||||
u32 rval;
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "%sabling the clock\n",
|
||||
oclk_en ? "en" : "dis");
|
||||
|
||||
rval = mmc_readl(host, REG_CLKCR);
|
||||
rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON);
|
||||
rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON | SDXC_MASK_DATA0);
|
||||
|
||||
if (oclk_en)
|
||||
rval |= SDXC_CARD_CLOCK_ON;
|
||||
if (host->cfg->mask_data0)
|
||||
rval |= SDXC_MASK_DATA0;
|
||||
|
||||
mmc_writel(host, REG_CLKCR, rval);
|
||||
|
||||
|
@ -678,46 +690,29 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
if (host->cfg->mask_data0) {
|
||||
rval = mmc_readl(host, REG_CLKCR);
|
||||
mmc_writel(host, REG_CLKCR, rval & ~SDXC_MASK_DATA0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sunxi_mmc_calibrate(struct sunxi_mmc_host *host, int reg_off)
|
||||
{
|
||||
u32 reg = readl(host->reg_base + reg_off);
|
||||
u32 delay;
|
||||
unsigned long timeout;
|
||||
|
||||
if (!host->cfg->can_calibrate)
|
||||
return 0;
|
||||
|
||||
reg &= ~(SDXC_CAL_DL_MASK << SDXC_CAL_DL_SW_SHIFT);
|
||||
reg &= ~SDXC_CAL_DL_SW_EN;
|
||||
|
||||
writel(reg | SDXC_CAL_START, host->reg_base + reg_off);
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "calibration started\n");
|
||||
|
||||
timeout = jiffies + HZ * SDXC_CAL_TIMEOUT;
|
||||
|
||||
while (!((reg = readl(host->reg_base + reg_off)) & SDXC_CAL_DONE)) {
|
||||
if (time_before(jiffies, timeout))
|
||||
cpu_relax();
|
||||
else {
|
||||
reg &= ~SDXC_CAL_START;
|
||||
writel(reg, host->reg_base + reg_off);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
delay = (reg >> SDXC_CAL_DL_SHIFT) & SDXC_CAL_DL_MASK;
|
||||
|
||||
reg &= ~SDXC_CAL_START;
|
||||
reg |= (delay << SDXC_CAL_DL_SW_SHIFT) | SDXC_CAL_DL_SW_EN;
|
||||
|
||||
writel(reg, host->reg_base + reg_off);
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "calibration ended, reg is 0x%x\n", reg);
|
||||
/*
|
||||
* FIXME:
|
||||
* This is not clear how the calibration is supposed to work
|
||||
* yet. The best rate have been obtained by simply setting the
|
||||
* delay to 0, as Allwinner does in its BSP.
|
||||
*
|
||||
* The only mode that doesn't have such a delay is HS400, that
|
||||
* is in itself a TODO.
|
||||
*/
|
||||
writel(SDXC_CAL_DL_SW_EN, host->reg_base + reg_off);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -745,6 +740,7 @@ static int sunxi_mmc_clk_set_phase(struct sunxi_mmc_host *host,
|
|||
index = SDXC_CLK_50M_DDR;
|
||||
}
|
||||
} else {
|
||||
dev_dbg(mmc_dev(host->mmc), "Invalid clock... returning\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -757,10 +753,21 @@ static int sunxi_mmc_clk_set_phase(struct sunxi_mmc_host *host,
|
|||
static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
|
||||
struct mmc_ios *ios)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
long rate;
|
||||
u32 rval, clock = ios->clock;
|
||||
int ret;
|
||||
|
||||
ret = sunxi_mmc_oclk_onoff(host, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Our clock is gated now */
|
||||
mmc->actual_clock = 0;
|
||||
|
||||
if (!ios->clock)
|
||||
return 0;
|
||||
|
||||
/* 8 bit DDR requires a higher module clock */
|
||||
if (ios->timing == MMC_TIMING_MMC_DDR52 &&
|
||||
ios->bus_width == MMC_BUS_WIDTH_8)
|
||||
|
@ -768,25 +775,21 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
|
|||
|
||||
rate = clk_round_rate(host->clk_mmc, clock);
|
||||
if (rate < 0) {
|
||||
dev_err(mmc_dev(host->mmc), "error rounding clk to %d: %ld\n",
|
||||
dev_err(mmc_dev(mmc), "error rounding clk to %d: %ld\n",
|
||||
clock, rate);
|
||||
return rate;
|
||||
}
|
||||
dev_dbg(mmc_dev(host->mmc), "setting clk to %d, rounded %ld\n",
|
||||
dev_dbg(mmc_dev(mmc), "setting clk to %d, rounded %ld\n",
|
||||
clock, rate);
|
||||
|
||||
/* setting clock rate */
|
||||
ret = clk_set_rate(host->clk_mmc, rate);
|
||||
if (ret) {
|
||||
dev_err(mmc_dev(host->mmc), "error setting clk to %ld: %d\n",
|
||||
dev_err(mmc_dev(mmc), "error setting clk to %ld: %d\n",
|
||||
rate, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sunxi_mmc_oclk_onoff(host, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* clear internal divider */
|
||||
rval = mmc_readl(host, REG_CLKCR);
|
||||
rval &= ~0xff;
|
||||
|
@ -798,6 +801,9 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
|
|||
}
|
||||
mmc_writel(host, REG_CLKCR, rval);
|
||||
|
||||
if (host->cfg->needs_new_timings)
|
||||
mmc_writel(host, REG_SD_NTSR, SDXC_2X_TIMING_MODE);
|
||||
|
||||
ret = sunxi_mmc_clk_set_phase(host, ios, rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -806,9 +812,22 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* TODO: enable calibrate on sdc2 SDXC_REG_DS_DL_REG of A64 */
|
||||
/*
|
||||
* FIXME:
|
||||
*
|
||||
* In HS400 we'll also need to calibrate the data strobe
|
||||
* signal. This should only happen on the MMC2 controller (at
|
||||
* least on the A64).
|
||||
*/
|
||||
|
||||
return sunxi_mmc_oclk_onoff(host, 1);
|
||||
ret = sunxi_mmc_oclk_onoff(host, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* And we just enabled our clock back */
|
||||
mmc->actual_clock = rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
|
@ -882,7 +901,7 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
mmc_writel(host, REG_GCTRL, rval);
|
||||
|
||||
/* set up clock */
|
||||
if (ios->clock && ios->power_mode) {
|
||||
if (ios->power_mode) {
|
||||
host->ferror = sunxi_mmc_clk_set_rate(host, ios);
|
||||
/* Android code had a usleep_range(50000, 55000); here */
|
||||
}
|
||||
|
@ -1089,6 +1108,14 @@ static const struct sunxi_mmc_cfg sun50i_a64_cfg = {
|
|||
.idma_des_size_bits = 16,
|
||||
.clk_delays = NULL,
|
||||
.can_calibrate = true,
|
||||
.mask_data0 = true,
|
||||
.needs_new_timings = true,
|
||||
};
|
||||
|
||||
static const struct sunxi_mmc_cfg sun50i_a64_emmc_cfg = {
|
||||
.idma_des_size_bits = 13,
|
||||
.clk_delays = NULL,
|
||||
.can_calibrate = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id sunxi_mmc_of_match[] = {
|
||||
|
@ -1097,6 +1124,7 @@ static const struct of_device_id sunxi_mmc_of_match[] = {
|
|||
{ .compatible = "allwinner,sun7i-a20-mmc", .data = &sun7i_a20_cfg },
|
||||
{ .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg },
|
||||
{ .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg },
|
||||
{ .compatible = "allwinner,sun50i-a64-emmc", .data = &sun50i_a64_emmc_cfg },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/pagemap.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#define CTL_SD_CMD 0x00
|
||||
#define CTL_ARG_REG 0x04
|
||||
|
@ -90,6 +91,8 @@
|
|||
#define TMIO_SDIO_STAT_EXWT 0x8000
|
||||
#define TMIO_SDIO_MASK_ALL 0xc007
|
||||
|
||||
#define TMIO_SDIO_SETBITS_MASK 0x0006
|
||||
|
||||
/* Define some IRQ masks */
|
||||
/* This is the mask used at reset by the chip */
|
||||
#define TMIO_MASK_ALL 0x837f031d
|
||||
|
|
|
@ -134,18 +134,25 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
|||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
|
||||
if (enable && !host->sdio_irq_enabled) {
|
||||
u16 sdio_status;
|
||||
|
||||
/* Keep device active while SDIO irq is enabled */
|
||||
pm_runtime_get_sync(mmc_dev(mmc));
|
||||
host->sdio_irq_enabled = true;
|
||||
|
||||
host->sdio_irq_enabled = true;
|
||||
host->sdio_irq_mask = TMIO_SDIO_MASK_ALL &
|
||||
~TMIO_SDIO_STAT_IOIRQ;
|
||||
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
|
||||
|
||||
/* Clear obsolete interrupts before enabling */
|
||||
sdio_status = sd_ctrl_read16(host, CTL_SDIO_STATUS) & ~TMIO_SDIO_MASK_ALL;
|
||||
if (host->pdata->flags & TMIO_MMC_SDIO_STATUS_SETBITS)
|
||||
sdio_status |= TMIO_SDIO_SETBITS_MASK;
|
||||
sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status);
|
||||
|
||||
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
|
||||
} else if (!enable && host->sdio_irq_enabled) {
|
||||
host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
|
||||
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
|
||||
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000);
|
||||
|
||||
host->sdio_irq_enabled = false;
|
||||
pm_runtime_mark_last_busy(mmc_dev(mmc));
|
||||
|
@ -711,9 +718,8 @@ static bool __tmio_mmc_sdcard_irq(struct tmio_mmc_host *host,
|
|||
return false;
|
||||
}
|
||||
|
||||
static void tmio_mmc_sdio_irq(int irq, void *devid)
|
||||
static void __tmio_mmc_sdio_irq(struct tmio_mmc_host *host)
|
||||
{
|
||||
struct tmio_mmc_host *host = devid;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
unsigned int ireg, status;
|
||||
|
@ -726,8 +732,8 @@ static void tmio_mmc_sdio_irq(int irq, void *devid)
|
|||
ireg = status & TMIO_SDIO_MASK_ALL & ~host->sdio_irq_mask;
|
||||
|
||||
sdio_status = status & ~TMIO_SDIO_MASK_ALL;
|
||||
if (pdata->flags & TMIO_MMC_SDIO_STATUS_QUIRK)
|
||||
sdio_status |= 6;
|
||||
if (pdata->flags & TMIO_MMC_SDIO_STATUS_SETBITS)
|
||||
sdio_status |= TMIO_SDIO_SETBITS_MASK;
|
||||
|
||||
sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status);
|
||||
|
||||
|
@ -754,7 +760,7 @@ irqreturn_t tmio_mmc_irq(int irq, void *devid)
|
|||
if (__tmio_mmc_sdcard_irq(host, ireg, status))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
tmio_mmc_sdio_irq(irq, devid);
|
||||
__tmio_mmc_sdio_irq(host);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -902,6 +908,12 @@ static int tmio_mmc_clk_enable(struct tmio_mmc_host *host)
|
|||
return host->clk_enable(host);
|
||||
}
|
||||
|
||||
static void tmio_mmc_clk_disable(struct tmio_mmc_host *host)
|
||||
{
|
||||
if (host->clk_disable)
|
||||
host->clk_disable(host);
|
||||
}
|
||||
|
||||
static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
@ -1145,7 +1157,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
|
|||
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret < 0)
|
||||
goto host_free;
|
||||
return ret;
|
||||
|
||||
_host->pdata = pdata;
|
||||
platform_set_drvdata(pdev, mmc);
|
||||
|
@ -1155,14 +1167,12 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
|
|||
|
||||
ret = tmio_mmc_init_ocr(_host);
|
||||
if (ret < 0)
|
||||
goto host_free;
|
||||
return ret;
|
||||
|
||||
_host->ctl = devm_ioremap(&pdev->dev,
|
||||
res_ctl->start, resource_size(res_ctl));
|
||||
if (!_host->ctl) {
|
||||
ret = -ENOMEM;
|
||||
goto host_free;
|
||||
}
|
||||
if (!_host->ctl)
|
||||
return -ENOMEM;
|
||||
|
||||
tmio_mmc_ops.card_busy = _host->card_busy;
|
||||
tmio_mmc_ops.start_signal_voltage_switch = _host->start_signal_voltage_switch;
|
||||
|
@ -1179,8 +1189,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
|
|||
|
||||
_host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
|
||||
mmc->caps & MMC_CAP_NEEDS_POLL ||
|
||||
!mmc_card_is_removable(mmc) ||
|
||||
mmc->slot.cd_irq >= 0);
|
||||
!mmc_card_is_removable(mmc));
|
||||
|
||||
/*
|
||||
* On Gen2+, eMMC with NONREMOVABLE currently fails because native
|
||||
|
@ -1200,10 +1209,8 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
|
|||
* Check the sanity of mmc->f_min to prevent tmio_mmc_set_clock() from
|
||||
* looping forever...
|
||||
*/
|
||||
if (mmc->f_min == 0) {
|
||||
ret = -EINVAL;
|
||||
goto host_free;
|
||||
}
|
||||
if (mmc->f_min == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* While using internal tmio hardware logic for card detection, we need
|
||||
|
@ -1232,7 +1239,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
|
|||
if (pdata->flags & TMIO_MMC_SDIO_IRQ) {
|
||||
_host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
|
||||
sd_ctrl_write16(_host, CTL_SDIO_IRQ_MASK, _host->sdio_irq_mask);
|
||||
sd_ctrl_write16(_host, CTL_TRANSACTION_CTL, 0x0000);
|
||||
sd_ctrl_write16(_host, CTL_TRANSACTION_CTL, 0x0001);
|
||||
}
|
||||
|
||||
spin_lock_init(&_host->lock);
|
||||
|
@ -1268,10 +1275,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
|
|||
}
|
||||
|
||||
return 0;
|
||||
|
||||
host_free:
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_host_probe);
|
||||
|
||||
|
@ -1280,6 +1283,9 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
|
|||
struct platform_device *pdev = host->pdev;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
if (host->pdata->flags & TMIO_MMC_SDIO_IRQ)
|
||||
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000);
|
||||
|
||||
if (!host->native_hotplug)
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
|
@ -1292,6 +1298,8 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
|
|||
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
tmio_mmc_clk_disable(host);
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_host_remove);
|
||||
|
||||
|
@ -1306,8 +1314,7 @@ int tmio_mmc_host_runtime_suspend(struct device *dev)
|
|||
if (host->clk_cache)
|
||||
tmio_mmc_clk_stop(host);
|
||||
|
||||
if (host->clk_disable)
|
||||
host->clk_disable(host);
|
||||
tmio_mmc_clk_disable(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/dma-mapping.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue