odroid-xu3/xu4:emmc5.1 microSD R/W stability modify.

Change-Id: Ia482e3f1ad86ffac71057334bbc5afab27c4eb69
This commit is contained in:
Kevin Kim
2017-07-26 17:22:25 +09:00
parent 9c8669133b
commit 5f1c44fc86
31 changed files with 3366 additions and 569 deletions

31
arch/arm/boot/dts/exynos5422-odroidxu3.dts Normal file → Executable file
View 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
View 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

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

File diff suppressed because it is too large Load Diff

93
drivers/mmc/host/dw_mmc-exynos.h Normal file → Executable file
View 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
View 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

File diff suppressed because it is too large Load Diff

14
drivers/mmc/host/dw_mmc.h Normal file → Executable file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 */