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:
Linus Torvalds 2017-02-21 12:04:54 -08:00
commit e67bd12d60
114 changed files with 2590 additions and 1817 deletions

View File

@ -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:

View 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>;
}

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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";
};

View File

@ -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"

View 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";
};

View File

@ -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;

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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

View File

@ -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"

View File

@ -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
View 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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -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);

View 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");

View File

@ -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);

View File

@ -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;

View File

@ -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
View 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);
}
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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 */

View File

@ -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

View File

@ -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);

View File

@ -25,6 +25,7 @@
#include <linux/of.h>
#include "core.h"
#include "card.h"
#include "sdio_cis.h"
#include "sdio_bus.h"

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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)
{

View File

@ -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;

View File

@ -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)
{

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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>

View 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");

View 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_ */

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -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");

View File

@ -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,

View File

@ -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;
}

View File

@ -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)

View File

@ -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)

View File

@ -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>

View File

@ -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;
}

View File

@ -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

View File

@ -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 */

View File

@ -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,
};

View File

@ -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;

View File

@ -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"))

View File

@ -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;

View File

@ -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;

View File

@ -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 */

View File

@ -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

View File

@ -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;

View File

@ -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>

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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