mirror of
https://github.com/hardkernel/linux.git
synced 2026-03-24 19:40:21 +09:00
odroid-xu3/xu4:emmc5.1 microSD R/W stability modify.
Change-Id: Ia482e3f1ad86ffac71057334bbc5afab27c4eb69
This commit is contained in:
31
arch/arm/boot/dts/exynos5422-odroidxu3.dts
Normal file → Executable file
31
arch/arm/boot/dts/exynos5422-odroidxu3.dts
Normal file → Executable file
@@ -41,21 +41,27 @@
|
||||
broken-cd;
|
||||
bypass-smu;
|
||||
fixed_volt;
|
||||
clock-gate;
|
||||
enable-cclk-on-suspend;
|
||||
only_once_tune;
|
||||
extra_tuning;
|
||||
supports-highspeed;
|
||||
supports-hs200-1-8v-mode;
|
||||
supports-ddr200-1-8v-mode;
|
||||
error-retry;
|
||||
enable-cache-control;
|
||||
supports-poweroff-notification;
|
||||
use-fine-tuning;
|
||||
supports-ddr200-enhanced-strobe;
|
||||
sw_data_timeout;
|
||||
fifo-depth = <0x40>;
|
||||
card-detect-delay = <200>;
|
||||
qos_int_level = <222000>;
|
||||
samsung,dw-mshc-ciu-div = <3>;
|
||||
samsung,dw-mshc-sdr-timing = <0 4 3>;
|
||||
samsung,dw-mshc-ddr-timing = <0 2 3>;
|
||||
samsung,dw-mshc-hs200-timing = <0 2 3>;
|
||||
samsung,dw-mshc-ddr200-timing = <0 2 2>;
|
||||
samsung,dw-mshc-sdr-timing = <3 0 4 0>;
|
||||
samsung,dw-mshc-ddr-timing = <3 0 2 0>;
|
||||
samsung,dw-mshc-hs200-timing = <3 0 2 0>;
|
||||
samsung,dw-mshc-ddr200-timing = <1 1 2 0>;
|
||||
vmmc-supply = <&buck10_reg>;
|
||||
vqmmc-supply = <&ldo18_reg>;
|
||||
clk_pin = "gpc0-0";
|
||||
@@ -77,23 +83,26 @@
|
||||
dwmmc2@12220000 {
|
||||
status = "okay";
|
||||
num-slots = <1>;
|
||||
extra_tuning;
|
||||
use-fine-tuning;
|
||||
supports-highspeed;
|
||||
cd-type = <1>;
|
||||
supports-highspeed;
|
||||
clock-gate;
|
||||
enable-cclk-on-suspend;
|
||||
sw_data_timeout;
|
||||
use-cpu-mode-tuning;
|
||||
use-fine-tuning;
|
||||
ignore-phase = <(1 << 7)>;
|
||||
fifo-depth = <0x40>;
|
||||
card-detect-delay = <200>;
|
||||
qos_int_level = <222000>;
|
||||
samsung,dw-mshc-ciu-div = <3>;
|
||||
samsung,dw-mshc-sdr-timing = <0 4 3>;
|
||||
samsung,dw-mshc-ddr-timing = <0 2 3>;
|
||||
samsung,dw-mshc-sdr-timing = <3 0 4 0>;
|
||||
samsung,dw-mshc-ddr-timing = <3 0 2 0>;
|
||||
vmmc-supply = <&ldo19_reg>;
|
||||
vqmmc-supply = <&ldo13_reg>;
|
||||
clk_pin = "gpc2-0";
|
||||
clk_addr = "13410000.pinctrl";
|
||||
clk_val = <0x3>;
|
||||
clk_str_num = <3>;
|
||||
clk_val = <0x1>;
|
||||
clk_str_num = <4>;
|
||||
num-ref-clks = <8>;
|
||||
ciu_clkin = <20812500 41625000 41625000 83250000 166500000 83250000 166500000 333000000>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
9
arch/arm/configs/odroidxu3_defconfig
Normal file → Executable file
9
arch/arm/configs/odroidxu3_defconfig
Normal file → Executable file
@@ -2574,10 +2574,11 @@ CONFIG_USB_G_ANDROID=y
|
||||
# CONFIG_USB_G_WEBCAM is not set
|
||||
CONFIG_MMC=y
|
||||
# CONFIG_MMC_DEBUG is not set
|
||||
CONFIG_MMC_SUPPORT_STLOG=y
|
||||
CONFIG_MMC_UNSAFE_RESUME=y
|
||||
CONFIG_MMC_CLKGATE=y
|
||||
CONFIG_MMC_EMBEDDED_SDIO=y
|
||||
CONFIG_MMC_PARANOID_SD_INIT=y
|
||||
# CONFIG_MMC_PARANOID_SD_INIT is not set
|
||||
|
||||
#
|
||||
# MMC/SD/SDIO Card Drivers
|
||||
@@ -2589,7 +2590,7 @@ CONFIG_MMC_SLOTINDEX=y
|
||||
# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set
|
||||
# CONFIG_SDIO_UART is not set
|
||||
# CONFIG_MMC_TEST is not set
|
||||
# CONFIG_MMC_SUPPORT_BKOPS_MODE is not set
|
||||
CONFIG_MMC_SUPPORT_BKOPS_MODE=y
|
||||
|
||||
#
|
||||
# MMC/SD/SDIO Host Controller Drivers
|
||||
@@ -2602,8 +2603,11 @@ CONFIG_MMC_DW=y
|
||||
CONFIG_MMC_DW_BYPASS_FMP=y
|
||||
# CONFIG_MMC_DW_FMP_DM_CRYPT is not set
|
||||
CONFIG_MMC_DW_IDMAC=y
|
||||
# CONFIG_MMC_DW_64_IDMAC is not set
|
||||
CONFIG_MMC_DW_PLTFM=y
|
||||
CONFIG_MMC_DW_EXYNOS=y
|
||||
# CONFIG_MMC_DW_DEBUG is not set
|
||||
# CONFIG_MMC_DW_SKIP_CACHE_OP is not set
|
||||
# CONFIG_MMC_VUB300 is not set
|
||||
# CONFIG_MMC_USHC is not set
|
||||
# CONFIG_MEMSTICK is not set
|
||||
@@ -3155,6 +3159,7 @@ CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
|
||||
CONFIG_PROC_FS=y
|
||||
CONFIG_PROC_SYSCTL=y
|
||||
CONFIG_PROC_PAGE_MONITOR=y
|
||||
CONFIG_PROC_STLOG=y
|
||||
CONFIG_SYSFS=y
|
||||
CONFIG_TMPFS=y
|
||||
CONFIG_TMPFS_POSIX_ACL=y
|
||||
|
||||
162
arch/arm/mach-exynos/include/mach/exynos-dwmci.h
Executable file
162
arch/arm/mach-exynos/include/mach/exynos-dwmci.h
Executable file
@@ -0,0 +1,162 @@
|
||||
/* arch/arm/mach-exynos/include/mach/exynos-dwmci.h
|
||||
*
|
||||
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Exynos dependent definitions for DesignWare MMC driver
|
||||
*
|
||||
* 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 _EXYNOS_DWMCI_H_
|
||||
#define _EXYNOS_DWMCI_H_
|
||||
|
||||
/*****************/
|
||||
/* SFR addresses */
|
||||
/*****************/
|
||||
|
||||
#define SFR_OFFSET 0x0004
|
||||
|
||||
#if defined(CONFIG_SOC_EXYNOS5433)
|
||||
|
||||
#define SDMMC_DBADDRL 0x0088
|
||||
#define SDMMC_DBADDRU (SDMMC_DBADDRL + SFR_OFFSET)
|
||||
#define SDMMC_IDSTS (SDMMC_DBADDRU + SFR_OFFSET)
|
||||
#define SDMMC_IDINTEN (SDMMC_IDSTS + SFR_OFFSET)
|
||||
#define SDMMC_DSCADDRL (SDMMC_IDINTEN + SFR_OFFSET)
|
||||
#define SDMMC_DSCADDRU (SDMMC_DSCADDRL + SFR_OFFSET)
|
||||
#define SDMMC_BUFADDR (SDMMC_DSCADDRU + SFR_OFFSET)
|
||||
#define SDMMC_BUFADDRU (SDMMC_BUFADDR + SFR_OFFSET)
|
||||
#define SDMMC_CLKSEL (SDMMC_BUFADDRU + SFR_OFFSET) /* specific to Samsung Exynos */
|
||||
|
||||
#define SDMMC_AXI_BURST_LEN 0x00b4
|
||||
#define SDMMC_SECTOR_NUM_INC 0x01F8
|
||||
|
||||
#else
|
||||
|
||||
#define SDMMC_DBADDR 0x0088
|
||||
#define SDMMC_IDSTS (SDMMC_DBADDR + SFR_OFFSET)
|
||||
#define SDMMC_IDINTEN (SDMMC_IDSTS + SFR_OFFSET)
|
||||
#define SDMMC_DSCADDR (SDMMC_IDINTEN + SFR_OFFSET)
|
||||
#define SDMMC_BUFADDR (SDMMC_DSCADDR + SFR_OFFSET)
|
||||
#define SDMMC_CLKSEL (SDMMC_BUFADDR + SFR_OFFSET) /* specific to Samsung Exynos */
|
||||
|
||||
#define SDMMC_AXI_BURST_LEN 0xffff /*not used*/
|
||||
#define SDMMC_SECTOR_NUM_INC 0xffff /*not used*/
|
||||
|
||||
#endif
|
||||
|
||||
#define SDMMC_CDTHRCTL 0x100
|
||||
#define SDMMC_DATA(x) (x)
|
||||
|
||||
#if defined(CONFIG_SOC_EXYNOS5422) || defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433)
|
||||
#define SDMMC_DDR200_ENABLE_SHIFT 0x110
|
||||
#define SDMMC_DDR200_RDDQS_EN 0x180
|
||||
#define SDMMC_DDR200_ASYNC_FIFO_CTRL 0x184
|
||||
#define SDMMC_DDR200_DLINE_CTRL 0x188
|
||||
#else
|
||||
#define SDMMC_DDR200_RDDQS_EN 0x110
|
||||
#define SDMMC_DDR200_ASYNC_FIFO_CTRL 0x114
|
||||
#define SDMMC_DDR200_DLINE_CTRL 0x118
|
||||
#endif
|
||||
|
||||
#define SDMMC_EMMCP_BASE 0x1000
|
||||
#define SDMMC_MPSTAT (SDMMC_EMMCP_BASE + 0x0008)
|
||||
#define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010)
|
||||
#define SDMMC_MPENCKEY (SDMMC_EMMCP_BASE + 0x0020)
|
||||
#define SDMMC_MPSBEGIN0 (SDMMC_EMMCP_BASE + 0x0200)
|
||||
#define SDMMC_MPSEND0 (SDMMC_EMMCP_BASE + 0x0204)
|
||||
#define SDMMC_MPSCTRL0 (SDMMC_EMMCP_BASE + 0x020C)
|
||||
#define SDMMC_MPSBEGIN1 (SDMMC_EMMCP_BASE + 0x0210)
|
||||
#define SDMMC_MPSEND1 (SDMMC_EMMCP_BASE + 0x0214)
|
||||
#define SDMMC_MPSCTRL1 (SDMMC_EMMCP_BASE + 0x021C)
|
||||
|
||||
/******************/
|
||||
/* Derived macros */
|
||||
/******************/
|
||||
/* SDMMC_CLKSEL */
|
||||
#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0)
|
||||
#define SDMMC_CLKSEL_CCLK_FINE_SAMPLE(x) (((x) & 0xF) << 0)
|
||||
#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16)
|
||||
#define SDMMC_CLKSEL_CCLK_FINE_DRIVE(x) (((x) & 3) << 22)
|
||||
#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
|
||||
#define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7)
|
||||
#define SDMMC_CLKSEL_GET_DIVRATIO(x) ((((x) >> 24) & 0x7) + 1)
|
||||
#define SDMMC_CLKSEL_TIMING(div, f_drv, drv, sample) \
|
||||
(SDMMC_CLKSEL_CCLK_DIVIDER(div) | \
|
||||
SDMMC_CLKSEL_CCLK_FINE_DRIVE(f_drv) | \
|
||||
SDMMC_CLKSEL_CCLK_DRIVE(drv) | \
|
||||
SDMMC_CLKSEL_CCLK_SAMPLE(sample))
|
||||
|
||||
/* SDMMC_DDR200_RDDQS_EN */
|
||||
#define DWMCI_TXDT_CRC_TIMER_FASTLIMIT(x) (((x) & 0xFF) << 16)
|
||||
#define DWMCI_TXDT_CRC_TIMER_INITVAL(x) (((x) & 0xFF) << 8)
|
||||
#define DWMCI_TXDT_CRC_TIMER_SET(x, y) (DWMCI_TXDT_CRC_TIMER_FASTLIMIT(x) | \
|
||||
DWMCI_TXDT_CRC_TIMER_INITVAL(y))
|
||||
#define DWMCI_AXI_NON_BLOCKING_WRITE BIT(7)
|
||||
#define DWMCI_RESP_RCLK_MODE BIT(5)
|
||||
#define DWMCI_BUSY_CHK_CLK_STOP_EN BIT(2)
|
||||
#define DWMCI_RXDATA_START_BIT_SEL BIT(1)
|
||||
#define DWMCI_RDDQS_EN BIT(0)
|
||||
#define DWMCI_DDR200_RDDQS_EN_DEF (DWMCI_TXDT_CRC_TIMER_FASTLIMIT(0x13) | \
|
||||
DWMCI_TXDT_CRC_TIMER_INITVAL(0x15))
|
||||
|
||||
/* SDMMC_DDR200_ASYNC_FIFO_CTRL */
|
||||
#define DWMCI_ASYNC_FIFO_RESET BIT(0)
|
||||
|
||||
/* SDMMC_DDR200_DLINE_CTRL */
|
||||
#define DWMCI_WD_DQS_DELAY_CTRL(x) (((x) & 0x3FF) << 20)
|
||||
#define DWMCI_FIFO_CLK_DELAY_CTRL(x) (((x) & 0x3) << 16)
|
||||
#define DWMCI_RD_DQS_DELAY_CTRL(x) ((x) & 0x3FF)
|
||||
#define DWMCI_DDR200_DLINE_CTRL_SET(x, y, z) (DWMCI_WD_DQS_DELAY_CTRL(x) | \
|
||||
DWMCI_FIFO_CLK_DELAY_CTRL(y) | \
|
||||
DWMCI_RD_DQS_DELAY_CTRL(z))
|
||||
#define DWMCI_DDR200_DLINE_CTRL_DEF (DWMCI_FIFO_CLK_DELAY_CTRL(0x2) | \
|
||||
DWMCI_RD_DQS_DELAY_CTRL(0x40))
|
||||
|
||||
/* SDMMC_SECTOR_NUM_INC */
|
||||
#define DWMCI_BURST_LENGTH_MASK (0xF)
|
||||
#define DWMCI_BURST_LENGTH_CTRL(x) (((x)&DWMCI_BURST_LENGTH_MASK) | \
|
||||
(((x)&DWMCI_BURST_LENGTH_MASK)<<16))
|
||||
|
||||
/* SDMMC_SECTOR_NUM_INC */
|
||||
#define DWMCI_SECTOR_SIZE_MASK (0x1FFF)
|
||||
#define DWMCI_SECTOR_SIZE_CTRL(x) ((x)&DWMCI_SECTOR_SIZE_MASK)
|
||||
|
||||
/* SDMMC_DDR200_ENABLE_SHIFT */
|
||||
#define DWMCI_ENABLE_SHIFT_MASK (0x3)
|
||||
#define DWMCI_ENABLE_SHIFT(x) ((x) & DWMCI_ENABLE_SHIFT_MASK)
|
||||
|
||||
/* Block number in eMMC */
|
||||
#define DWMCI_BLOCK_NUM 0xFFFFFFFF
|
||||
|
||||
/* SMU control bits */
|
||||
#define DWMCI_MPSCTRL_SECURE_READ_BIT BIT(7)
|
||||
#define DWMCI_MPSCTRL_SECURE_WRITE_BIT BIT(6)
|
||||
#define DWMCI_MPSCTRL_NON_SECURE_READ_BIT BIT(5)
|
||||
#define DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT BIT(4)
|
||||
#define DWMCI_MPSCTRL_USE_FUSE_KEY BIT(3)
|
||||
#define DWMCI_MPSCTRL_ECB_MODE BIT(2)
|
||||
#define DWMCI_MPSCTRL_ENCRYPTION BIT(1)
|
||||
#define DWMCI_MPSCTRL_VALID BIT(0)
|
||||
|
||||
#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
|
||||
#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
|
||||
|
||||
/* FMP configuration */
|
||||
#if defined(CONFIG_SOC_EXYNOS5433)
|
||||
#define DW_MMC_BYPASS_SECTOR_BEGIN 0x80000000
|
||||
#define DW_MMC_BYPASS_SECTOR_END 0xFFFFFFFF
|
||||
#define DW_MMC_ENCRYPTION_SECTOR_BEGIN 0
|
||||
#define DW_MMC_ENCRYPTION_SECTOR_END 0x7FFFFFFF
|
||||
#else
|
||||
#define DW_MMC_BYPASS_SECTOR_BEGIN 0
|
||||
#define DW_MMC_BYPASS_SECTOR_END 0
|
||||
#define DW_MMC_ENCRYPTION_SECTOR_BEGIN 1
|
||||
#define DW_MMC_ENCRYPTION_SECTOR_END 0xFFFFFFFF
|
||||
#endif
|
||||
|
||||
#endif /* _EXYNOS_DWMCI_H_ */
|
||||
|
||||
5
drivers/mmc/Kconfig
Normal file → Executable file
5
drivers/mmc/Kconfig
Normal file → Executable file
@@ -19,6 +19,11 @@ config MMC_DEBUG
|
||||
This is an option for use by developers; most people should
|
||||
say N here. This enables MMC core and driver debugging.
|
||||
|
||||
config MMC_SUPPORT_STLOG
|
||||
bool "Enable storage log"
|
||||
depends on MMC && PROC_STLOG
|
||||
default y
|
||||
|
||||
if MMC
|
||||
|
||||
source "drivers/mmc/core/Kconfig"
|
||||
|
||||
274
drivers/mmc/card/block.c
Normal file → Executable file
274
drivers/mmc/card/block.c
Normal file → Executable file
@@ -46,6 +46,8 @@
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "../core/core.h"
|
||||
|
||||
#if defined(CONFIG_MACH_ODROIDXU3)
|
||||
#include <asm/uaccess.h>
|
||||
#include <mach/regs-pmu.h>
|
||||
@@ -69,10 +71,9 @@ MODULE_ALIAS("mmc:block");
|
||||
#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_BLK_TIMEOUT_MS (20 * 1000) /* 20 sec timeout */
|
||||
|
||||
#define mmc_req_rel_wr(req) (((req->cmd_flags & REQ_FUA) || \
|
||||
(req->cmd_flags & REQ_META)) && \
|
||||
#define mmc_req_rel_wr(req) ((req->cmd_flags & REQ_FUA) && \
|
||||
(rq_data_dir(req) == WRITE))
|
||||
#define PACKED_CMD_VER 0x01
|
||||
#define PACKED_CMD_WR 0x02
|
||||
@@ -416,6 +417,45 @@ static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status,
|
||||
return err;
|
||||
}
|
||||
|
||||
struct scatterlist *mmc_blk_get_sg(struct mmc_card *card,
|
||||
unsigned char *buf, int *sg_len, int size)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
struct scatterlist *sl;
|
||||
int total_sec_cnt, sec_cnt;
|
||||
int max_seg_size, len;
|
||||
|
||||
total_sec_cnt = size;
|
||||
max_seg_size = card->host->max_seg_size;
|
||||
len = (size - 1 + max_seg_size) / max_seg_size;
|
||||
sl = kmalloc(sizeof(struct scatterlist) * len, GFP_KERNEL);
|
||||
|
||||
if (!sl) {
|
||||
return NULL;
|
||||
}
|
||||
sg = (struct scatterlist *)sl;
|
||||
sg_init_table(sg, len);
|
||||
|
||||
while (total_sec_cnt) {
|
||||
if (total_sec_cnt < max_seg_size)
|
||||
sec_cnt = total_sec_cnt;
|
||||
else
|
||||
sec_cnt = max_seg_size;
|
||||
|
||||
sg_set_page(sg, virt_to_page(buf), sec_cnt, offset_in_page(buf));
|
||||
buf = buf + sec_cnt;
|
||||
total_sec_cnt = total_sec_cnt - sec_cnt;
|
||||
if (total_sec_cnt == 0)
|
||||
break;
|
||||
sg = sg_next(sg);
|
||||
}
|
||||
|
||||
if (sg)
|
||||
sg_mark_end(sg);
|
||||
*sg_len = len;
|
||||
return sl;
|
||||
}
|
||||
|
||||
static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
struct mmc_ioc_cmd __user *ic_ptr)
|
||||
{
|
||||
@@ -425,8 +465,8 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct scatterlist sg;
|
||||
int err;
|
||||
struct scatterlist *sg = 0;
|
||||
int err = 0;
|
||||
int is_rpmb = false;
|
||||
u32 status = 0;
|
||||
|
||||
@@ -462,12 +502,13 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
cmd.flags = idata->ic.flags;
|
||||
|
||||
if (idata->buf_bytes) {
|
||||
data.sg = &sg;
|
||||
data.sg_len = 1;
|
||||
int len;
|
||||
data.blksz = idata->ic.blksz;
|
||||
data.blocks = idata->ic.blocks;
|
||||
|
||||
sg_init_one(data.sg, idata->buf, idata->buf_bytes);
|
||||
sg = mmc_blk_get_sg(card, idata->buf, &len, idata->buf_bytes);
|
||||
data.sg = sg;
|
||||
data.sg_len = len;
|
||||
|
||||
if (idata->ic.write_flag)
|
||||
data.flags = MMC_DATA_WRITE;
|
||||
@@ -570,6 +611,9 @@ cmd_rel_host:
|
||||
|
||||
cmd_done:
|
||||
mmc_blk_put(md);
|
||||
if (sg)
|
||||
kfree(sg);
|
||||
|
||||
cmd_err:
|
||||
kfree(idata->buf);
|
||||
kfree(idata);
|
||||
@@ -579,9 +623,28 @@ cmd_err:
|
||||
static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct mmc_blk_data *md = bdev->bd_disk->private_data;
|
||||
struct mmc_card *card = md->queue.card;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (cmd == MMC_IOC_CMD)
|
||||
ret = mmc_blk_ioctl_cmd(bdev, (struct mmc_ioc_cmd __user *)arg);
|
||||
else if(cmd == MMC_IOC_CLOCK)
|
||||
{
|
||||
unsigned int clock = (unsigned int)arg;
|
||||
if( clock < card->host->f_min )
|
||||
clock = card->host->f_min;
|
||||
|
||||
mmc_set_clock(card->host, clock);
|
||||
ret = 0;
|
||||
}
|
||||
else if(cmd == MMC_IOC_BUSWIDTH)
|
||||
{
|
||||
unsigned int width = (unsigned int)arg;
|
||||
mmc_set_bus_width(card->host, width);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -810,6 +873,34 @@ static int mmc_blk_cmd_error(struct request *req, const char *name, int error,
|
||||
}
|
||||
}
|
||||
|
||||
static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host,
|
||||
int type)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (md->reset_done & type)
|
||||
return -EEXIST;
|
||||
|
||||
md->reset_done |= type;
|
||||
err = mmc_hw_reset(host);
|
||||
/* Ensure we switch back to the correct partition */
|
||||
if (err != -EOPNOTSUPP) {
|
||||
struct mmc_blk_data *main_md = mmc_get_drvdata(host->card);
|
||||
int part_err;
|
||||
|
||||
main_md->part_curr = main_md->part_type;
|
||||
part_err = mmc_blk_part_switch(host->card, md);
|
||||
if (part_err) {
|
||||
/*
|
||||
* We have failed to get back into the correct
|
||||
* partition, so we need to abort the whole request.
|
||||
*/
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initial r/w and stop cmd error recovery.
|
||||
* We don't know whether the card received the r/w cmd or not, so try to
|
||||
@@ -831,6 +922,7 @@ static int mmc_blk_cmd_error(struct request *req, const char *name, int error,
|
||||
static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
|
||||
struct mmc_blk_request *brq, int *ecc_err)
|
||||
{
|
||||
struct mmc_command *cmd = &brq->cmd;
|
||||
bool prev_cmd_status_valid = true;
|
||||
u32 status, stop_status = 0;
|
||||
int err, retry;
|
||||
@@ -898,6 +990,43 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
|
||||
return mmc_blk_cmd_error(req, "r/w cmd", brq->cmd.error,
|
||||
prev_cmd_status_valid, status);
|
||||
|
||||
/* eMMC read Data timeout */
|
||||
if (mmc_card_mmc(card) &&
|
||||
(card->quirks & MMC_QUIRK_SEC_HW_RESET) &&
|
||||
(cmd->data->flags & MMC_DATA_READ) &&
|
||||
(cmd->data->error == -ETIMEDOUT) &&
|
||||
(R1_CURRENT_STATE(status) == R1_STATE_TRAN)) {
|
||||
struct mmc_blk_data *md = mmc_get_drvdata(card);
|
||||
int type = rq_data_dir(req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE;
|
||||
int ret = 0;
|
||||
|
||||
/* eMMC reset when read data timeout occurs */
|
||||
pr_err("%s: eMMC card soft-reset: cmd error %d, data error %d, status = %#x.\n",
|
||||
req->rq_disk->disk_name,
|
||||
cmd->error, cmd->data->error, status);
|
||||
|
||||
/* enable MMC_CAP_HW_RESET */
|
||||
card->host->caps |= MMC_CAP_HW_RESET;
|
||||
mdelay(10);
|
||||
|
||||
if (mmc_blk_reset(md, card->host, type)) {
|
||||
pr_err("%s: softreset is failed.\n",
|
||||
mmc_hostname(card->host));
|
||||
ret = ERR_ABORT;
|
||||
} else {
|
||||
card->host->blk_reset_cnt++;
|
||||
pr_err("%s: softreset is ok. command retrying.\n",
|
||||
mmc_hostname(card->host));
|
||||
ret = ERR_RETRY;
|
||||
}
|
||||
|
||||
/*
|
||||
* disable MMC_CAP_HW_RESET and return
|
||||
*/
|
||||
card->host->caps &= ~MMC_CAP_HW_RESET;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Data errors */
|
||||
if (!brq->stop.error)
|
||||
return ERR_CONTINUE;
|
||||
@@ -918,34 +1047,6 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
|
||||
return ERR_CONTINUE;
|
||||
}
|
||||
|
||||
static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host,
|
||||
int type)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (md->reset_done & type)
|
||||
return -EEXIST;
|
||||
|
||||
md->reset_done |= type;
|
||||
err = mmc_hw_reset(host);
|
||||
/* Ensure we switch back to the correct partition */
|
||||
if (err != -EOPNOTSUPP) {
|
||||
struct mmc_blk_data *main_md = mmc_get_drvdata(host->card);
|
||||
int part_err;
|
||||
|
||||
main_md->part_curr = main_md->part_type;
|
||||
part_err = mmc_blk_part_switch(host->card, md);
|
||||
if (part_err) {
|
||||
/*
|
||||
* We have failed to get back into the correct
|
||||
* partition, so we need to abort the whole request.
|
||||
*/
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void mmc_blk_reset_success(struct mmc_blk_data *md, int type)
|
||||
{
|
||||
md->reset_done &= ~type;
|
||||
@@ -1191,6 +1292,19 @@ static int mmc_blk_err_check(struct mmc_card *card,
|
||||
return MMC_BLK_CMD_ERR;
|
||||
}
|
||||
|
||||
if (status & CMD_ERRORS) {
|
||||
pr_err("%s: command error reported, status = %#x\n",
|
||||
req->rq_disk->disk_name, status);
|
||||
if (!(status & R1_WP_VIOLATION))
|
||||
brq->data.error = -EIO;
|
||||
if ((R1_CURRENT_STATE(status) == R1_STATE_RCV) ||
|
||||
(R1_CURRENT_STATE(status) == R1_STATE_DATA)) {
|
||||
err = send_stop(card, &status);
|
||||
if (err)
|
||||
return MMC_BLK_ABORT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Timeout if the device never becomes ready for data
|
||||
* and never leaves the program state.
|
||||
*/
|
||||
@@ -1319,8 +1433,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||
* XXX: this really needs a good explanation of why REQ_META
|
||||
* is treated special.
|
||||
*/
|
||||
bool do_rel_wr = ((req->cmd_flags & REQ_FUA) ||
|
||||
(req->cmd_flags & REQ_META)) &&
|
||||
bool do_rel_wr = (req->cmd_flags & REQ_FUA) &&
|
||||
(rq_data_dir(req) == WRITE) &&
|
||||
(md->flags & MMC_BLK_REL_WR);
|
||||
|
||||
@@ -1387,6 +1500,9 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||
brq->data.flags |= MMC_DATA_WRITE;
|
||||
}
|
||||
|
||||
if (req->cmd_flags & REQ_KERNEL)
|
||||
brq->data.flags |= MMC_DATA_DIRECT;
|
||||
|
||||
if (do_rel_wr)
|
||||
mmc_apply_rel_rw(brq, card, req);
|
||||
|
||||
@@ -1900,6 +2016,8 @@ 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;
|
||||
/* clear reset flags */
|
||||
mmc_blk_reset_success(md, type);
|
||||
goto cmd_abort;
|
||||
case MMC_BLK_DATA_ERR: {
|
||||
int err;
|
||||
@@ -1920,6 +2038,15 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
||||
disable_multi = 1;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* case : SDcard Sector 0 read data error even single read
|
||||
* skip reading other blocks.
|
||||
*/
|
||||
if (mmc_card_sd(card) &&
|
||||
(unsigned)blk_rq_pos(req) == 0 &&
|
||||
brq->data.error)
|
||||
goto cmd_abort;
|
||||
|
||||
/*
|
||||
* After an error, we redo I/O one sector at a
|
||||
* time, so we only reach here after trying to
|
||||
@@ -1967,9 +2094,14 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
||||
} else {
|
||||
if (mmc_card_removed(card))
|
||||
req->cmd_flags |= REQ_QUIET;
|
||||
while (ret)
|
||||
ret = blk_end_request(req, -EIO,
|
||||
blk_rq_cur_bytes(req));
|
||||
if (mmc_card_sd(card) && mmc_card_removed(card)) {
|
||||
if (ret)
|
||||
blk_end_request_all(req, -EIO);
|
||||
} else {
|
||||
while (ret)
|
||||
ret = blk_end_request(req, -EIO,
|
||||
blk_rq_cur_bytes(req));
|
||||
}
|
||||
}
|
||||
|
||||
start_new_req:
|
||||
@@ -2322,7 +2454,8 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
||||
if (mmc_host_cmd23(card->host)) {
|
||||
if (mmc_card_mmc(card) ||
|
||||
(mmc_card_sd(card) &&
|
||||
card->scr.cmds & SD_SCR_CMD23_SUPPORT))
|
||||
card->scr.cmds & SD_SCR_CMD23_SUPPORT &&
|
||||
mmc_sd_card_uhs(card)))
|
||||
md->flags |= MMC_BLK_CMD23;
|
||||
}
|
||||
|
||||
@@ -2515,6 +2648,11 @@ force_ro_fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define CID_MANFID_SANDISK 0x2
|
||||
#define CID_MANFID_TOSHIBA 0x11
|
||||
#define CID_MANFID_MICRON 0x13
|
||||
#define CID_MANFID_SAMSUNG 0x15
|
||||
|
||||
static const struct mmc_fixup blk_fixups[] =
|
||||
{
|
||||
MMC_FIXUP("SEM02G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
@@ -2528,6 +2666,8 @@ static const struct mmc_fixup blk_fixups[] =
|
||||
MMC_FIXUP("SEM32G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
|
||||
MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_HW_RESET),
|
||||
/*
|
||||
* Some MMC cards experience performance degradation with CMD23
|
||||
* instead of CMD12-bounded multiblock transfers. For now we'll
|
||||
@@ -2571,6 +2711,15 @@ static const struct mmc_fixup blk_fixups[] =
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
/* Samsung MoviNAND ePOP */
|
||||
MMC_FIXUP("R3W00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_HW_RESET),
|
||||
MMC_FIXUP("R3W9MA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_HW_RESET),
|
||||
MMC_FIXUP("R2W9MA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_HW_RESET),
|
||||
MMC_FIXUP("R2WAMA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_HW_RESET),
|
||||
|
||||
END_FIXUP
|
||||
};
|
||||
@@ -2644,9 +2793,10 @@ static inline void mmc_blk_bkops_sysfs_init(struct mmc_card *card)
|
||||
card->bkops_attr.attr.name = "bkops_en";
|
||||
card->bkops_attr.attr.mode = S_IRUGO | S_IWUSR;
|
||||
|
||||
if (device_create_file((disk_to_dev(md->disk)), &card->bkops_attr))
|
||||
if (device_create_file((disk_to_dev(md->disk)), &card->bkops_attr)) {
|
||||
pr_err("%s: Failed to create bkops_en sysfs entry\n",
|
||||
mmc_hostname(card->host));
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void mmc_blk_bkops_sysfs_init(struct mmc_card *card)
|
||||
@@ -2682,7 +2832,8 @@ static int mmc_blk_probe(struct mmc_card *card)
|
||||
mmc_fixup_device(card, blk_fixups);
|
||||
|
||||
#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
|
||||
mmc_set_bus_resume_policy(card->host, 1);
|
||||
if (card && mmc_card_mmc(card))
|
||||
mmc_set_bus_resume_policy(card->host, 1);
|
||||
#endif
|
||||
if (mmc_add_disk(md))
|
||||
goto out;
|
||||
@@ -2721,21 +2872,47 @@ static void mmc_blk_remove(struct mmc_card *card)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mmc_blk_suspend(struct mmc_card *card)
|
||||
static int mmc_blk_shutdown(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_blk_data *part_md;
|
||||
struct mmc_blk_data *md = mmc_get_drvdata(card);
|
||||
|
||||
if (md) {
|
||||
mmc_queue_suspend(&md->queue);
|
||||
mmc_queue_suspend(&md->queue, 1);
|
||||
list_for_each_entry(part_md, &md->part, part) {
|
||||
mmc_queue_suspend(&part_md->queue);
|
||||
mmc_queue_suspend(&part_md->queue, 1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mmc_blk_suspend(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_blk_data *part_md;
|
||||
struct mmc_blk_data *md = mmc_get_drvdata(card);
|
||||
int rc = 0;
|
||||
|
||||
if (md) {
|
||||
rc = mmc_queue_suspend(&md->queue, 0);
|
||||
if (rc)
|
||||
goto out;
|
||||
list_for_each_entry(part_md, &md->part, part) {
|
||||
rc = mmc_queue_suspend(&part_md->queue, 0);
|
||||
if (rc)
|
||||
goto out_resume;
|
||||
}
|
||||
}
|
||||
goto out;
|
||||
out_resume:
|
||||
mmc_queue_resume(&md->queue);
|
||||
list_for_each_entry(part_md, &md->part, part) {
|
||||
mmc_queue_resume(&part_md->queue);
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int mmc_blk_resume(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_blk_data *part_md;
|
||||
@@ -2767,6 +2944,7 @@ static struct mmc_driver mmc_driver = {
|
||||
.remove = mmc_blk_remove,
|
||||
.suspend = mmc_blk_suspend,
|
||||
.resume = mmc_blk_resume,
|
||||
.shutdown = mmc_blk_shutdown,
|
||||
};
|
||||
|
||||
static int __init mmc_blk_init(void)
|
||||
|
||||
41
drivers/mmc/card/queue.c
Normal file → Executable file
41
drivers/mmc/card/queue.c
Normal file → Executable file
@@ -20,6 +20,8 @@
|
||||
#include <linux/mmc/host.h>
|
||||
#include "queue.h"
|
||||
|
||||
#include <asm/topology.h>
|
||||
|
||||
#define MMC_QUEUE_BOUNCESZ 65536
|
||||
|
||||
/*
|
||||
@@ -98,6 +100,9 @@ static int mmc_queue_thread(void *d)
|
||||
|
||||
current->flags |= PF_MEMALLOC;
|
||||
|
||||
#if defined(CONFIG_SOC_EXYNOS5430) || defined(CONFIG_SOC_EXYNOS5433)
|
||||
set_cpus_allowed_ptr(current, cpu_coregroup_mask(0));
|
||||
#endif
|
||||
down(&mq->thread_sem);
|
||||
do {
|
||||
struct request *req = NULL;
|
||||
@@ -541,10 +546,12 @@ void mmc_packed_clean(struct mmc_queue *mq)
|
||||
* complete any outstanding requests. This ensures that we
|
||||
* won't suspend while a request is being processed.
|
||||
*/
|
||||
void mmc_queue_suspend(struct mmc_queue *mq)
|
||||
int mmc_queue_suspend(struct mmc_queue *mq, int wait)
|
||||
{
|
||||
struct request_queue *q = mq->queue;
|
||||
struct request *req;
|
||||
unsigned long flags;
|
||||
int rc = 0;
|
||||
|
||||
if (!(mq->flags & MMC_QUEUE_SUSPENDED)) {
|
||||
mq->flags |= MMC_QUEUE_SUSPENDED;
|
||||
@@ -553,8 +560,38 @@ void mmc_queue_suspend(struct mmc_queue *mq)
|
||||
blk_stop_queue(q);
|
||||
spin_unlock_irqrestore(q->queue_lock, flags);
|
||||
|
||||
down(&mq->thread_sem);
|
||||
rc = down_trylock(&mq->thread_sem);
|
||||
if (rc && !wait) {
|
||||
mq->flags &= ~MMC_QUEUE_SUSPENDED;
|
||||
spin_lock_irqsave(q->queue_lock, flags);
|
||||
blk_start_queue(q);
|
||||
spin_unlock_irqrestore(q->queue_lock, flags);
|
||||
rc = -EBUSY;
|
||||
} else if (wait) {
|
||||
printk("%s: mq->flags: %x, q->queue_flags: 0x%lx, \
|
||||
q->in_flight (%d, %d) \n",
|
||||
mmc_hostname(mq->card->host), mq->flags,
|
||||
q->queue_flags, q->in_flight[0], q->in_flight[1]);
|
||||
mutex_lock(&q->sysfs_lock);
|
||||
queue_flag_set_unlocked(QUEUE_FLAG_DYING, q);
|
||||
|
||||
spin_lock_irqsave(q->queue_lock, flags);
|
||||
queue_flag_set(QUEUE_FLAG_DYING, q);
|
||||
|
||||
while ((req = blk_fetch_request(q)) != NULL) {
|
||||
req->cmd_flags |= REQ_QUIET;
|
||||
__blk_end_request_all(req, -EIO);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(q->queue_lock, flags);
|
||||
mutex_unlock(&q->sysfs_lock);
|
||||
if (rc) {
|
||||
down(&mq->thread_sem);
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
2
drivers/mmc/card/queue.h
Normal file → Executable file
2
drivers/mmc/card/queue.h
Normal file → Executable file
@@ -68,7 +68,7 @@ struct mmc_queue {
|
||||
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
|
||||
const char *);
|
||||
extern void mmc_cleanup_queue(struct mmc_queue *);
|
||||
extern void mmc_queue_suspend(struct mmc_queue *);
|
||||
extern int mmc_queue_suspend(struct mmc_queue *, int);
|
||||
extern void mmc_queue_resume(struct mmc_queue *);
|
||||
|
||||
extern unsigned int mmc_queue_map_sg(struct mmc_queue *,
|
||||
|
||||
87
drivers/mmc/core/core.c
Normal file → Executable file
87
drivers/mmc/core/core.c
Normal file → Executable file
@@ -45,6 +45,12 @@
|
||||
#include "sd_ops.h"
|
||||
#include "sdio_ops.h"
|
||||
|
||||
#ifdef CONFIG_MMC_SUPPORT_STLOG
|
||||
#include <linux/stlog.h>
|
||||
#else
|
||||
#define ST_LOG(fmt,...)
|
||||
#endif
|
||||
|
||||
/* If the device is not responding */
|
||||
#define MMC_CORE_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
|
||||
|
||||
@@ -55,7 +61,7 @@
|
||||
#define MMC_BKOPS_MAX_TIMEOUT (4 * 60 * 1000) /* max time to wait in ms */
|
||||
|
||||
static struct workqueue_struct *workqueue;
|
||||
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
|
||||
static const unsigned freqs[] = { 400000, 300000 };
|
||||
|
||||
/*
|
||||
* Enabling software CRCs on the data blocks can be a significant (30%)
|
||||
@@ -490,8 +496,12 @@ EXPORT_SYMBOL(mmc_start_bkops);
|
||||
*/
|
||||
static void mmc_wait_data_done(struct mmc_request *mrq)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mrq->host->context_info.lock, flags);
|
||||
mrq->host->context_info.is_done_rcv = true;
|
||||
wake_up_interruptible(&mrq->host->context_info.wait);
|
||||
spin_unlock_irqrestore(&mrq->host->context_info.lock, flags);
|
||||
}
|
||||
|
||||
static void mmc_wait_done(struct mmc_request *mrq)
|
||||
@@ -681,13 +691,19 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
|
||||
context_info->is_new_req));
|
||||
spin_lock_irqsave(&context_info->lock, flags);
|
||||
context_info->is_waiting_last_req = false;
|
||||
spin_unlock_irqrestore(&context_info->lock, flags);
|
||||
if (context_info->is_done_rcv) {
|
||||
context_info->is_done_rcv = false;
|
||||
context_info->is_new_req = false;
|
||||
spin_unlock_irqrestore(&context_info->lock, flags);
|
||||
cmd = mrq->cmd;
|
||||
if (!cmd->error || !cmd->retries ||
|
||||
mmc_card_removed(host->card)) {
|
||||
if (mrq->sbc && (mrq->sbc->error == -ETIMEDOUT) && mrq->sbc->retries) {
|
||||
mrq->sbc->retries--;
|
||||
mrq->sbc->error = 0;
|
||||
mmc_host_clk_hold(host);
|
||||
host->ops->request(host, mrq);
|
||||
continue;
|
||||
} else if (!cmd->error || !cmd->retries ||
|
||||
mmc_card_removed(host->card)) {
|
||||
err = host->areq->err_check(host->card,
|
||||
host->areq);
|
||||
break; /* return err */
|
||||
@@ -703,11 +719,14 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
|
||||
} else if (context_info->is_new_req) {
|
||||
context_info->is_new_req = false;
|
||||
if (!next_req) {
|
||||
spin_unlock_irqrestore(&context_info->lock,
|
||||
flags);
|
||||
err = MMC_BLK_NEW_REQUEST;
|
||||
break; /* return err */
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&context_info->lock, flags);
|
||||
} /* while */
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -883,6 +902,11 @@ EXPORT_SYMBOL(mmc_start_req);
|
||||
*/
|
||||
void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
|
||||
if (mmc_bus_needs_resume(host))
|
||||
mmc_resume_bus(host);
|
||||
#endif
|
||||
|
||||
__mmc_start_req(host, mrq);
|
||||
mmc_wait_for_req_done(host, mrq);
|
||||
}
|
||||
@@ -909,7 +933,7 @@ int mmc_interrupt_hpi(struct mmc_card *card)
|
||||
}
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
err = mmc_send_status(card, &status);
|
||||
err = mmc_send_status(card, &status, 0);
|
||||
if (err) {
|
||||
pr_err("%s: Get card status fail\n", mmc_hostname(card->host));
|
||||
goto out;
|
||||
@@ -941,7 +965,7 @@ int mmc_interrupt_hpi(struct mmc_card *card)
|
||||
|
||||
prg_wait = jiffies + msecs_to_jiffies(card->ext_csd.out_of_int_time);
|
||||
do {
|
||||
err = mmc_send_status(card, &status);
|
||||
err = mmc_send_status(card, &status, 0);
|
||||
|
||||
if (!err && R1_CURRENT_STATE(status) == R1_STATE_TRAN)
|
||||
break;
|
||||
@@ -1660,6 +1684,10 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
|
||||
|
||||
BUG_ON(!host);
|
||||
|
||||
/* Some devices use only dedicated specific signaling level for
|
||||
* design reasons. MMC_CAP2_BROKEN_VOLTAGE flag is used when
|
||||
* there is no needs to change to any signaling level.
|
||||
*/
|
||||
if (host->caps2 & MMC_CAP2_BROKEN_VOLTAGE)
|
||||
return 0;
|
||||
|
||||
@@ -1802,6 +1830,7 @@ static void mmc_power_up(struct mmc_host *host)
|
||||
host->ios.power_mode = MMC_POWER_UP;
|
||||
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
||||
host->ios.timing = MMC_TIMING_LEGACY;
|
||||
host->ios.clock = host->f_init;
|
||||
mmc_set_ios(host);
|
||||
|
||||
/* Set signal voltage to 3.3V */
|
||||
@@ -1928,11 +1957,10 @@ int mmc_resume_bus(struct mmc_host *host)
|
||||
mmc_power_up(host);
|
||||
BUG_ON(!host->bus_ops->resume);
|
||||
host->bus_ops->resume(host);
|
||||
if (host->bus_ops->detect)
|
||||
host->bus_ops->detect(host);
|
||||
}
|
||||
|
||||
if (host->bus_ops->detect && !host->bus_dead)
|
||||
host->bus_ops->detect(host);
|
||||
|
||||
mmc_bus_put(host);
|
||||
printk("%s: Deferred resume completed\n", mmc_hostname(host));
|
||||
return 0;
|
||||
@@ -2341,6 +2369,16 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
|
||||
if (to <= from)
|
||||
return -EINVAL;
|
||||
|
||||
/* to set the address in 16k (32sectors) */
|
||||
if(arg == MMC_TRIM_ARG) {
|
||||
if ((from % 32) != 0)
|
||||
from = ((from >> 5) + 1) << 5;
|
||||
|
||||
to = (to >> 5) << 5;
|
||||
if (from >= to)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 'from' and 'to' are inclusive */
|
||||
to -= 1;
|
||||
|
||||
@@ -2379,6 +2417,9 @@ EXPORT_SYMBOL(mmc_can_discard);
|
||||
|
||||
int mmc_can_sanitize(struct mmc_card *card)
|
||||
{
|
||||
/* do not use sanitize*/
|
||||
return 0;
|
||||
|
||||
if (!mmc_can_trim(card) && !mmc_can_erase(card))
|
||||
return 0;
|
||||
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_SANITIZE)
|
||||
@@ -2523,6 +2564,9 @@ int mmc_can_reset(struct mmc_card *card)
|
||||
{
|
||||
u8 rst_n_function;
|
||||
|
||||
if (card->host->caps & MMC_CAP_HW_RESET)
|
||||
return 1;
|
||||
|
||||
if (!mmc_card_mmc(card))
|
||||
return 0;
|
||||
rst_n_function = card->ext_csd.rst_n_function;
|
||||
@@ -2549,6 +2593,15 @@ static int mmc_do_hw_reset(struct mmc_host *host, int check)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mmc_host_clk_hold(host);
|
||||
|
||||
if (!check && mmc_card_mmc(card)) {
|
||||
/*
|
||||
* f_min is needed to change max 400KHz
|
||||
* before mmc_blk_reset, eMMC bus_hz is 400MHz
|
||||
*/
|
||||
host->f_min = 400000;
|
||||
}
|
||||
|
||||
mmc_set_clock(host, host->f_init);
|
||||
|
||||
host->ops->hw_reset(host);
|
||||
@@ -2666,6 +2719,7 @@ int _mmc_detect_card_removed(struct mmc_host *host)
|
||||
if (ret) {
|
||||
mmc_card_set_removed(host->card);
|
||||
pr_debug("%s: card remove detected\n", mmc_hostname(host));
|
||||
ST_LOG("<%s> %s: card remove detected\n", __func__,mmc_hostname(host));
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -2681,6 +2735,11 @@ int mmc_detect_card_removed(struct mmc_host *host)
|
||||
if (!card)
|
||||
return 1;
|
||||
|
||||
/* Check : SDcard is removed physically */
|
||||
if (host->card && mmc_card_sd(host->card) &&
|
||||
host->ops->get_cd && host->ops->get_cd(host) == 0)
|
||||
mmc_card_set_removed(host->card);
|
||||
|
||||
ret = mmc_card_removed(card);
|
||||
/*
|
||||
* The card will be considered unchanged unless we have been asked to
|
||||
@@ -2766,6 +2825,7 @@ void mmc_rescan(struct work_struct *work)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ST_LOG("<%s> %s insertion detected",__func__,host->class_dev.kobj.name);
|
||||
mmc_claim_host(host);
|
||||
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
|
||||
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {
|
||||
@@ -2926,6 +2986,9 @@ int mmc_card_can_sleep(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_card *card = host->card;
|
||||
|
||||
if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD)
|
||||
return 0;
|
||||
|
||||
if (card && mmc_card_mmc(card) && card->ext_csd.rev >= 3)
|
||||
return 1;
|
||||
return 0;
|
||||
@@ -3174,14 +3237,14 @@ static int mmc_wait_trans_state(struct mmc_card *card, unsigned int wait_ms)
|
||||
int waited = 0;
|
||||
int status = 0;
|
||||
|
||||
mmc_send_status(card, &status);
|
||||
mmc_send_status(card, &status, 0);
|
||||
|
||||
while (R1_CURRENT_STATE(status) != R1_STATE_TRAN) {
|
||||
if (waited > wait_ms)
|
||||
return 0;
|
||||
mdelay(MIN_WAIT_MS);
|
||||
waited += MIN_WAIT_MS;
|
||||
mmc_send_status(card, &status);
|
||||
mmc_send_status(card, &status, 0);
|
||||
}
|
||||
return waited;
|
||||
}
|
||||
|
||||
2
drivers/mmc/core/debugfs.c
Normal file → Executable file
2
drivers/mmc/core/debugfs.c
Normal file → Executable file
@@ -260,7 +260,7 @@ static int mmc_dbg_card_status_get(void *data, u64 *val)
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
|
||||
ret = mmc_send_status(data, &status);
|
||||
ret = mmc_send_status(data, &status, 0);
|
||||
if (!ret)
|
||||
*val = status;
|
||||
|
||||
|
||||
251
drivers/mmc/core/mmc.c
Normal file → Executable file
251
drivers/mmc/core/mmc.c
Normal file → Executable file
@@ -272,6 +272,37 @@ static void mmc_select_card_type(struct mmc_card *card)
|
||||
card->ext_csd.card_type = card_type;
|
||||
}
|
||||
|
||||
/* eMMC 5.0 or later only */
|
||||
/*
|
||||
* mmc_merge_ext_csd - merge some ext_csd field to a variable.
|
||||
* @ext_csd : pointer of ext_csd.(1 Byte/field)
|
||||
* @continuous : if you want to merge continuous field, set true.
|
||||
* @count : a number of ext_csd field to merge(=< 8)
|
||||
* @args : list of ext_csd index or first index.
|
||||
*/
|
||||
static unsigned long long mmc_merge_ext_csd(u8 *ext_csd, bool continuous, int count, ...)
|
||||
{
|
||||
unsigned long long merge_ext_csd = 0;
|
||||
va_list args;
|
||||
int i = 0;
|
||||
int index;
|
||||
|
||||
va_start(args, count);
|
||||
|
||||
index = va_arg(args, int);
|
||||
for (i = 0; i < count; i++) {
|
||||
if (continuous) {
|
||||
merge_ext_csd = merge_ext_csd << 8 | ext_csd[index + count - 1 - i];
|
||||
} else {
|
||||
merge_ext_csd = merge_ext_csd << 8 | ext_csd[index];
|
||||
index = va_arg(args, int);
|
||||
}
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
return merge_ext_csd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode extended CSD.
|
||||
*/
|
||||
@@ -304,6 +335,14 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
* are authorized, see JEDEC JESD84-B50 section B.8.
|
||||
*/
|
||||
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
|
||||
#if !defined(CONFIG_MACH_ODROIDXU3)
|
||||
if (card->ext_csd.rev > 7) {
|
||||
pr_err("%s: unrecognised EXT_CSD revision %d\n",
|
||||
mmc_hostname(card->host), card->ext_csd.rev);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
card->ext_csd.raw_sectors[0] = ext_csd[EXT_CSD_SEC_CNT + 0];
|
||||
card->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1];
|
||||
@@ -556,14 +595,57 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
}
|
||||
|
||||
if (card->ext_csd.rev >= 7) {
|
||||
card->ext_csd.cmdq_support = ext_csd[EXT_CSD_CMDQ_SUPPORT];
|
||||
if (card->ext_csd.cmdq_support) {
|
||||
card->ext_csd.cmdq_depth = ext_csd[EXT_CSD_CMDQ_DEPTH];
|
||||
card->ext_csd.qrdy_support =
|
||||
ext_csd[EXT_CSD_QRDY_SUPPORT];
|
||||
card->ext_csd.qrdy_function =
|
||||
ext_csd[EXT_CSD_CMDQ_QRDY_FUNCTION];
|
||||
/* Enable CMDQ only when explicitly enabled at device tree */
|
||||
/* CMDQ supporting eMMC device can be used though we don't */
|
||||
/* use it */
|
||||
if (card->host->caps2 & MMC_CAP2_CMDQ) {
|
||||
card->ext_csd.cmdq_support =
|
||||
ext_csd[EXT_CSD_CMDQ_SUPPORT];
|
||||
if (card->ext_csd.cmdq_support) {
|
||||
/* depth is 0~31 in spec. so +1 for 1~32 */
|
||||
card->ext_csd.cmdq_depth =
|
||||
ext_csd[EXT_CSD_CMDQ_DEPTH] + 1;
|
||||
if (EMMC_MAX_QUEUE_DEPTH <
|
||||
card->ext_csd.cmdq_depth)
|
||||
card->ext_csd.cmdq_depth =
|
||||
EMMC_MAX_QUEUE_DEPTH;
|
||||
card->ext_csd.qrdy_support =
|
||||
ext_csd[EXT_CSD_QRDY_SUPPORT];
|
||||
card->ext_csd.qrdy_function =
|
||||
ext_csd[EXT_CSD_CMDQ_QRDY_FUNCTION];
|
||||
}
|
||||
}
|
||||
if (card->cid.manfid == 0x15 &&
|
||||
ext_csd[EXT_CSD_PRE_EOL_INFO] == 0x0 &&
|
||||
ext_csd[EXT_CSD_DEVICE_VERSION] == 0x0) {
|
||||
/* eMMC : moviNAND VMX device Only */
|
||||
card->ext_csd.smart_info = mmc_merge_ext_csd(ext_csd, false, 8,
|
||||
EXT_CSD_PREv5_LIFE_TIME_EST,
|
||||
EXT_CSD_PREv5_LIFE_TIME_EST,
|
||||
EXT_CSD_PREv5_PRE_EOL_INFO,
|
||||
EXT_CSD_PREv5_OPT_ERASE_SIZE,
|
||||
EXT_CSD_PREv5_OPT_WRITE_SIZE,
|
||||
EXT_CSD_PREv5_CTRL_VERSION,
|
||||
EXT_CSD_HC_ERASE_GRP_SIZE,
|
||||
EXT_CSD_HC_WP_GRP_SIZE);
|
||||
card->ext_csd.fwdate = mmc_merge_ext_csd(ext_csd, false, 1,
|
||||
EXT_CSD_PREv5_FIRMWARE_VERSION);
|
||||
} else {
|
||||
card->ext_csd.smart_info = mmc_merge_ext_csd(ext_csd, false, 8,
|
||||
EXT_CSD_DEVICE_LIFE_TIME_EST_TYPE_B,
|
||||
EXT_CSD_DEVICE_LIFE_TIME_EST_TYPE_A,
|
||||
EXT_CSD_PRE_EOL_INFO,
|
||||
EXT_CSD_OPTIMAL_TRIM_UNIT_SIZE,
|
||||
EXT_CSD_DEVICE_VERSION + 1,
|
||||
EXT_CSD_DEVICE_VERSION,
|
||||
EXT_CSD_HC_ERASE_GRP_SIZE,
|
||||
EXT_CSD_HC_WP_GRP_SIZE);
|
||||
card->ext_csd.fwdate = mmc_merge_ext_csd(ext_csd, true, 8,
|
||||
EXT_CSD_FIRMWARE_VERSION);
|
||||
}
|
||||
|
||||
card->ext_csd.enhanced_strobe_support =
|
||||
ext_csd[EXT_CSD_STORBE_SUPPORT];
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -653,6 +735,20 @@ MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
|
||||
MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
|
||||
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
|
||||
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
|
||||
MMC_DEV_ATTR(smart, "0x%016llx\n", card->ext_csd.smart_info);
|
||||
MMC_DEV_ATTR(fwdate, "0x%016llx\n", card->ext_csd.fwdate);
|
||||
MMC_DEV_ATTR(caps, "0x%08x\n", card->host->caps);
|
||||
MMC_DEV_ATTR(caps2, "0x%08x\n", card->host->caps2);
|
||||
MMC_DEV_ATTR(erase_type, "MMC_CAP_ERASE %s, type %s, SECURE %s, Sanitize %s\n",
|
||||
card->host->caps & MMC_CAP_ERASE ? "enabled" : "disabled",
|
||||
mmc_can_discard(card) ? "DISCARD" :
|
||||
(mmc_can_trim(card) ? "TRIM" : "NORMAL"),
|
||||
(!mmc_can_sanitize(card) && mmc_can_secure_erase_trim(card) &&
|
||||
!(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN)) ?
|
||||
"supportable" : "disabled",
|
||||
(mmc_can_sanitize(card) &&
|
||||
!(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN)) ?
|
||||
"enabled" : "disabled");
|
||||
|
||||
static struct attribute *mmc_std_attrs[] = {
|
||||
&dev_attr_cid.attr,
|
||||
@@ -671,6 +767,11 @@ static struct attribute *mmc_std_attrs[] = {
|
||||
&dev_attr_enhanced_area_size.attr,
|
||||
&dev_attr_raw_rpmb_size_mult.attr,
|
||||
&dev_attr_rel_sectors.attr,
|
||||
&dev_attr_smart.attr,
|
||||
&dev_attr_fwdate.attr,
|
||||
&dev_attr_caps.attr,
|
||||
&dev_attr_caps2.attr,
|
||||
&dev_attr_erase_type.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -776,7 +877,7 @@ static int mmc_select_powerclass(struct mmc_card *card,
|
||||
* Selects the desired buswidth and switch to the HS200 mode
|
||||
* if bus width set without error
|
||||
*/
|
||||
static int mmc_select_hs200(struct mmc_card *card, u8 driver_type)
|
||||
static int mmc_select_bus_width(struct mmc_card *card)
|
||||
{
|
||||
int idx, err = -EINVAL;
|
||||
struct mmc_host *host;
|
||||
@@ -807,7 +908,7 @@ static int mmc_select_hs200(struct mmc_card *card, u8 driver_type)
|
||||
|
||||
/* If fails try again during next card power cycle */
|
||||
if (err)
|
||||
goto err;
|
||||
return err;
|
||||
|
||||
idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 1 : 0;
|
||||
|
||||
@@ -843,12 +944,43 @@ static int mmc_select_hs200(struct mmc_card *card, u8 driver_type)
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Selects the desired buswidth and switch to the HS200 mode
|
||||
* if bus width set without error
|
||||
*/
|
||||
static int mmc_select_hs200(struct mmc_card *card, u8 driver_type)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
|
||||
err = mmc_select_bus_width(card);
|
||||
|
||||
/* switch to HS200 mode if bus width set successfully */
|
||||
if (!err)
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING,
|
||||
driver_type | MMC_HS_TIMING_HS200, 0);
|
||||
err:
|
||||
EXT_CSD_HS_TIMING, driver_type | 2, 0);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Selects the desired buswidth and switch to the HS mode
|
||||
* if bus width set without error
|
||||
*/
|
||||
static int mmc_select_hs(struct mmc_card *card)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
|
||||
err = mmc_select_bus_width(card);
|
||||
|
||||
/* switch to HS mode if bus width set successfully */
|
||||
if (!err)
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, 1,
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -868,6 +1000,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
u32 rocr;
|
||||
u8 *ext_csd = NULL;
|
||||
u8 driver_type = MMC_DRIVER_TYPE_0 << 4;
|
||||
bool en_strobe_enhanced = false;
|
||||
|
||||
BUG_ON(!host);
|
||||
WARN_ON(!host->claimed);
|
||||
@@ -961,10 +1094,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
goto free_card;
|
||||
}
|
||||
|
||||
if (card->cid.manfid == CID_MANFID_SAMSUNG &&
|
||||
host->caps2 & MMC_CAP2_DEVICE_DRIVER)
|
||||
driver_type = MMC_DRIVER_TYPE_5 << 4;
|
||||
|
||||
/*
|
||||
* Select card, as all following commands rely on that.
|
||||
*/
|
||||
@@ -1064,6 +1193,27 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
card->ext_csd.power_off_notification = EXT_CSD_POWER_ON;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sequence for Enhanced Strobe
|
||||
*
|
||||
* 1. CMD6(BUS_WIDTH) with 8 bit SDR bus
|
||||
* 2. CMD6(HS_TIMING) with HS mode
|
||||
* 3. Set timing and clock as HS mode
|
||||
* 4. CMD6(BUS_WIDTH) with 8 bit DDR bus and enhanced strobe
|
||||
* 5. CMD6(HS_TIMING) with HS400 mode
|
||||
* 6. Set timing and clock as HS400 mode and enhanced strobe
|
||||
* 7. CMD6(POWER_CLASS) with 8 bit DDR bus and MMC_HS200_MAX_DTR
|
||||
*/
|
||||
if (card->ext_csd.enhanced_strobe_support &
|
||||
MMC_STROBE_ENHANCED_SUPPORT) {
|
||||
if (host->caps2 & MMC_CAP2_STROBE_ENHANCED &&
|
||||
host->caps2 & MMC_CAP2_HS200_DDR) {
|
||||
en_strobe_enhanced = true;
|
||||
pr_warning("%s: STROBE ENHANCED enable\n",
|
||||
mmc_hostname(card->host));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Activate high speed (if supported)
|
||||
*/
|
||||
@@ -1071,9 +1221,17 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
err = 0;
|
||||
if (card->ext_csd.hs_max_dtr > 52000000 &&
|
||||
(host->caps2 & MMC_CAP2_HS200 ||
|
||||
host->caps2 & MMC_CAP2_HS200_DDR))
|
||||
err = mmc_select_hs200(card, driver_type);
|
||||
else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
|
||||
host->caps2 & MMC_CAP2_HS200_DDR)) {
|
||||
/*
|
||||
* Device output driver strength
|
||||
*/
|
||||
driver_type = host->dev_drv_str << 4;
|
||||
if (en_strobe_enhanced) {
|
||||
err = mmc_select_hs(card);
|
||||
} else {
|
||||
err = mmc_select_hs200(card, driver_type);
|
||||
}
|
||||
} else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, 1,
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
@@ -1086,7 +1244,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
mmc_hostname(card->host));
|
||||
err = 0;
|
||||
} else {
|
||||
if (card->ext_csd.hs_max_dtr > 52000000) {
|
||||
if (card->ext_csd.hs_max_dtr > 52000000 &&
|
||||
!en_strobe_enhanced) {
|
||||
if (host->caps2 & MMC_CAP2_HS200 ||
|
||||
host->caps2 & MMC_CAP2_HS200_DDR) {
|
||||
mmc_card_set_hs200(card);
|
||||
@@ -1107,7 +1266,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
|
||||
if (mmc_card_highspeed(card) ||
|
||||
mmc_card_hs200(card)) {
|
||||
if (max_dtr > card->ext_csd.hs_max_dtr)
|
||||
if (en_strobe_enhanced)
|
||||
max_dtr = MMC_HIGH_52_MAX_DTR;
|
||||
else if (max_dtr > card->ext_csd.hs_max_dtr)
|
||||
max_dtr = card->ext_csd.hs_max_dtr;
|
||||
if (mmc_card_highspeed(card) && (max_dtr > 52000000))
|
||||
max_dtr = 52000000;
|
||||
@@ -1136,10 +1297,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
/*
|
||||
* Indicate HS200 SDR mode (if supported).
|
||||
*/
|
||||
if (mmc_card_hs200(card)) {
|
||||
if (mmc_card_hs200(card) || en_strobe_enhanced) {
|
||||
u32 ext_csd_bits;
|
||||
u32 bus_width = card->host->ios.bus_width;
|
||||
int ddr;
|
||||
unsigned int timing;
|
||||
|
||||
/*
|
||||
* For devices supporting HS200 mode, the bus width has
|
||||
@@ -1158,7 +1320,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
(card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_200_1_8V &&
|
||||
host->caps2 & MMC_CAP2_HS200_1_8V_DDR)) ? 1 : 0;
|
||||
|
||||
if (card->host->ops->execute_tuning) {
|
||||
if (card->host->ops->execute_tuning && !en_strobe_enhanced) {
|
||||
host->tuning_progress |= MMC_HS200_TUNING;
|
||||
mmc_host_clk_hold(card->host);
|
||||
if (ddr) {
|
||||
@@ -1186,23 +1348,28 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
1 << bus_width);
|
||||
|
||||
if (ddr) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING,
|
||||
driver_type | MMC_HS_TIMING_HS,
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
if (err) {
|
||||
pr_warning("%s: switch to high-speed "
|
||||
"from hs200 failed\n",
|
||||
mmc_hostname(card->host));
|
||||
goto err;
|
||||
}
|
||||
if (!en_strobe_enhanced) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, 1,
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
if (err) {
|
||||
pr_warning("%s: switch to high-speed "
|
||||
"from hs200 failed\n",
|
||||
mmc_hostname(card->host));
|
||||
goto err;
|
||||
}
|
||||
|
||||
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
||||
mmc_set_clock(host, MMC_HIGH_52_MAX_DTR);
|
||||
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
||||
mmc_set_clock(host, MMC_HIGH_52_MAX_DTR);
|
||||
}
|
||||
|
||||
ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
|
||||
EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4;
|
||||
|
||||
if (en_strobe_enhanced) {
|
||||
ext_csd_bits |= EXT_CSD_STROBE_ENHANCED_EN;
|
||||
}
|
||||
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BUS_WIDTH,
|
||||
ext_csd_bits,
|
||||
@@ -1225,7 +1392,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
|
||||
mmc_card_clr_hs200(card);
|
||||
mmc_card_set_hs200_ddr(card);
|
||||
mmc_set_timing(card->host, MMC_TIMING_MMC_HS200_DDR);
|
||||
if (en_strobe_enhanced)
|
||||
timing = MMC_TIMING_MMC_HS200_DDR_ES;
|
||||
else
|
||||
timing = MMC_TIMING_MMC_HS200_DDR;
|
||||
mmc_set_timing(card->host, timing);
|
||||
mmc_set_clock(host, MMC_HS200_MAX_DTR);
|
||||
}
|
||||
}
|
||||
@@ -1431,7 +1602,7 @@ static int mmc_can_poweroff_notify(const struct mmc_card *card)
|
||||
(card->ext_csd.power_off_notification == EXT_CSD_POWER_ON);
|
||||
}
|
||||
|
||||
static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
|
||||
int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
|
||||
{
|
||||
unsigned int timeout = card->ext_csd.generic_cmd6_time;
|
||||
int err;
|
||||
@@ -1452,6 +1623,7 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_poweroff_notify);
|
||||
|
||||
/*
|
||||
* Host is being removed. Free up the current card.
|
||||
@@ -1470,7 +1642,7 @@ static void mmc_remove(struct mmc_host *host)
|
||||
*/
|
||||
static int mmc_alive(struct mmc_host *host)
|
||||
{
|
||||
return mmc_send_status(host->card, NULL);
|
||||
return mmc_send_status(host->card, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1520,7 +1692,8 @@ static int mmc_suspend(struct mmc_host *host)
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (mmc_can_poweroff_notify(host->card))
|
||||
if (mmc_can_poweroff_notify(host->card) &&
|
||||
!(host->caps2 & MMC_CAP2_NO_SLEEP_CMD))
|
||||
err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_SHORT);
|
||||
else if (mmc_card_can_sleep(host))
|
||||
err = mmc_card_sleep(host);
|
||||
|
||||
8
drivers/mmc/core/mmc_ops.c
Normal file → Executable file
8
drivers/mmc/core/mmc_ops.c
Normal file → Executable file
@@ -412,6 +412,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
unsigned int ignore;
|
||||
unsigned long timeout;
|
||||
u32 status;
|
||||
|
||||
@@ -442,8 +443,10 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
|
||||
/* Must check status to be sure of no errors */
|
||||
timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS);
|
||||
ignore = (index == EXT_CSD_HS_TIMING) ? MMC_RSP_CRC : 0;
|
||||
|
||||
do {
|
||||
err = mmc_send_status(card, &status);
|
||||
err = mmc_send_status(card, &status, ignore);
|
||||
if (err)
|
||||
return err;
|
||||
if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
|
||||
@@ -481,7 +484,7 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_switch);
|
||||
|
||||
int mmc_send_status(struct mmc_card *card, u32 *status)
|
||||
int mmc_send_status(struct mmc_card *card, u32 *status, unsigned int ignore)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
@@ -493,6 +496,7 @@ int mmc_send_status(struct mmc_card *card, u32 *status)
|
||||
if (!mmc_host_is_spi(card->host))
|
||||
cmd.arg = card->rca << 16;
|
||||
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||
cmd.flags &= ~ignore;
|
||||
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
|
||||
if (err)
|
||||
|
||||
2
drivers/mmc/core/mmc_ops.h
Normal file → Executable file
2
drivers/mmc/core/mmc_ops.h
Normal file → Executable file
@@ -20,7 +20,7 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid);
|
||||
int mmc_set_relative_addr(struct mmc_card *card);
|
||||
int mmc_send_csd(struct mmc_card *card, u32 *csd);
|
||||
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
|
||||
int mmc_send_status(struct mmc_card *card, u32 *status);
|
||||
int mmc_send_status(struct mmc_card *card, u32 *status, unsigned int ignore);
|
||||
int mmc_send_cid(struct mmc_host *host, u32 *cid);
|
||||
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);
|
||||
|
||||
5
drivers/mmc/core/quirks.c
Normal file → Executable file
5
drivers/mmc/core/quirks.c
Normal file → Executable file
@@ -33,19 +33,22 @@
|
||||
/*
|
||||
* This hook just adds a quirk for all sdio devices
|
||||
*/
|
||||
#if !defined(CONFIG_MMC_CLKGATE)
|
||||
static void add_quirk_for_sdio_devices(struct mmc_card *card, int data)
|
||||
{
|
||||
if (mmc_card_sdio(card))
|
||||
card->quirks |= data;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct mmc_fixup mmc_fixup_methods[] = {
|
||||
/* by default sdio devices are considered CLK_GATING broken */
|
||||
/* good cards will be whitelisted as they are tested */
|
||||
#if !defined(CONFIG_MMC_CLKGATE)
|
||||
SDIO_FIXUP(SDIO_ANY_ID, SDIO_ANY_ID,
|
||||
add_quirk_for_sdio_devices,
|
||||
MMC_QUIRK_BROKEN_CLK_GATING),
|
||||
|
||||
#endif
|
||||
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
|
||||
remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
|
||||
|
||||
|
||||
6
drivers/mmc/core/sd.c
Normal file → Executable file
6
drivers/mmc/core/sd.c
Normal file → Executable file
@@ -649,10 +649,12 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
|
||||
try:
|
||||
/* SPI mode doesn't define CMD19 */
|
||||
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) {
|
||||
card->host->tuning_progress |= MMC_HS200_TUNING;
|
||||
mmc_host_clk_hold(card->host);
|
||||
err = card->host->ops->execute_tuning(card->host,
|
||||
MMC_SEND_TUNING_BLOCK);
|
||||
mmc_host_clk_release(card->host);
|
||||
card->host->tuning_progress = 0;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
@@ -1063,7 +1065,7 @@ static void mmc_sd_remove(struct mmc_host *host)
|
||||
*/
|
||||
static int mmc_sd_alive(struct mmc_host *host)
|
||||
{
|
||||
return mmc_send_status(host->card, NULL);
|
||||
return mmc_send_status(host->card, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1086,7 +1088,7 @@ static void mmc_sd_detect(struct mmc_host *host)
|
||||
*/
|
||||
#ifdef CONFIG_MMC_PARANOID_SD_INIT
|
||||
while(retries) {
|
||||
err = mmc_send_status(host->card, NULL);
|
||||
err = mmc_send_status(host->card, NULL, 0);
|
||||
if (err) {
|
||||
retries--;
|
||||
udelay(5);
|
||||
|
||||
17
drivers/mmc/core/sdio.c
Normal file → Executable file
17
drivers/mmc/core/sdio.c
Normal file → Executable file
@@ -1261,20 +1261,33 @@ int sdio_reset_comm(struct mmc_card *card)
|
||||
printk("%s():\n", __func__);
|
||||
mmc_claim_host(host);
|
||||
|
||||
mmc_set_timing(host, MMC_TIMING_LEGACY);
|
||||
//mmc_set_clock(host, host->f_min);
|
||||
mmc_set_clock(host, host->f_init);
|
||||
|
||||
sdio_reset(host);
|
||||
mmc_go_idle(host);
|
||||
|
||||
mmc_set_clock(host, host->f_min);
|
||||
mmc_send_if_cond(host, host->ocr_avail);
|
||||
|
||||
err = mmc_send_io_op_cond(host, 0, &ocr);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
host->ocr = mmc_select_voltage(host, ocr);
|
||||
if (host->ocr_avail_sdio)
|
||||
host->ocr_avail = host->ocr_avail_sdio;
|
||||
|
||||
host->ocr = mmc_select_voltage(host, ocr & ~0x7F);
|
||||
if (!host->ocr) {
|
||||
err = -EINVAL;
|
||||
printk("%s(): voltage err\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (mmc_host_uhs(host))
|
||||
/* to query card if 1.8V signalling is supported */
|
||||
host->ocr |= R4_18V_PRESENT;
|
||||
|
||||
err = mmc_sdio_init_card(host, host->ocr, card, 0);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
22
drivers/mmc/host/Kconfig
Normal file → Executable file
22
drivers/mmc/host/Kconfig
Normal file → Executable file
@@ -557,6 +557,14 @@ config MMC_DW_IDMAC
|
||||
Designware Mobile Storage IP block. This disables the external DMA
|
||||
interface.
|
||||
|
||||
config MMC_DW_64_IDMAC
|
||||
bool "64bit Internal DMAC interface"
|
||||
depends on MMC_DW_IDMAC
|
||||
help
|
||||
This selects support for the 64bit internal DMAC block within the Synopsys
|
||||
Designware Mobile Storage IP block. This disables the 32bit internal DMA
|
||||
interface.
|
||||
|
||||
config MMC_DW_PLTFM
|
||||
tristate "Synopsys Designware MCI Support as platform device"
|
||||
depends on MMC_DW
|
||||
@@ -580,6 +588,20 @@ config MMC_DW_EXYNOS
|
||||
Synopsys DesignWare Memory Card Interface driver. Select this option
|
||||
for platforms based on Exynos4 and Exynos5 SoC's.
|
||||
|
||||
config MMC_DW_DEBUG
|
||||
bool "Samsung DWMCI debug feature"
|
||||
depends on MMC_DW
|
||||
help
|
||||
This selects support for infomation logging for debugging.
|
||||
Select this option if this feature is needed on working.
|
||||
|
||||
config MMC_DW_SKIP_CACHE_OP
|
||||
bool "Skip operations for cache coherency"
|
||||
depends on MMC_DW
|
||||
help
|
||||
This selects support for skipping operations for cache coherency
|
||||
to reduce I/O overhead if file system layer do same thing.
|
||||
|
||||
config MMC_DW_PCI
|
||||
tristate "Synopsys Designware MCI support on PCI bus"
|
||||
depends on MMC_DW && PCI
|
||||
|
||||
793
drivers/mmc/host/dw_mmc-exynos.c
Normal file → Executable file
793
drivers/mmc/host/dw_mmc-exynos.c
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
93
drivers/mmc/host/dw_mmc-exynos.h
Normal file → Executable file
93
drivers/mmc/host/dw_mmc-exynos.h
Normal file → Executable file
@@ -12,80 +12,13 @@
|
||||
|
||||
#define NUM_PINS(x) (x + 2)
|
||||
|
||||
#define DWMCI_CLKSEL 0x09C /* Ken : need to unify definition */
|
||||
#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0)
|
||||
#define SDMMC_CLKSEL_CCLK_FINE_SAMPLE(x) (((x) & 0xF) << 0)
|
||||
#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16)
|
||||
#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
|
||||
#define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7)
|
||||
#define SDMMC_CLKSEL_GET_DIVRATIO(x) ((((x) >> 24) & 0x7) + 1)
|
||||
#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \
|
||||
SDMMC_CLKSEL_CCLK_DRIVE(y) | \
|
||||
SDMMC_CLKSEL_CCLK_DIVIDER(z))
|
||||
|
||||
#define SDMMC_CMD_USE_HOLD_REG BIT(29)
|
||||
|
||||
/*
|
||||
* DDR200 dependent
|
||||
*/
|
||||
#define DWMCI_DDR200_RDDQS_EN 0x110
|
||||
#define DWMCI_DDR200_ASYNC_FIFO_CTRL 0x114
|
||||
#define DWMCI_DDR200_DLINE_CTRL 0x118
|
||||
/* DDR200 RDDQS Enable*/
|
||||
#define DWMCI_TXDT_CRC_TIMER_FASTLIMIT(x) (((x) & 0xFF) << 16)
|
||||
#define DWMCI_TXDT_CRC_TIMER_INITVAL(x) (((x) & 0xFF) << 8)
|
||||
#define DWMCI_TXDT_CRC_TIMER_SET(x, y) (DWMCI_TXDT_CRC_TIMER_FASTLIMIT(x) | \
|
||||
DWMCI_TXDT_CRC_TIMER_INITVAL(y))
|
||||
#define DWMCI_AXI_NON_BLOCKING_WRITE BIT(7)
|
||||
#define DWMCI_BUSY_CHK_CLK_STOP_EN BIT(2)
|
||||
#define DWMCI_RXDATA_START_BIT_SEL BIT(1)
|
||||
#define DWMCI_RDDQS_EN BIT(0)
|
||||
#define DWMCI_DDR200_RDDQS_EN_DEF DWMCI_TXDT_CRC_TIMER_FASTLIMIT(0x13) | \
|
||||
DWMCI_TXDT_CRC_TIMER_INITVAL(0x15)
|
||||
/* DDR200 DLINE CTRL */
|
||||
#define DWMCI_WD_DQS_DELAY_CTRL(x) (((x) & 0x3FF) << 20)
|
||||
#define DWMCI_FIFO_CLK_DELAY_CTRL(x) (((x) & 0x3) << 16)
|
||||
#define DWMCI_RD_DQS_DELAY_CTRL(x) ((x) & 0x3FF)
|
||||
#define DWMCI_DDR200_DLINE_CTRL_SET(x, y, z) (DWMCI_WD_DQS_DELAY_CTRL(x) | \
|
||||
DWMCI_FIFO_CLK_DELAY_CTRL(y) | \
|
||||
DWMCI_RD_DQS_DELAY_CTRL(z))
|
||||
#define DWMCI_DDR200_DLINE_CTRL_DEF DWMCI_FIFO_CLK_DELAY_CTRL(0x2) | \
|
||||
DWMCI_RD_DQS_DELAY_CTRL(0x40)
|
||||
|
||||
/* DDR200 Async FIFO Control */
|
||||
#define DWMCI_ASYNC_FIFO_RESET BIT(0)
|
||||
|
||||
/* Block number in eMMC */
|
||||
#define DWMCI_BLOCK_NUM 0xFFFFFFFF
|
||||
|
||||
#define DWMCI_EMMCP_BASE 0x1000
|
||||
#define DWMCI_MPSTAT (DWMCI_EMMCP_BASE + 0x0008)
|
||||
#define DWMCI_MPSECURITY (DWMCI_EMMCP_BASE + 0x0010)
|
||||
#define DWMCI_MPENCKEY (DWMCI_EMMCP_BASE + 0x0020)
|
||||
#define DWMCI_MPSBEGIN0 (DWMCI_EMMCP_BASE + 0x0200)
|
||||
#define DWMCI_MPSEND0 (DWMCI_EMMCP_BASE + 0x0204)
|
||||
#define DWMCI_MPSCTRL0 (DWMCI_EMMCP_BASE + 0x020C)
|
||||
#define DWMCI_MPSBEGIN1 (DWMCI_EMMCP_BASE + 0x0210)
|
||||
#define DWMCI_MPSEND1 (DWMCI_EMMCP_BASE + 0x0214)
|
||||
#define DWMCI_MPSCTRL1 (DWMCI_EMMCP_BASE + 0x021C)
|
||||
|
||||
/* SMU control bits */
|
||||
#define DWMCI_MPSCTRL_SECURE_READ_BIT BIT(7)
|
||||
#define DWMCI_MPSCTRL_SECURE_WRITE_BIT BIT(6)
|
||||
#define DWMCI_MPSCTRL_NON_SECURE_READ_BIT BIT(5)
|
||||
#define DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT BIT(4)
|
||||
#define DWMCI_MPSCTRL_USE_FUSE_KEY BIT(3)
|
||||
#define DWMCI_MPSCTRL_ECB_MODE BIT(2)
|
||||
#define DWMCI_MPSCTRL_ENCRYPTION BIT(1)
|
||||
#define DWMCI_MPSCTRL_VALID BIT(0)
|
||||
|
||||
#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
|
||||
#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
|
||||
|
||||
#define DWMCI_RESP_RCLK_MODE BIT(5)
|
||||
#define EXYNOS_DEF_MMC_0_CAPS (MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR | \
|
||||
MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23)
|
||||
MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23 | \
|
||||
MMC_CAP_ERASE)
|
||||
#define EXYNOS_DEF_MMC_1_CAPS MMC_CAP_CMD23
|
||||
#define EXYNOS_DEF_MMC_2_CAPS (MMC_CAP_CMD23 | MMC_CAP_UHS_SDR104)
|
||||
#define EXYNOS_DEF_MMC_2_CAPS (MMC_CAP_CMD23 | MMC_CAP_UHS_SDR50 | \
|
||||
MMC_CAP_UHS_SDR104 | MMC_CAP_ERASE)
|
||||
|
||||
#define MAX_TUNING_RETRIES 6
|
||||
#define MAX_TUNING_LOOP (MAX_TUNING_RETRIES * 8 * 2)
|
||||
@@ -103,21 +36,36 @@ enum dw_mci_exynos_type {
|
||||
struct dw_mci_exynos_priv_data {
|
||||
u8 ciu_div;
|
||||
u32 sdr_timing;
|
||||
u32 sdr_hs_timing;
|
||||
u32 ddr_timing;
|
||||
u32 hs200_timing;
|
||||
u32 ddr200_timing;
|
||||
u32 ddr200_ulp_timing;
|
||||
u32 ddr200_tx_t_fastlimit;
|
||||
u32 ddr200_tx_t_initval;
|
||||
u32 *ref_clk;
|
||||
const char *drv_str_pin;
|
||||
const char *drv_str_addr;
|
||||
int drv_str_val;
|
||||
u32 delay_line;
|
||||
u32 tx_delay_line;
|
||||
int drv_str_base_val;
|
||||
u32 drv_str_num;
|
||||
int cd_gpio;
|
||||
int sec_sd_slot_type;
|
||||
#define SEC_DEFAULT_SD_SLOT 0 /* No detect GPIO SD slot case */
|
||||
#define SEC_HOTPLUG_SD_SLOT 1 /* detect GPIO SD slot without Tray */
|
||||
#define SEC_HYBRID_SD_SLOT 2 /* detect GPIO SD slot with Tray */
|
||||
int vqmmc_en_gpio;
|
||||
int vmmc_en_gpio;
|
||||
u32 caps;
|
||||
u32 ctrl_flag;
|
||||
u32 ignore_phase;
|
||||
u32 selclk_drv;
|
||||
|
||||
#define DW_MMC_EXYNOS_USE_FINE_TUNING BIT(0)
|
||||
#define DW_MMC_EXYNOS_BYPASS_FOR_ALL_PASS BIT(1)
|
||||
#define DW_MMC_EXYNOS_ENABLE_SHIFT BIT(2)
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -155,3 +103,4 @@ static const u8 tuning_blk_pattern_8bit[] = {
|
||||
};
|
||||
|
||||
extern int dw_mci_exynos_request_status(void);
|
||||
extern void dw_mci_reg_dump(struct dw_mci *host);
|
||||
|
||||
17
drivers/mmc/host/dw_mmc-pltfm.c
Normal file → Executable file
17
drivers/mmc/host/dw_mmc-pltfm.c
Normal file → Executable file
@@ -105,12 +105,27 @@ static int dw_mci_pltfm_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_pltfm_early_resume(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dw_mci *host = dev_get_drvdata(dev);
|
||||
|
||||
ret = dw_mci_early_resume(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#define dw_mci_pltfm_suspend NULL
|
||||
#define dw_mci_pltfm_resume NULL
|
||||
#define dw_mci_pltfm_early_resume NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume);
|
||||
struct dev_pm_ops dw_mci_pltfm_pmops = {
|
||||
.suspend = dw_mci_pltfm_suspend,
|
||||
.resume = dw_mci_pltfm_resume,
|
||||
.resume_early = dw_mci_pltfm_early_resume,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);
|
||||
|
||||
static const struct of_device_id dw_mci_pltfm_match[] = {
|
||||
|
||||
1120
drivers/mmc/host/dw_mmc.c
Normal file → Executable file
1120
drivers/mmc/host/dw_mmc.c
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
14
drivers/mmc/host/dw_mmc.h
Normal file → Executable file
14
drivers/mmc/host/dw_mmc.h
Normal file → Executable file
@@ -60,14 +60,6 @@
|
||||
#define SDMMC_UHS_DDR_MODE 0x1
|
||||
#define SDMMC_BMOD 0x080
|
||||
#define SDMMC_PLDMND 0x084
|
||||
#define SDMMC_DBADDR 0x088
|
||||
#define SDMMC_IDSTS 0x08c
|
||||
#define SDMMC_IDINTEN 0x090
|
||||
#define SDMMC_DSCADDR 0x094
|
||||
#define SDMMC_BUFADDR 0x098
|
||||
#define SDMMC_CLKSEL 0x09C /* specific to Samsung Exynos */
|
||||
#define SDMMC_CDTHRCTL 0x100
|
||||
#define SDMMC_DATA(x) (x)
|
||||
|
||||
#define SDMMC_SHA_CMD_IE 0x190
|
||||
#define SDMMC_SHA_CMD_IS 0x194
|
||||
@@ -131,6 +123,7 @@
|
||||
#define SDMMC_INT_ERROR 0xbfc2
|
||||
/* Command register defines */
|
||||
#define SDMMC_CMD_START BIT(31)
|
||||
#define SDMMC_CMD_USE_HOLD_REG BIT(29)
|
||||
#define SDMMC_VOLT_SWITCH BIT(28)
|
||||
#define SDMMC_CMD_CCS_EXP BIT(23)
|
||||
#define SDMMC_CMD_CEATA_RD BIT(22)
|
||||
@@ -220,13 +213,18 @@ enum dw_mci_misc_control {
|
||||
CTRL_REQUEST_EXT_IRQ,
|
||||
CTRL_CHECK_CD_GPIO,
|
||||
CTRL_SET_DEF_CAPS,
|
||||
CTRL_SET_ETC_GPIO,
|
||||
};
|
||||
|
||||
extern int dw_mci_probe(struct dw_mci *host);
|
||||
extern void dw_mci_remove(struct dw_mci *host);
|
||||
extern void dw_mci_cmd_reg_summary(struct dw_mci *host);
|
||||
extern void dw_mci_status_reg_summary(struct dw_mci *host);
|
||||
#ifdef CONFIG_PM
|
||||
extern int dw_mci_suspend(struct dw_mci *host);
|
||||
extern int dw_mci_resume(struct dw_mci *host);
|
||||
extern void dw_mci_shutdown(struct dw_mci *host);
|
||||
extern int dw_mci_early_resume(struct dw_mci *host);
|
||||
#endif
|
||||
extern int dw_mci_ciu_clk_en(struct dw_mci *host, bool force_gating);
|
||||
extern void dw_mci_ciu_clk_dis(struct dw_mci *host);
|
||||
|
||||
5
fs/proc/Kconfig
Normal file → Executable file
5
fs/proc/Kconfig
Normal file → Executable file
@@ -67,3 +67,8 @@ config PROC_PAGE_MONITOR
|
||||
/proc/pid/smaps, /proc/pid/clear_refs, /proc/pid/pagemap,
|
||||
/proc/kpagecount, and /proc/kpageflags. Disabling these
|
||||
interfaces will reduce the size of the kernel by approximately 4kb.
|
||||
|
||||
config PROC_STLOG
|
||||
bool "Enable Storage log"
|
||||
depends on PROC_FS
|
||||
default y
|
||||
|
||||
1
fs/proc/Makefile
Normal file → Executable file
1
fs/proc/Makefile
Normal file → Executable file
@@ -23,6 +23,7 @@ proc-y += version.o
|
||||
proc-y += softirqs.o
|
||||
proc-y += namespaces.o
|
||||
proc-y += self.o
|
||||
proc-$(CONFIG_PROC_STLOG) += stlog.o
|
||||
proc-$(CONFIG_PROC_SYSCTL) += proc_sysctl.o
|
||||
proc-$(CONFIG_NET) += proc_net.o
|
||||
proc-$(CONFIG_PROC_KCORE) += kcore.o
|
||||
|
||||
736
fs/proc/stlog.c
Executable file
736
fs/proc/stlog.c
Executable file
@@ -0,0 +1,736 @@
|
||||
/*
|
||||
* linux/fs/proc/stlog.c
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/stlog.h>
|
||||
|
||||
extern wait_queue_head_t ringbuf_wait;
|
||||
|
||||
/*
|
||||
* Compatibility Issue :
|
||||
* dumpstate process of Android M tries to open stlog node with O_NONBLOCK.
|
||||
* But on Android L, it tries to open stlog node without O_NONBLOCK.
|
||||
* In order to resolve this issue, stlog_open() always works as NONBLOCK mode.
|
||||
* If you want runtime debugging, please use stlog_open_pipe().
|
||||
*/
|
||||
static int stlog_open(struct inode * inode, struct file * file)
|
||||
{
|
||||
//Open as non-blocking mode for printing once.
|
||||
file->f_flags |= O_NONBLOCK;
|
||||
return do_stlog(STLOG_ACTION_OPEN, NULL, 0, STLOG_FROM_PROC);
|
||||
}
|
||||
|
||||
static int stlog_open_pipe(struct inode * inode, struct file * file)
|
||||
{
|
||||
//Open as blocking mode for runtime debugging
|
||||
file->f_flags &= ~(O_NONBLOCK);
|
||||
return do_stlog(STLOG_ACTION_OPEN, NULL, 0, STLOG_FROM_PROC);
|
||||
}
|
||||
|
||||
static int stlog_release(struct inode * inode, struct file * file)
|
||||
{
|
||||
(void) do_stlog(STLOG_ACTION_CLOSE, NULL, 0, STLOG_FROM_PROC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t stlog_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
//Blocking mode for runtime debugging
|
||||
if (!(file->f_flags & O_NONBLOCK))
|
||||
return do_stlog(STLOG_ACTION_READ, buf, count, STLOG_FROM_PROC);
|
||||
|
||||
//Non-blocking mode, print once, consume all the buffers
|
||||
return do_stlog(STLOG_ACTION_READ_ALL, buf, count, STLOG_FROM_PROC);
|
||||
}
|
||||
|
||||
static ssize_t stlog_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
return do_stlog_write(STLOG_ACTION_WRITE, buf, count, STLOG_FROM_READER);
|
||||
}
|
||||
|
||||
|
||||
loff_t stlog_llseek(struct file *file, loff_t offset, int whence)
|
||||
{
|
||||
return (loff_t)do_stlog(STLOG_ACTION_SIZE_BUFFER, 0, 0, STLOG_FROM_READER);
|
||||
}
|
||||
|
||||
static const struct file_operations stlog_operations = {
|
||||
.read = stlog_read,
|
||||
.write = stlog_write,
|
||||
.open = stlog_open,
|
||||
.release = stlog_release,
|
||||
.llseek = stlog_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations stlog_pipe_operations = {
|
||||
.read = stlog_read,
|
||||
.open = stlog_open_pipe,
|
||||
.release = stlog_release,
|
||||
.llseek = stlog_llseek,
|
||||
};
|
||||
|
||||
static const char DEF_STLOG_VER_STR[] = "1.0.2\n";
|
||||
|
||||
static ssize_t stlog_ver_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
loff_t off = *ppos;
|
||||
ssize_t len = strlen(DEF_STLOG_VER_STR);
|
||||
|
||||
if (off >= len)
|
||||
return 0;
|
||||
|
||||
len -= off;
|
||||
if (count < len)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = copy_to_user(buf, &DEF_STLOG_VER_STR[off], len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
len -= ret;
|
||||
*ppos += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct file_operations stlog_ver_operations = {
|
||||
.read = stlog_ver_read,
|
||||
};
|
||||
|
||||
static int __init stlog_init(void)
|
||||
{
|
||||
proc_create("stlog", S_IRUGO, NULL, &stlog_operations);
|
||||
proc_create("stlog_pipe", S_IRUGO, NULL, &stlog_pipe_operations);
|
||||
proc_create("stlog_version", S_IRUGO, NULL, &stlog_ver_operations);
|
||||
return 0;
|
||||
}
|
||||
module_init(stlog_init);
|
||||
|
||||
//#define CONFIG_STLOG_CPU_ID
|
||||
#define CONFIG_STLOG_PID
|
||||
#define CONFIG_STLOG_TIME
|
||||
#ifdef CONFIG_STLOG
|
||||
#define CONFIG_RINGBUF_SHIFT 15 /*32KB*/
|
||||
#else
|
||||
#define CONFIG_RINGBUF_SHIFT 10 /*1KB*/
|
||||
#endif
|
||||
|
||||
//#define DEBUG_STLOG
|
||||
|
||||
|
||||
#if defined(CONFIG_STLOG_CPU_ID)
|
||||
static bool stlog_cpu_id = 1;
|
||||
#else
|
||||
static bool stlog_cpu_id;
|
||||
#endif
|
||||
|
||||
module_param_named(cpu, stlog_cpu_id, bool, S_IRUGO | S_IWUSR);
|
||||
|
||||
#if defined(CONFIG_STLOG_PID)
|
||||
static bool stlog_pid = 1;
|
||||
#else
|
||||
static bool stlog_pid;
|
||||
#endif
|
||||
module_param_named(pid, stlog_pid, bool, S_IRUGO | S_IWUSR);
|
||||
|
||||
|
||||
enum ringbuf_flags {
|
||||
RINGBUF_NOCONS = 1,
|
||||
RINGBUF_NEWLINE = 2,
|
||||
RINGBUF_PREFIX = 4,
|
||||
RINGBUF_CONT = 8,
|
||||
};
|
||||
|
||||
struct ringbuf {
|
||||
u64 ts_nsec;
|
||||
u16 len;
|
||||
u16 text_len;
|
||||
#ifdef CONFIG_STLOG_CPU_ID
|
||||
u8 cpu_id;
|
||||
#endif
|
||||
#ifdef CONFIG_STLOG_PID
|
||||
char comm[TASK_COMM_LEN];
|
||||
pid_t pid;
|
||||
#endif
|
||||
u8 flags:5;
|
||||
u8 level:3;
|
||||
};
|
||||
|
||||
/*
|
||||
* The ringbuf_lock protects smsg buffer, indices, counters.
|
||||
*/
|
||||
static DEFINE_RAW_SPINLOCK(ringbuf_lock);
|
||||
|
||||
DECLARE_WAIT_QUEUE_HEAD(ringbuf_wait);
|
||||
/* the next stlog record to read by /proc/stlog */
|
||||
static u64 stlog_seq;
|
||||
static u32 stlog_idx;
|
||||
static enum ringbuf_flags ringbuf_prev;
|
||||
|
||||
static u64 ringbuf_first_seq;
|
||||
static u32 ringbuf_first_idx;
|
||||
|
||||
static u64 ringbuf_next_seq;
|
||||
static u32 ringbuf_next_idx;
|
||||
|
||||
static u64 stlog_clear_seq;
|
||||
static u32 stlog_clear_idx;
|
||||
|
||||
#define S_PREFIX_MAX 32
|
||||
#define RINGBUF_LINE_MAX 1024 - S_PREFIX_MAX
|
||||
|
||||
/* record buffer */
|
||||
#define RINGBUF_ALIGN __alignof__(struct ringbuf)
|
||||
#define __RINGBUF_LEN (1 << CONFIG_RINGBUF_SHIFT)
|
||||
|
||||
static char __ringbuf_buf[__RINGBUF_LEN] __aligned(RINGBUF_ALIGN);
|
||||
static char *ringbuf_buf = __ringbuf_buf;
|
||||
|
||||
static u32 ringbuf_buf_len = __RINGBUF_LEN;
|
||||
|
||||
/* cpu currently holding logbuf_lock */
|
||||
static volatile unsigned int ringbuf_cpu = UINT_MAX;
|
||||
|
||||
/* human readable text of the record */
|
||||
static char *ringbuf_text(const struct ringbuf *msg)
|
||||
{
|
||||
return (char *)msg + sizeof(struct ringbuf);
|
||||
}
|
||||
|
||||
static struct ringbuf *ringbuf_from_idx(u32 idx)
|
||||
{
|
||||
struct ringbuf *msg = (struct ringbuf *)(ringbuf_buf + idx);
|
||||
|
||||
if (!msg->len)
|
||||
return (struct ringbuf *)ringbuf_buf;
|
||||
return msg;
|
||||
}
|
||||
|
||||
static u32 ringbuf_next(u32 idx)
|
||||
{
|
||||
struct ringbuf *msg = (struct ringbuf *)(ringbuf_buf + idx);
|
||||
|
||||
if (!msg->len) {
|
||||
msg = (struct ringbuf *)ringbuf_buf;
|
||||
return msg->len;
|
||||
}
|
||||
return idx + msg->len;
|
||||
}
|
||||
|
||||
static void ringbuf_store(enum ringbuf_flags flags, const char *text, u16 text_len,
|
||||
u8 cpu_id, struct task_struct *owner)
|
||||
{
|
||||
struct ringbuf *msg;
|
||||
u32 size, pad_len;
|
||||
|
||||
|
||||
#ifdef DEBUG_STLOG
|
||||
printk("[STLOG] %s stlog_seq %llu stlog_idx %lu ringbuf_first_seq %llu ringbuf_first_idx %lu ringbuf_next_seq %llu ringbuf_next_idx %lu \n",
|
||||
__func__,stlog_seq,stlog_idx,ringbuf_first_seq,ringbuf_first_idx,ringbuf_next_seq,ringbuf_next_idx);
|
||||
#endif
|
||||
|
||||
|
||||
/* number of '\0' padding bytes to next message */
|
||||
size = sizeof(struct ringbuf) + text_len;
|
||||
pad_len = (-size) & (RINGBUF_ALIGN - 1);
|
||||
size += pad_len;
|
||||
|
||||
while (ringbuf_first_seq < ringbuf_next_seq) {
|
||||
u32 free;
|
||||
|
||||
if (ringbuf_next_idx > ringbuf_first_idx)
|
||||
free = max(ringbuf_buf_len - ringbuf_next_idx, ringbuf_first_idx);
|
||||
else
|
||||
free = ringbuf_first_idx - ringbuf_next_idx;
|
||||
|
||||
if (free > size + sizeof(struct ringbuf))
|
||||
break;
|
||||
|
||||
/* drop old messages until we have enough space */
|
||||
ringbuf_first_idx = ringbuf_next(ringbuf_first_idx);
|
||||
ringbuf_first_seq++;
|
||||
}
|
||||
|
||||
if (ringbuf_next_idx + size + sizeof(struct ringbuf) >= ringbuf_buf_len) {
|
||||
memset(ringbuf_buf + ringbuf_next_idx, 0, sizeof(struct ringbuf));
|
||||
ringbuf_next_idx = 0;
|
||||
}
|
||||
|
||||
/* fill message */
|
||||
msg = (struct ringbuf *)(ringbuf_buf + ringbuf_next_idx);
|
||||
memcpy(ringbuf_text(msg), text, text_len);
|
||||
msg->text_len = text_len;
|
||||
msg->flags = flags & 0x1f;
|
||||
#ifdef CONFIG_STLOG_CPU_ID
|
||||
msg->cpu_id = cpu_id;
|
||||
#endif
|
||||
#ifdef CONFIG_STLOG_PID
|
||||
msg->pid = owner->pid;
|
||||
memcpy(msg->comm, owner->comm, TASK_COMM_LEN);
|
||||
#endif
|
||||
msg->ts_nsec = local_clock();
|
||||
msg->len = sizeof(struct ringbuf) + text_len + pad_len;
|
||||
|
||||
/* insert message */
|
||||
ringbuf_next_idx += msg->len;
|
||||
ringbuf_next_seq++;
|
||||
wake_up_interruptible(&ringbuf_wait);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#if defined(CONFIG_STLOG_TIME)
|
||||
static bool stlog_time = 1;
|
||||
#else
|
||||
static bool stlog_time;
|
||||
#endif
|
||||
module_param_named(time, stlog_time, bool, S_IRUGO | S_IWUSR);
|
||||
|
||||
static size_t stlog_print_time(u64 ts, char *buf)
|
||||
{
|
||||
unsigned long rem_nsec;
|
||||
|
||||
if (!stlog_time)
|
||||
return 0;
|
||||
|
||||
rem_nsec = do_div(ts, 1000000000);
|
||||
|
||||
if (!buf)
|
||||
return snprintf(NULL, 0, "[%5lu.000000] ", (unsigned long)ts);
|
||||
|
||||
return sprintf(buf, "[%5lu.%06lu] ",
|
||||
(unsigned long)ts, rem_nsec / 1000);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_STLOG_PID
|
||||
static size_t stlog_print_pid(const struct ringbuf *msg, char *buf)
|
||||
{
|
||||
if (!stlog_pid)
|
||||
return 0;
|
||||
|
||||
if (!buf)
|
||||
return snprintf(NULL, 0, "[%15s, %d] ", msg->comm, msg->pid);
|
||||
|
||||
return sprintf(buf, "[%15s, %d] ", msg->comm, msg->pid);
|
||||
}
|
||||
#else
|
||||
static size_t stlog_print_pid(const struct ringbuf *msg, char *buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_STLOG_CPU_ID
|
||||
static size_t stlog_print_cpuid(const struct ringbuf *msg, char *buf)
|
||||
{
|
||||
|
||||
if (!stlog_cpu_id)
|
||||
return 0;
|
||||
|
||||
if (!buf)
|
||||
return snprintf(NULL, 0, "C%d ", msg->cpu_id);
|
||||
|
||||
return sprintf(buf, "C%d ", msg->cpu_id);
|
||||
}
|
||||
#else
|
||||
static size_t stlog_print_cpuid(const struct ringbuf *msg, char *buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static size_t stlog_print_prefix(const struct ringbuf *msg, bool ringbuf, char *buf)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
len += stlog_print_time(msg->ts_nsec, buf ? buf + len : NULL);
|
||||
len += stlog_print_cpuid(msg, buf ? buf + len : NULL);
|
||||
len += stlog_print_pid(msg, buf ? buf + len : NULL);
|
||||
return len;
|
||||
}
|
||||
|
||||
static size_t stlog_print_text(const struct ringbuf *msg, enum ringbuf_flags prev,
|
||||
bool ringbuf, char *buf, size_t size)
|
||||
{
|
||||
const char *text = ringbuf_text(msg);
|
||||
size_t text_size = msg->text_len;
|
||||
bool prefix = true;
|
||||
bool newline = true;
|
||||
size_t len = 0;
|
||||
|
||||
do {
|
||||
const char *next = memchr(text, '\n', text_size);
|
||||
size_t text_len;
|
||||
|
||||
if (next) {
|
||||
text_len = next - text;
|
||||
next++;
|
||||
text_size -= next - text;
|
||||
} else {
|
||||
text_len = text_size;
|
||||
}
|
||||
|
||||
if (buf) {
|
||||
if (stlog_print_prefix(msg, ringbuf, NULL) + text_len + 1 >= size - len)
|
||||
break;
|
||||
|
||||
if (prefix)
|
||||
len += stlog_print_prefix(msg, ringbuf, buf + len);
|
||||
memcpy(buf + len, text, text_len);
|
||||
len += text_len;
|
||||
if (next || newline)
|
||||
buf[len++] = '\n';
|
||||
} else {
|
||||
/* buffer size only calculation */
|
||||
if (prefix)
|
||||
len += stlog_print_prefix(msg, ringbuf, NULL);
|
||||
len += text_len;
|
||||
if (next || newline)
|
||||
len++;
|
||||
}
|
||||
|
||||
prefix = true;
|
||||
text = next;
|
||||
} while (text);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int stlog_print(char __user *buf, int size)
|
||||
{
|
||||
char *text;
|
||||
struct ringbuf *msg;
|
||||
int len = 0;
|
||||
|
||||
#ifdef DEBUG_STLOG
|
||||
printk("[STLOG] %s stlog_seq %llu stlog_idx %lu ringbuf_first_seq %llu ringbuf_first_idx %lu ringbuf_next_seq %llu ringbuf_next_idx %lu \n",
|
||||
__func__,stlog_seq,stlog_idx,ringbuf_first_seq,ringbuf_first_idx,ringbuf_next_seq,ringbuf_next_idx);
|
||||
#endif
|
||||
|
||||
text = kmalloc(RINGBUF_LINE_MAX + S_PREFIX_MAX, GFP_KERNEL);
|
||||
if (!text)
|
||||
return -ENOMEM;
|
||||
|
||||
while (size > 0) {
|
||||
size_t n;
|
||||
|
||||
raw_spin_lock_irq(&ringbuf_lock);
|
||||
if (stlog_seq < ringbuf_first_seq) {
|
||||
/* messages are gone, move to first one */
|
||||
stlog_seq = ringbuf_first_seq;
|
||||
stlog_idx = ringbuf_first_idx;
|
||||
ringbuf_prev = 0;
|
||||
}
|
||||
if (stlog_seq == ringbuf_next_seq) {
|
||||
raw_spin_unlock_irq(&ringbuf_lock);
|
||||
break;
|
||||
}
|
||||
|
||||
msg = ringbuf_from_idx(stlog_idx);
|
||||
n = stlog_print_text(msg, ringbuf_prev, false, text, RINGBUF_LINE_MAX + S_PREFIX_MAX);
|
||||
if (n <= size) {
|
||||
/* message fits into buffer, move forward */
|
||||
stlog_idx = ringbuf_next(stlog_idx);
|
||||
stlog_seq++;
|
||||
ringbuf_prev = msg->flags;
|
||||
} else if (!len){
|
||||
n = size;
|
||||
} else
|
||||
n = 0;
|
||||
raw_spin_unlock_irq(&ringbuf_lock);
|
||||
|
||||
if (!n)
|
||||
break;
|
||||
|
||||
if (copy_to_user(buf, text, n)) {
|
||||
if (!len)
|
||||
len = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
len += n;
|
||||
size -= n;
|
||||
buf += n;
|
||||
}
|
||||
|
||||
kfree(text);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int stlog_print_all(char __user *buf, int size, bool clear)
|
||||
{
|
||||
char *text;
|
||||
int len = 0;
|
||||
|
||||
text = kmalloc(RINGBUF_LINE_MAX + S_PREFIX_MAX, GFP_KERNEL);
|
||||
if (!text)
|
||||
return -ENOMEM;
|
||||
|
||||
raw_spin_lock_irq(&ringbuf_lock);
|
||||
if (buf) {
|
||||
u64 next_seq;
|
||||
u64 seq;
|
||||
u32 idx;
|
||||
enum ringbuf_flags prev;
|
||||
|
||||
if (stlog_clear_seq < ringbuf_first_seq) {
|
||||
/* messages are gone, move to first available one */
|
||||
stlog_clear_seq = ringbuf_first_seq;
|
||||
stlog_clear_idx = ringbuf_first_idx;
|
||||
}
|
||||
|
||||
seq = stlog_clear_seq;
|
||||
idx = stlog_clear_idx;
|
||||
prev = 0;
|
||||
while (seq < ringbuf_next_seq) {
|
||||
struct ringbuf *msg = ringbuf_from_idx(idx);
|
||||
|
||||
len += stlog_print_text(msg, prev, false, NULL, 0);
|
||||
prev = msg->flags;
|
||||
idx = ringbuf_next(idx);
|
||||
seq++;
|
||||
}
|
||||
|
||||
/* move first record forward until length fits into the buffer */
|
||||
seq = stlog_clear_seq;
|
||||
idx = stlog_clear_idx;
|
||||
prev = 0;
|
||||
while (len > size && seq < ringbuf_next_seq) {
|
||||
struct ringbuf *msg = ringbuf_from_idx(idx);
|
||||
|
||||
len -= stlog_print_text(msg, prev, false, NULL, 0);
|
||||
prev = msg->flags;
|
||||
idx = ringbuf_next(idx);
|
||||
seq++;
|
||||
}
|
||||
|
||||
/* last message fitting into this dump */
|
||||
next_seq = ringbuf_next_seq;
|
||||
|
||||
len = 0;
|
||||
prev = 0;
|
||||
while (len >= 0 && seq < next_seq) {
|
||||
struct ringbuf *msg = ringbuf_from_idx(idx);
|
||||
int textlen;
|
||||
|
||||
textlen = stlog_print_text(msg, prev, false, text,
|
||||
RINGBUF_LINE_MAX + S_PREFIX_MAX);
|
||||
if (textlen < 0) {
|
||||
len = textlen;
|
||||
break;
|
||||
}
|
||||
idx = ringbuf_next(idx);
|
||||
seq++;
|
||||
prev = msg->flags;
|
||||
|
||||
raw_spin_unlock_irq(&ringbuf_lock);
|
||||
if (copy_to_user(buf + len, text, textlen))
|
||||
len = -EFAULT;
|
||||
else{
|
||||
#ifdef DEBUG_STLOG
|
||||
printk("[STLOG] %s seq %llu text %s \n",__func__,seq,text);
|
||||
#endif
|
||||
len += textlen;
|
||||
}
|
||||
raw_spin_lock_irq(&ringbuf_lock);
|
||||
|
||||
if (seq < ringbuf_first_seq) {
|
||||
/* messages are gone, move to next one */
|
||||
seq = ringbuf_first_seq;
|
||||
idx = ringbuf_first_idx;
|
||||
prev = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clear) {
|
||||
stlog_clear_seq = ringbuf_next_seq;
|
||||
stlog_clear_idx = ringbuf_next_idx;
|
||||
}
|
||||
raw_spin_unlock_irq(&ringbuf_lock);
|
||||
|
||||
kfree(text);
|
||||
return len;
|
||||
}
|
||||
|
||||
int do_stlog(int type, char __user *buf, int len, bool from_file)
|
||||
{
|
||||
int error=0;
|
||||
|
||||
switch (type) {
|
||||
case STLOG_ACTION_CLOSE: /* Close log */
|
||||
break;
|
||||
case STLOG_ACTION_OPEN: /* Open log */
|
||||
|
||||
#ifdef DEBUG_STLOG
|
||||
printk("[STLOG] %s OPEN stlog_seq %llu stlog_idx %lu ringbuf_first_seq %llu ringbuf_first_idx %lu ringbuf_next_seq %llu ringbuf_next_idx %lu \n",
|
||||
__func__,stlog_seq,stlog_idx,ringbuf_first_seq,ringbuf_first_idx,ringbuf_next_seq,ringbuf_next_idx);
|
||||
#endif
|
||||
break;
|
||||
case STLOG_ACTION_READ: /* cat -f /proc/stlog */
|
||||
#ifdef DEBUG_STLOG
|
||||
printk("[STLOG] %s READ stlog_seq %llu stlog_idx %lu ringbuf_first_seq %llu ringbuf_first_idx %lu ringbuf_next_seq %llu ringbuf_next_idx %lu \n",
|
||||
__func__,stlog_seq,stlog_idx,ringbuf_first_seq,ringbuf_first_idx,ringbuf_next_seq,ringbuf_next_idx);
|
||||
#endif
|
||||
error = -EINVAL;
|
||||
if (!buf || len < 0)
|
||||
goto out;
|
||||
if (!len)
|
||||
goto out;
|
||||
if (!access_ok(VERIFY_WRITE, buf, len)) {
|
||||
error = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
error = wait_event_interruptible(ringbuf_wait,
|
||||
stlog_seq != ringbuf_next_seq);
|
||||
if (error)
|
||||
goto out;
|
||||
error = stlog_print(buf, len);
|
||||
break;
|
||||
case STLOG_ACTION_READ_ALL: /* cat /proc/stlog */ /* dumpstate */
|
||||
#ifdef DEBUG_STLOG
|
||||
printk("[STLOG] %s READ_ALL stlog_seq %llu stlog_idx %lu ringbuf_first_seq %llu ringbuf_first_idx %lu ringbuf_next_seq %llu ringbuf_next_idx %lu \n",
|
||||
__func__,stlog_seq,stlog_idx,ringbuf_first_seq,ringbuf_first_idx,ringbuf_next_seq,ringbuf_next_idx);
|
||||
#endif
|
||||
error = -EINVAL;
|
||||
if (!buf || len < 0)
|
||||
goto out;
|
||||
error = 0;
|
||||
if (!len)
|
||||
goto out;
|
||||
if (!access_ok(VERIFY_WRITE, buf, len)) {
|
||||
error = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
if(stlog_clear_seq==ringbuf_next_seq){
|
||||
stlog_clear_seq=ringbuf_first_seq;
|
||||
stlog_clear_idx=ringbuf_first_idx;
|
||||
error=0;
|
||||
goto out;
|
||||
}
|
||||
error = stlog_print_all(buf, len, true);
|
||||
break;
|
||||
/* Size of the log buffer */
|
||||
case STLOG_ACTION_SIZE_BUFFER:
|
||||
#ifdef DEBUG_STLOG
|
||||
printk("[STLOG] %s SIZE_BUFFER %lu\n",__func__,ringbuf_buf_len);
|
||||
#endif
|
||||
error = ringbuf_buf_len;
|
||||
break;
|
||||
default:
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
int do_stlog_write(int type, const char __user *buf, int len, bool from_file)
|
||||
{
|
||||
int error=0;
|
||||
char *kern_buf=0;
|
||||
char *line=0;
|
||||
|
||||
if (!buf || len < 0)
|
||||
goto out;
|
||||
if (!len)
|
||||
goto out;
|
||||
if (len > RINGBUF_LINE_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
kern_buf = kmalloc(len+1, GFP_KERNEL);
|
||||
if (kern_buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
line = kern_buf;
|
||||
if (copy_from_user(line, buf, len)) {
|
||||
error = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
line[len] = '\0';
|
||||
error = stlog("%s", line);
|
||||
if ((line[len-1] == '\n') && (error == (len-1)))
|
||||
error++;
|
||||
out:
|
||||
kfree(kern_buf);
|
||||
return error;
|
||||
|
||||
}
|
||||
|
||||
asmlinkage int vstlog(const char *fmt, va_list args)
|
||||
{
|
||||
static char textbuf[RINGBUF_LINE_MAX];
|
||||
char *text = textbuf;
|
||||
size_t text_len;
|
||||
enum ringbuf_flags lflags = 0;
|
||||
unsigned long flags;
|
||||
int this_cpu;
|
||||
int printed_len = 0;
|
||||
bool stored = false;
|
||||
|
||||
local_irq_save(flags);
|
||||
this_cpu = smp_processor_id();
|
||||
|
||||
raw_spin_lock(&ringbuf_lock);
|
||||
ringbuf_cpu = this_cpu;
|
||||
|
||||
text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
|
||||
|
||||
|
||||
/* mark and strip a trailing newline */
|
||||
if (text_len && text[text_len-1] == '\n') {
|
||||
text_len--;
|
||||
lflags |= RINGBUF_NEWLINE;
|
||||
}
|
||||
|
||||
if (!stored)
|
||||
ringbuf_store(lflags,text, text_len, ringbuf_cpu, current);
|
||||
|
||||
printed_len += text_len;
|
||||
|
||||
raw_spin_unlock(&ringbuf_lock);
|
||||
local_irq_restore(flags);
|
||||
|
||||
|
||||
return printed_len;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(vstlog);
|
||||
|
||||
/**
|
||||
* stlog - print a storage message
|
||||
* @fmt: format string
|
||||
*/
|
||||
asmlinkage int stlog(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int r;
|
||||
|
||||
va_start(args, fmt);
|
||||
r = vstlog(fmt, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
return r;
|
||||
}
|
||||
EXPORT_SYMBOL(stlog);
|
||||
|
||||
|
||||
|
||||
20
include/linux/mmc/card.h
Normal file → Executable file
20
include/linux/mmc/card.h
Normal file → Executable file
@@ -91,6 +91,8 @@ struct mmc_ext_csd {
|
||||
u8 raw_partition_support; /* 160 */
|
||||
u8 raw_rpmb_size_mult; /* 168 */
|
||||
u8 raw_erased_mem_count; /* 181 */
|
||||
u8 enhanced_strobe_support; /* 184 */
|
||||
#define MMC_STROBE_ENHANCED_SUPPORT BIT(0)
|
||||
u8 raw_ext_csd_structure; /* 194 */
|
||||
u8 raw_card_type; /* 196 */
|
||||
u8 out_of_int_time; /* 198 */
|
||||
@@ -115,6 +117,22 @@ struct mmc_ext_csd {
|
||||
|
||||
unsigned int feature_support;
|
||||
#define MMC_DISCARD_FEATURE BIT(0) /* CMD38 feature */
|
||||
/*
|
||||
* smart_info : It's for eMMC 5.0 or later device
|
||||
* [63:56] : DEVICE_LIFE_TIME_EST_TYPE_B [269]
|
||||
* [55:48] : DEVICE_LIFE_TIME_EST_TYPE_A [268]
|
||||
* [47:40] : PRE_EOL_INFO [267]
|
||||
* [39:32] : OPTIMAL_TRIM_UNIT_SIZE [264]
|
||||
* [31:16] : DEVICE_VERSION [263-262]
|
||||
* [15:08] : HC_ERASE_GRP_SIZE [224]
|
||||
* [07:00] : HC_WP_GRP_SIZE [221]
|
||||
*/
|
||||
unsigned long long smart_info;
|
||||
/*
|
||||
* fwdate : It's for eMMC 5.0 or later device
|
||||
* [63:00] : FIRMWARE_VERSION [261-254]
|
||||
*/
|
||||
unsigned long long fwdate;
|
||||
};
|
||||
|
||||
struct sd_scr {
|
||||
@@ -273,6 +291,7 @@ struct mmc_card {
|
||||
#define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */
|
||||
#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */
|
||||
/* byte mode */
|
||||
#define MMC_QUIRK_SEC_HW_RESET (1<<11) /* allow HW_RESET to recovery when some error happens */
|
||||
|
||||
unsigned int erase_size; /* erase size in sectors */
|
||||
unsigned int erase_shift; /* if erase unit is power 2 */
|
||||
@@ -528,6 +547,7 @@ struct mmc_driver {
|
||||
void (*remove)(struct mmc_card *);
|
||||
int (*suspend)(struct mmc_card *);
|
||||
int (*resume)(struct mmc_card *);
|
||||
int (*shutdown)(struct mmc_card *);
|
||||
};
|
||||
|
||||
extern int mmc_register_driver(struct mmc_driver *);
|
||||
|
||||
2
include/linux/mmc/core.h
Normal file → Executable file
2
include/linux/mmc/core.h
Normal file → Executable file
@@ -112,6 +112,7 @@ struct mmc_data {
|
||||
#define MMC_DATA_WRITE (1 << 8)
|
||||
#define MMC_DATA_READ (1 << 9)
|
||||
#define MMC_DATA_STREAM (1 << 10)
|
||||
#define MMC_DATA_DIRECT (1 << 11)
|
||||
|
||||
unsigned int bytes_xfered;
|
||||
|
||||
@@ -195,6 +196,7 @@ extern int mmc_try_claim_host(struct mmc_host *host);
|
||||
|
||||
extern int mmc_flush_cache(struct mmc_card *);
|
||||
extern int mmc_bkops_enable(struct mmc_host *host, u8 value);
|
||||
extern int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type);
|
||||
|
||||
extern int mmc_detect_card_removed(struct mmc_host *host);
|
||||
|
||||
|
||||
123
include/linux/mmc/dw_mmc.h
Normal file → Executable file
123
include/linux/mmc/dw_mmc.h
Normal file → Executable file
@@ -20,9 +20,6 @@
|
||||
|
||||
#define MAX_MCI_SLOTS 2
|
||||
|
||||
#define DW_MMC_BYPASS_SECTOR 0
|
||||
#define DW_MMC_ENCRYPTION_SECTOR 1
|
||||
|
||||
enum dw_mci_state {
|
||||
STATE_IDLE = 0,
|
||||
STATE_SENDING_CMD,
|
||||
@@ -80,6 +77,87 @@ struct dw_mci_slot {
|
||||
int last_detect_state;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dw_mci_debug_data - DwMMC debugging infomation
|
||||
* @host_count: a number of all hosts
|
||||
* @info_count: a number of set of debugging information
|
||||
* @info_index: index of debugging information for each host
|
||||
* @host: pointer of each dw_mci structure
|
||||
* @debug_info: debugging information structure
|
||||
*/
|
||||
|
||||
struct dw_mci_cmd_log {
|
||||
u64 send_time;
|
||||
u64 done_time;
|
||||
u8 cmd;
|
||||
u32 arg;
|
||||
u8 data_size;
|
||||
/* no data CMD = CD, data CMD = DTO */
|
||||
/*
|
||||
* 0b1000 0000 : new_cmd with without send_cmd
|
||||
* 0b0000 1000 : error occurs
|
||||
* 0b0000 0100 : data_done : DTO(Data Transfer Over)
|
||||
* 0b0000 0010 : resp : CD(Command Done)
|
||||
* 0b0000 0001 : send_cmd : set 1 only start_command
|
||||
*/
|
||||
u8 seq_status; /* 0bxxxx xxxx : error data_done resp send */
|
||||
#define DW_MCI_FLAG_SEND_CMD BIT(0)
|
||||
#define DW_MCI_FLAG_CD BIT(1)
|
||||
#define DW_MCI_FLAG_DTO BIT(2)
|
||||
#define DW_MCI_FLAG_ERROR BIT(3)
|
||||
#define DW_MCI_FLAG_NEW_CMD_ERR BIT(7)
|
||||
|
||||
u16 rint_sts; /* RINTSTS value in case of error */
|
||||
u32 resp0; /* resp0 is needed to check eMMC status when CD */
|
||||
u32 status_count; /* TBD : It can be changed */
|
||||
};
|
||||
|
||||
enum dw_mci_req_log_state {
|
||||
STATE_REQ_START = 0,
|
||||
STATE_REQ_CMD_PROCESS,
|
||||
STATE_REQ_DATA_PROCESS,
|
||||
STATE_REQ_END,
|
||||
};
|
||||
|
||||
struct dw_mci_req_log {
|
||||
u64 timestamp;
|
||||
u32 info0;
|
||||
u32 info1;
|
||||
u32 info2;
|
||||
u32 info3;
|
||||
u32 pending_events;
|
||||
u32 completed_events;
|
||||
enum dw_mci_state state_cmd;
|
||||
enum dw_mci_state state_dat;
|
||||
enum dw_mci_req_log_state log_state;
|
||||
};
|
||||
|
||||
#define DWMCI_LOG_MAX 0x80
|
||||
#define DWMCI_REQ_LOG_MAX 0x40
|
||||
struct dw_mci_debug_info {
|
||||
struct dw_mci_cmd_log cmd_log[DWMCI_LOG_MAX];
|
||||
atomic_t cmd_log_count;
|
||||
struct dw_mci_req_log req_log[DWMCI_REQ_LOG_MAX];
|
||||
atomic_t req_log_count;
|
||||
unsigned char en_logging;
|
||||
#define DW_MCI_DEBUG_ON_CMD BIT(0)
|
||||
#define DW_MCI_DEBUG_ON_REQ BIT(1)
|
||||
};
|
||||
|
||||
#define DWMCI_DBG_NUM_HOST 3
|
||||
|
||||
#define DWMCI_DBG_NUM_INFO 2 /* configurable */
|
||||
#define DWMCI_DBG_MASK_INFO (BIT(0) | BIT(2)) /* configurable */
|
||||
#define DWMCI_DBG_BIT_HOST(x) BIT(x)
|
||||
|
||||
struct dw_mci_debug_data {
|
||||
unsigned char host_count;
|
||||
unsigned char info_count;
|
||||
unsigned char info_index[DWMCI_DBG_NUM_HOST];
|
||||
struct dw_mci *host[DWMCI_DBG_NUM_HOST];
|
||||
struct dw_mci_debug_info debug_info[DWMCI_DBG_NUM_INFO];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dw_mci - MMC controller state shared between all slots
|
||||
* @lock: Spinlock protecting the queue and associated data.
|
||||
@@ -230,6 +308,10 @@ struct dw_mci {
|
||||
atomic_t ciu_en_win;
|
||||
struct dw_mci_slot *slot[MAX_MCI_SLOTS];
|
||||
|
||||
/* pinctrl handles */
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pins_direction;
|
||||
|
||||
/* FIFO push and pull */
|
||||
int fifo_depth;
|
||||
int data_shift;
|
||||
@@ -271,6 +353,7 @@ struct dw_mci {
|
||||
#define DW_MMC_REQ_IDLE 0
|
||||
#define DW_MMC_REQ_BUSY 1
|
||||
unsigned int req_state;
|
||||
struct dw_mci_debug_info *debug_info; /* debug info */
|
||||
};
|
||||
|
||||
/* DMA ops for Internal/External DMAC interface */
|
||||
@@ -302,6 +385,18 @@ struct dw_mci_dma_ops {
|
||||
#define DW_MMC_QUIRK_FIXED_VOLTAGE BIT(7)
|
||||
/* Use S/W data timeout */
|
||||
#define DW_MMC_QUIRK_SW_DATA_TIMEOUT BIT(8)
|
||||
/* Retry CRC error */
|
||||
#define DW_MMC_QUIRK_RETRY_ERROR BIT(9)
|
||||
/* Use CPU mode for tuning */
|
||||
#define DW_MMC_QUIRK_USE_CPU_MODE_TUNING BIT(10)
|
||||
/* W/A to fix AXI hang-up for mismatch of sector size*/
|
||||
#define DW_MMC_QUIRK_FMP_SIZE_MISMATCH BIT(11)
|
||||
/* Not allow DMA single transfer */
|
||||
#define DW_MMC_QUIRK_NOT_ALLOW_SINGLE_DMA BIT(12)
|
||||
/* Use the security management unit */
|
||||
#define DW_MCI_QUIRK_USE_SMU BIT(13)
|
||||
/* Enables ultra low power mode */
|
||||
#define DW_MCI_QUIRK_ENABLE_ULP BIT(14)
|
||||
|
||||
/* Slot level quirks */
|
||||
/* This slot has no write protect */
|
||||
@@ -372,11 +467,14 @@ struct dw_mci_board {
|
||||
int (*get_cd)(u32 slot_id);
|
||||
int (*get_ocr)(u32 slot_id);
|
||||
int (*get_bus_wd)(u32 slot_id);
|
||||
void (*hw_reset)(u32 slot_id);
|
||||
void (*hw_reset)(struct dw_mci *host);
|
||||
|
||||
/* INT QOS khz */
|
||||
unsigned int qos_int_level;
|
||||
|
||||
unsigned char io_mode;
|
||||
|
||||
u32 error_retry_cnt;
|
||||
/* cd_type: Type of Card Detection method (see cd_types enum above) */
|
||||
enum dw_mci_cd_types cd_type;
|
||||
|
||||
@@ -392,9 +490,9 @@ struct dw_mci_board {
|
||||
*/
|
||||
|
||||
int (*ext_cd_init)(void (*notify_func)
|
||||
(struct platform_device *, int state));
|
||||
(void *dev_id, int state), void *dev_id);
|
||||
int (*ext_cd_cleanup)(void (*notify_func)
|
||||
(struct platform_device *, int state));
|
||||
(void *dev_id, int state), void *dev_id);
|
||||
|
||||
/*
|
||||
* Enable power to selected slot and set voltage to desired level.
|
||||
@@ -411,6 +509,19 @@ struct dw_mci_board {
|
||||
struct dw_mci_mon_table *tp_mon_tbl;
|
||||
unsigned int sw_timeout;
|
||||
bool use_gate_clock;
|
||||
bool enable_cclk_on_suspend;
|
||||
bool on_suspend;
|
||||
unsigned int dev_drv_str;
|
||||
void (*ext_setpower)(struct dw_mci *host, u32 flag);
|
||||
/* host->vmmc : SDcard power */
|
||||
#define DW_MMC_EXT_VMMC_ON BIT(0)
|
||||
/* host->vqmmc : SDcard I/F power */
|
||||
#define DW_MMC_EXT_VQMMC_ON BIT(1)
|
||||
|
||||
#if defined(CONFIG_MMC_DW_CMD_LOGGING)
|
||||
atomic_t log_count;
|
||||
bool dw_mci_cmd_logging;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* LINUX_MMC_DW_MMC_H */
|
||||
|
||||
23
include/linux/mmc/host.h
Normal file → Executable file
23
include/linux/mmc/host.h
Normal file → Executable file
@@ -61,6 +61,8 @@ struct mmc_ios {
|
||||
#define MMC_TIMING_UHS_DDR50 5
|
||||
#define MMC_TIMING_MMC_HS200 6
|
||||
#define MMC_TIMING_MMC_HS200_DDR 7
|
||||
#define MMC_TIMING_MMC_HS200_DDR_ES 8
|
||||
#define MMC_TIMING_MMC_HS200_DDR_ULP 9
|
||||
|
||||
#define MMC_SDR_MODE 0
|
||||
#define MMC_1_2V_DDR_MODE 1
|
||||
@@ -80,6 +82,7 @@ struct mmc_ios {
|
||||
#define MMC_SET_DRIVER_TYPE_A 1
|
||||
#define MMC_SET_DRIVER_TYPE_C 2
|
||||
#define MMC_SET_DRIVER_TYPE_D 3
|
||||
|
||||
};
|
||||
|
||||
struct mmc_host_ops {
|
||||
@@ -292,16 +295,25 @@ struct mmc_host {
|
||||
#define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \
|
||||
MMC_CAP2_PACKED_WR)
|
||||
#define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */
|
||||
#define MMC_CAP2_HS200_1_8V_DDR (1 << 12) /* can support */
|
||||
#define MMC_CAP2_HS200_1_2V_DDR (1 << 13) /* can support */
|
||||
#define MMC_CAP2_HS200_1_8V_DDR (1 << 15) /* can support */
|
||||
#define MMC_CAP2_HS200_1_2V_DDR (1 << 16) /* can support */
|
||||
#define MMC_CAP2_HS200_DDR (MMC_CAP2_HS200_1_8V_DDR | \
|
||||
MMC_CAP2_HS200_1_2V_SDR)
|
||||
MMC_CAP2_HS200_1_2V_DDR)
|
||||
#define MMC_CAP2_STROBE_ENHANCED (1 << 17) /* enhanced strobe */
|
||||
#define MMC_CAP2_CMDQ (MMC_CAP2_CACHE_CTRL | \
|
||||
(1 << 15)) /* Allow command queuing */
|
||||
#define MMC_CAP2_DEVICE_DRIVER (1 << 16) /* */
|
||||
(1 << 18)) /* Allow command queuing */
|
||||
|
||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||
|
||||
unsigned int dev_drv_str; /* device driver strength */
|
||||
#define MMC_DRIVER_TYPE_0 0 /* Default, x1 */
|
||||
#define MMC_DRIVER_TYPE_1 1 /* x1.5 */
|
||||
#define MMC_DRIVER_TYPE_2 2 /* x0.75 */
|
||||
#define MMC_DRIVER_TYPE_3 3 /* x0.5 */
|
||||
#define MMC_DRIVER_TYPE_4 4 /* x1.2 */
|
||||
#define MMC_DRIVER_TYPE_5 5 /* x2 */
|
||||
|
||||
|
||||
#ifdef CONFIG_MMC_CLKGATE
|
||||
int clk_requests; /* internal reference counter */
|
||||
unsigned int clk_delay; /* number of MCI clk hold cycles */
|
||||
@@ -415,6 +427,7 @@ struct mmc_host {
|
||||
} embedded_sdio_data;
|
||||
#endif
|
||||
|
||||
unsigned int blk_reset_cnt;
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
|
||||
28
include/linux/mmc/mmc.h
Normal file → Executable file
28
include/linux/mmc/mmc.h
Normal file → Executable file
@@ -302,6 +302,7 @@ struct _mmc_csd {
|
||||
#define EXT_CSD_PART_CONFIG 179 /* R/W */
|
||||
#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */
|
||||
#define EXT_CSD_BUS_WIDTH 183 /* R/W */
|
||||
#define EXT_CSD_STORBE_SUPPORT 184 /* R/W */
|
||||
#define EXT_CSD_HS_TIMING 185 /* R/W */
|
||||
#define EXT_CSD_POWER_CLASS 187 /* R/W */
|
||||
#define EXT_CSD_REV 192 /* RO */
|
||||
@@ -341,6 +342,22 @@ struct _mmc_csd {
|
||||
#define EXT_CSD_BKOPS_SUPPORT 502 /* RO */
|
||||
#define EXT_CSD_HPI_FEATURES 503 /* RO */
|
||||
|
||||
/* additional : eMMC v5.0 or later Only */
|
||||
#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYPE_B 269 /* RO */
|
||||
#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYPE_A 268 /* RO */
|
||||
#define EXT_CSD_PRE_EOL_INFO 267 /* RO */
|
||||
#define EXT_CSD_OPTIMAL_TRIM_UNIT_SIZE 264 /* RO */
|
||||
#define EXT_CSD_DEVICE_VERSION 262 /* RO, 2Byte */
|
||||
#define EXT_CSD_FIRMWARE_VERSION 254 /* RO, 8Byte */
|
||||
|
||||
/* additional : eMMC moviNAND VMX(PRE eMMC v5.0) device Only */
|
||||
#define EXT_CSD_PREv5_FIRMWARE_VERSION 259 /* RO */
|
||||
#define EXT_CSD_PREv5_CTRL_VERSION 258 /* RO */
|
||||
#define EXT_CSD_PREv5_OPT_ERASE_SIZE 257 /* RO */
|
||||
#define EXT_CSD_PREv5_OPT_WRITE_SIZE 256 /* RO */
|
||||
#define EXT_CSD_PREv5_PRE_EOL_INFO 255 /* RO */
|
||||
#define EXT_CSD_PREv5_LIFE_TIME_EST 254 /* RO */
|
||||
|
||||
/*
|
||||
* EXT_CSD field definitions
|
||||
*/
|
||||
@@ -387,6 +404,7 @@ struct _mmc_csd {
|
||||
#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */
|
||||
#define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */
|
||||
#define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */
|
||||
#define EXT_CSD_STROBE_ENHANCED_EN (1<<7) /* Card in enhanced strobe mode */
|
||||
|
||||
#define EXT_CSD_SEC_ER_EN BIT(0)
|
||||
#define EXT_CSD_SEC_BD_BLK_EN BIT(2)
|
||||
@@ -441,16 +459,6 @@ struct _mmc_csd {
|
||||
#define CID_MANFID_MICRON 0x13
|
||||
#define CID_MANFID_SAMSUNG 0x15
|
||||
|
||||
/*
|
||||
* Device Output Driver Type
|
||||
*/
|
||||
#define MMC_DRIVER_TYPE_0 0 /* Default, x1 */
|
||||
#define MMC_DRIVER_TYPE_1 1 /* x1.5 */
|
||||
#define MMC_DRIVER_TYPE_2 2 /* x0.75 */
|
||||
#define MMC_DRIVER_TYPE_3 3 /* x0.5 */
|
||||
#define MMC_DRIVER_TYPE_4 4 /* x1.2 */
|
||||
#define MMC_DRIVER_TYPE_5 5 /* x2 */
|
||||
|
||||
/*
|
||||
* HS_TIMING
|
||||
*/
|
||||
|
||||
32
include/linux/stlog.h
Executable file
32
include/linux/stlog.h
Executable file
@@ -0,0 +1,32 @@
|
||||
/* stlog internals
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_STLOG_H
|
||||
#define _LINUX_STLOG_H
|
||||
|
||||
#define STLOG_ACTION_CLOSE 0
|
||||
#define STLOG_ACTION_OPEN 1
|
||||
#define STLOG_ACTION_READ 2
|
||||
#define STLOG_ACTION_READ_ALL 3
|
||||
#define STLOG_ACTION_WRITE 4
|
||||
#define STLOG_ACTION_SIZE_BUFFER 5
|
||||
|
||||
#define STLOG_FROM_READER 0
|
||||
#define STLOG_FROM_PROC 1
|
||||
|
||||
#define CONFIG_STLOG
|
||||
|
||||
int do_stlog(int type, char __user *buf, int count, bool from_file);
|
||||
int do_stlog_write(int type, const char __user *buf, int count, bool from_file);
|
||||
int vstlog(const char *fmt, va_list args);
|
||||
int stlog(const char *fmt, ...);
|
||||
|
||||
#ifdef CONFIG_STLOG
|
||||
#define ST_LOG(fmt,...) stlog(fmt,##__VA_ARGS__)
|
||||
#else
|
||||
#define ST_LOG(fmt,...)
|
||||
#endif /* CONFIG_STLOG */
|
||||
|
||||
|
||||
#endif /* _STLOG_H */
|
||||
4
include/uapi/linux/mmc/ioctl.h
Normal file → Executable file
4
include/uapi/linux/mmc/ioctl.h
Normal file → Executable file
@@ -46,6 +46,8 @@ struct mmc_ioc_cmd {
|
||||
#define mmc_ioc_cmd_set_data(ic, ptr) ic.data_ptr = (__u64)(unsigned long) ptr
|
||||
|
||||
#define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd)
|
||||
#define MMC_IOC_BUSWIDTH _IO(MMC_BLOCK_MAJOR, 0xCB)
|
||||
#define MMC_IOC_CLOCK _IO(MMC_BLOCK_MAJOR, 0xCC)
|
||||
|
||||
/*
|
||||
* Since this ioctl is only meant to enhance (and not replace) normal access
|
||||
@@ -53,5 +55,5 @@ struct mmc_ioc_cmd {
|
||||
* is enforced per ioctl call. For larger data transfers, use the normal
|
||||
* block device operations.
|
||||
*/
|
||||
#define MMC_IOC_MAX_BYTES (512L * 256)
|
||||
#define MMC_IOC_MAX_BYTES (512L * 512)
|
||||
#endif /* LINUX_MMC_IOCTL_H */
|
||||
|
||||
Reference in New Issue
Block a user