unplugged-kernel/drivers/mmc/host/mediatek/ComboA/mtk_sd_misc.c

1282 lines
31 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <generated/autoconf.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/blkdev.h>
#include <linux/dma-mapping.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
#endif
#include "mtk_sd.h"
#include <mmc/core/core.h>
#include <mmc/core/card.h>
#include <mmc/core/mmc_ops.h>
#include <mt-plat/mtk_sd_misc.h>
#include "msdc_io.h"
#include "dbg.h"
#define PARTITION_NAME_LENGTH (64)
#define DRV_NAME_MISC "misc-sd"
#define DEBUG_MMC_IOCTL
#ifdef DEBUG_MMC_IOCTL
#define MMC_IOCTL_DECLARE_INT32(var) int var
#define MMC_IOCTL_PR_DBG(fmt, args...) pr_debug(fmt, ##args)
#else
#define MMC_IOCTL_DECLARE_INT32(...)
#define MMC_IOCTL_PR_DBG(fmt, args...)
#endif
/*
* For simple_sd_ioctl
*/
#define FORCE_IN_DMA (0x11)
#define FORCE_IN_PIO (0x10)
#define FORCE_NOTHING (0x0)
static u32 *sg_msdc_multi_buffer;
#define SG_MSDC_MULTI_BUFFER_SIZE (64 * 1024)
static int simple_sd_open(struct inode *inode, struct file *file)
{
return 0;
}
int msdc_reinit(struct msdc_host *host)
{
struct mmc_host *mmc;
struct mmc_card *card;
int ret = -1;
if (!host || !host->mmc || !host->mmc->card) {
pr_notice("Invalid msdc_host, mmc_host, or card");
return -1;
}
mmc = host->mmc;
card = mmc->card;
if (host->hw->host_function != MSDC_SD)
goto skip_reinit2;
if (host->block_bad_card)
ERR_MSG("Need block this bad SD card from re-initialization");
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE)
|| (host->block_bad_card != 0))
goto skip_reinit1;
mmc_get_card(card);
mmc->ios.timing = MMC_TIMING_LEGACY;
msdc_ops_set_ios(mmc, &mmc->ios);
/* FIX ME, check if bus_ops->reset() shall be un-commented */
/* power reset sdcard */
/* ret = mmc->bus_ops->reset(mmc); */
mmc_put_card(card);
ERR_MSG("Reinit %s", ret == 0 ? "success" : "fail");
skip_reinit1:
if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && (host->mmc->card)
&& mmc_card_present(host->mmc->card)
&& (!mmc_card_removed(host->mmc->card))
&& (host->block_bad_card == 0))
ret = 0;
skip_reinit2:
return ret;
}
static int simple_sd_ioctl_get_cid(struct msdc_ioctl *msdc_ctl)
{
struct msdc_host *host_ctl;
if (!msdc_ctl)
return -EINVAL;
host_ctl = mtk_msdc_host[msdc_ctl->host_num];
if (!host_ctl || !host_ctl->mmc || !host_ctl->mmc->card) {
pr_notice("host_ctl or mmc or card is NULL\n");
return -EINVAL;
}
MMC_IOCTL_PR_DBG("user want the cid in msdc slot%d\n",
msdc_ctl->host_num);
if (copy_to_user(msdc_ctl->buffer, &host_ctl->mmc->card->raw_cid, 16))
return -EFAULT;
MMC_IOCTL_PR_DBG("cid:0x%x,0x%x,0x%x,0x%x\n",
host_ctl->mmc->card->raw_cid[0],
host_ctl->mmc->card->raw_cid[1],
host_ctl->mmc->card->raw_cid[2],
host_ctl->mmc->card->raw_cid[3]);
return 0;
}
static int simple_sd_ioctl_set_bootpart(struct msdc_ioctl *msdc_ctl)
{
u8 *l_buf = NULL;
struct msdc_host *host_ctl;
struct mmc_host *mmc;
int ret = 0;
int bootpart = 0;
host_ctl = mtk_msdc_host[msdc_ctl->host_num];
if (!host_ctl || !host_ctl->mmc || !host_ctl->mmc->card) {
pr_notice("host_ctl or mmc or card is NULL\n");
return -EINVAL;
}
mmc = host_ctl->mmc;
if (msdc_ctl->buffer == NULL)
return -EINVAL;
mmc_get_card(mmc->card);
MMC_IOCTL_PR_DBG("user want set boot partition in msdc slot%d\n",
msdc_ctl->host_num);
ret = mmc_get_ext_csd(mmc->card, &l_buf);
if (ret) {
pr_debug("mmc_get_ext_csd error, set boot partition\n");
goto end;
}
if (copy_from_user(&bootpart, msdc_ctl->buffer, 1)) {
ret = -EFAULT;
goto end;
}
if ((bootpart != EMMC_BOOT1_EN)
&& (bootpart != EMMC_BOOT2_EN)
&& (bootpart != EMMC_BOOT_USER)) {
pr_debug("set boot partition error, not support %d\n",
bootpart);
ret = -EFAULT;
goto end;
}
if (((l_buf[EXT_CSD_PART_CFG] & 0x38) >> 3) != bootpart) {
/* active boot partition */
l_buf[EXT_CSD_PART_CFG] &= ~0x38;
l_buf[EXT_CSD_PART_CFG] |= (bootpart << 3);
pr_debug("mmc_switch set %x\n", l_buf[EXT_CSD_PART_CFG]);
ret = mmc_switch(mmc->card, 0, EXT_CSD_PART_CFG,
l_buf[EXT_CSD_PART_CFG], 1000);
if (ret) {
pr_debug("mmc_switch error, set boot partition\n");
} else {
mmc->card->ext_csd.part_config =
l_buf[EXT_CSD_PART_CFG];
}
}
end:
msdc_ctl->result = ret;
mmc_put_card(mmc->card);
kfree(l_buf);
return ret;
}
int simple_sd_ioctl_rw(struct msdc_ioctl *msdc_ctl)
{
struct scatterlist msdc_sg;
struct mmc_data msdc_data = { 0 };
struct mmc_command msdc_cmd = { 0 };
struct mmc_command msdc_stop;
int ret = 0;
char part_id;
int no_single_rw;
u32 total_size;
#ifdef CONFIG_MTK_EMMC_CQ_SUPPORT
int is_cmdq_en = false;
#endif
#ifdef MTK_MSDC_USE_CMD23
struct mmc_command msdc_sbc;
#endif
struct mmc_request msdc_mrq = { 0 };
struct msdc_host *host_ctl;
struct mmc_host *mmc;
if (!msdc_ctl)
return -EINVAL;
host_ctl = mtk_msdc_host[msdc_ctl->host_num];
if (!host_ctl || !host_ctl->mmc || !host_ctl->mmc->card) {
pr_notice("host_ctl or mmc or card is NULL\n");
return -EINVAL;
}
mmc = host_ctl->mmc;
if ((msdc_ctl->total_size <= 0) ||
(msdc_ctl->total_size > host_ctl->mmc->max_seg_size) ||
(msdc_ctl->total_size > SG_MSDC_MULTI_BUFFER_SIZE))
return -EINVAL;
total_size = msdc_ctl->total_size;
if (msdc_ctl->total_size > 512)
no_single_rw = 1;
else
no_single_rw = 0;
#ifdef MTK_MSDC_USE_CACHE
if (msdc_ctl->iswrite && mmc_card_mmc(mmc->card)
&& (mmc->card->ext_csd.cache_ctrl & 0x1))
no_single_rw = 1;
#endif
if (msdc_ctl->iswrite) {
if (msdc_ctl->opcode != MSDC_CARD_DUNM_FUNC) {
if (copy_from_user(sg_msdc_multi_buffer,
msdc_ctl->buffer, total_size)) {
dma_force[host_ctl->id] = FORCE_NOTHING;
ret = -EFAULT;
goto rw_end_without_release;
}
} else {
/* called from other kernel module */
memcpy(sg_msdc_multi_buffer, msdc_ctl->buffer,
total_size);
}
} else {
memset(sg_msdc_multi_buffer, 0, total_size);
}
mmc_get_card(mmc->card);
MMC_IOCTL_PR_DBG("user want access %d partition\n",
msdc_ctl->partition);
#ifdef CONFIG_MTK_EMMC_CQ_SUPPORT
if (mmc->card->ext_csd.cmdq_en) {
/* cmdq enabled, turn it off first */
pr_debug("[MSDC_DBG] cmdq enabled, turn it off\n");
ret = mmc_cmdq_disable(mmc->card);
if (ret) {
pr_debug("[MSDC_DBG] turn off cmdq en failed\n");
goto rw_end;
} else
is_cmdq_en = true;
}
#endif
if (host_ctl->hw->host_function == MSDC_EMMC) {
switch (msdc_ctl->partition) {
case EMMC_PART_BOOT1:
part_id = 1;
break;
case EMMC_PART_BOOT2:
part_id = 2;
break;
default:
/* make sure access partition is user data area */
part_id = 0;
break;
}
if (msdc_switch_part(host_ctl, part_id))
goto rw_end;
}
if (no_single_rw) {
memset(&msdc_stop, 0, sizeof(struct mmc_command));
#ifdef MTK_MSDC_USE_CMD23
memset(&msdc_sbc, 0, sizeof(struct mmc_command));
#endif
}
msdc_mrq.cmd = &msdc_cmd;
msdc_mrq.data = &msdc_data;
if (msdc_ctl->trans_type)
dma_force[host_ctl->id] = FORCE_IN_DMA;
else
dma_force[host_ctl->id] = FORCE_IN_PIO;
if (msdc_ctl->iswrite) {
msdc_data.flags = MMC_DATA_WRITE;
if (no_single_rw)
msdc_cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK;
else
msdc_cmd.opcode = MMC_WRITE_BLOCK;
msdc_data.blocks = total_size / 512;
} else {
msdc_data.flags = MMC_DATA_READ;
if (no_single_rw)
msdc_cmd.opcode = MMC_READ_MULTIPLE_BLOCK;
else
msdc_cmd.opcode = MMC_READ_SINGLE_BLOCK;
msdc_data.blocks = total_size / 512;
memset(sg_msdc_multi_buffer, 0, total_size);
}
#ifdef MTK_MSDC_USE_CMD23
if (no_single_rw == 0)
goto skip_sbc_prepare;
if ((mmc_card_mmc(mmc->card) || (mmc_card_sd(mmc->card)
&& mmc->card->scr.cmds & SD_SCR_CMD23_SUPPORT))
&& !(mmc->card->quirks & MMC_QUIRK_BLK_NO_CMD23)) {
msdc_mrq.sbc = &msdc_sbc;
msdc_mrq.sbc->opcode = MMC_SET_BLOCK_COUNT;
#ifdef MTK_MSDC_USE_CACHE
/* if ioctl access cacheable partition data,
* there is on flush mechanism in msdc driver
* so do reliable write .
*/
if (mmc_card_mmc(mmc->card)
&& (mmc->card->ext_csd.cache_ctrl & 0x1)
&& (msdc_cmd.opcode == MMC_WRITE_MULTIPLE_BLOCK))
msdc_mrq.sbc->arg = msdc_data.blocks | (1 << 31);
else
msdc_mrq.sbc->arg = msdc_data.blocks;
#else
msdc_mrq.sbc->arg = msdc_data.blocks;
#endif
msdc_mrq.sbc->flags = MMC_RSP_R1 | MMC_CMD_AC;
}
skip_sbc_prepare:
#endif
msdc_cmd.arg = msdc_ctl->address;
if (!mmc_card_blockaddr(mmc->card)) {
pr_debug("this device use byte address!!\n");
msdc_cmd.arg <<= 9;
}
msdc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
if (no_single_rw) {
msdc_stop.opcode = MMC_STOP_TRANSMISSION;
msdc_stop.arg = 0;
msdc_stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
msdc_data.stop = &msdc_stop;
} else {
msdc_data.stop = NULL;
}
msdc_data.blksz = 512;
msdc_data.sg = &msdc_sg;
msdc_data.sg_len = 1;
MMC_IOCTL_PR_DBG("total size is %d\n", total_size);
MMC_IOCTL_PR_DBG("ueser buf address is 0x%p!\n", msdc_ctl->buffer);
sg_init_one(&msdc_sg, sg_msdc_multi_buffer, total_size);
mmc_set_data_timeout(&msdc_data, mmc->card);
mmc_wait_for_req(mmc, &msdc_mrq);
if (msdc_ctl->partition)
msdc_switch_part(host_ctl, 0);
#ifdef CONFIG_MTK_EMMC_CQ_SUPPORT
if (is_cmdq_en) {
pr_debug("[MSDC_DBG] turn on cmdq\n");
ret = mmc_cmdq_enable(host_ctl->mmc->card);
if (ret)
pr_debug("[MSDC_DBG] turn on cmdq en failed\n");
else
is_cmdq_en = false;
}
#endif
mmc_put_card(mmc->card);
if (!msdc_ctl->iswrite) {
if (msdc_ctl->opcode != MSDC_CARD_DUNM_FUNC) {
if (copy_to_user(msdc_ctl->buffer, sg_msdc_multi_buffer,
total_size)) {
dma_force[host_ctl->id] = FORCE_NOTHING;
ret = -EFAULT;
goto rw_end_without_release;
}
} else {
/* called from other kernel module */
memcpy(msdc_ctl->buffer, sg_msdc_multi_buffer,
total_size);
}
}
/* clear the global buffer of R/W IOCTL */
memset(sg_msdc_multi_buffer, 0, total_size);
goto rw_end_without_release;
rw_end:
#ifdef CONFIG_MTK_EMMC_CQ_SUPPORT
if (is_cmdq_en) {
pr_debug("[MSDC_DBG] turn on cmdq\n");
ret = mmc_cmdq_enable(mmc->card);
if (ret)
pr_debug("[MSDC_DBG] turn on cmdq en failed\n");
else
is_cmdq_en = false;
}
#endif
mmc_put_card(mmc->card);
rw_end_without_release:
if (ret)
msdc_ctl->result = ret;
if (msdc_cmd.error)
msdc_ctl->result = msdc_cmd.error;
if (msdc_data.error)
msdc_ctl->result = msdc_data.error;
else
msdc_ctl->result = 0;
dma_force[host_ctl->id] = FORCE_NOTHING;
return msdc_ctl->result;
}
#ifdef CONFIG_PWR_LOSS_MTK_TEST
static int sd_ioctl_reinit(struct msdc_ioctl *msdc_ctl)
{
struct msdc_host *host = mtk_msdc_host[1];
if (host != NULL)
return msdc_reinit(host);
else
return -EINVAL;
}
static int sd_ioctl_cd_pin_en(struct msdc_ioctl *msdc_ctl)
{
struct msdc_host *host = mtk_msdc_host[1];
if (host != NULL)
return (host->mmc->caps & MMC_CAP_NONREMOVABLE)
== MMC_CAP_NONREMOVABLE;
else
return -EINVAL;
}
static int simple_sd_ioctl_get_csd(struct msdc_ioctl *msdc_ctl)
{
struct msdc_host *host_ctl;
if (!msdc_ctl)
return -EINVAL;
host_ctl = mtk_msdc_host[msdc_ctl->host_num];
if (!host_ctl || !host_ctl->mmc || !host_ctl->mmc->card) {
pr_notice("host_ctl or mmc or card is NULL\n");
return -EINVAL;
}
MMC_IOCTL_PR_DBG("user want the csd in msdc slot%d\n",
msdc_ctl->host_num);
if (copy_to_user(msdc_ctl->buffer, &host_ctl->mmc->card->raw_csd, 16))
return -EFAULT;
MMC_IOCTL_PR_DBG("csd:0x%x,0x%x,0x%x,0x%x\n",
host_ctl->mmc->card->raw_csd[0],
host_ctl->mmc->card->raw_csd[1],
host_ctl->mmc->card->raw_csd[2],
host_ctl->mmc->card->raw_csd[3]);
return 0;
}
static int simple_sd_ioctl_get_bootpart(struct msdc_ioctl *msdc_ctl)
{
u8 *l_buf = NULL;
struct msdc_host *host_ctl;
struct mmc_host *mmc;
int ret = 0;
int bootpart = 0;
unsigned int user_buffer;
host_ctl = mtk_msdc_host[msdc_ctl->host_num];
if (!host_ctl || !host_ctl->mmc || !host_ctl->mmc->card) {
pr_notice("host_ctl or mmc or card is NULL\n");
return -EINVAL;
}
mmc = host_ctl->mmc;
if (get_user(user_buffer, msdc_ctl->buffer))
return -EINVAL;
mmc_get_card(mmc->card);
MMC_IOCTL_PR_DBG("user want get boot partition info in msdc slot%d\n",
msdc_ctl->host_num);
ret = mmc_get_ext_csd(mmc->card, &l_buf);
if (ret) {
pr_debug("mmc_get_ext_csd error, get boot part\n");
goto end;
}
bootpart = (l_buf[EXT_CSD_PART_CFG] & 0x38) >> 3;
MMC_IOCTL_PR_DBG("bootpart Byte[EXT_CSD_PART_CFG] =%x, booten=%x\n",
l_buf[EXT_CSD_PART_CFG], bootpart);
if (msdc_ctl->opcode != MSDC_CARD_DUNM_FUNC) {
if (copy_to_user(msdc_ctl->buffer, &bootpart, 1)) {
ret = -EFAULT;
goto end;
}
} else {
/* called from other kernel module */
memcpy(msdc_ctl->buffer, &bootpart, 1);
}
end:
msdc_ctl->result = ret;
mmc_put_card(mmc->card);
kfree(l_buf);
return ret;
}
static int simple_sd_ioctl_get_partition_size(struct msdc_ioctl *msdc_ctl)
{
int ret = 0;
struct msdc_host *host_ctl;
unsigned long long partitionsize = 0;
struct mmc_host *mmc;
host_ctl = mtk_msdc_host[msdc_ctl->host_num];
if (!host_ctl || !host_ctl->mmc || !host_ctl->mmc->card) {
pr_notice("host_ctl or mmc or card is NULL\n");
return -EINVAL;
}
mmc = host_ctl->mmc;
mmc_get_card(mmc->card);
MMC_IOCTL_PR_DBG("get size of partition=%d\n", msdc_ctl->partition);
switch (msdc_ctl->partition) {
case EMMC_PART_BOOT1:
partitionsize = msdc_get_other_capacity(host_ctl, "boot0");
break;
case EMMC_PART_BOOT2:
partitionsize = msdc_get_other_capacity(host_ctl, "boot1");
break;
case EMMC_PART_RPMB:
partitionsize = msdc_get_other_capacity(host_ctl, "rpmb");
break;
case EMMC_PART_USER:
partitionsize = msdc_get_user_capacity(host_ctl);
break;
default:
pr_debug("not support partition =%d\n", msdc_ctl->partition);
partitionsize = 0;
break;
}
MMC_IOCTL_PR_DBG("bootpart partitionsize =%llx\n", partitionsize);
if (copy_to_user(msdc_ctl->buffer, &partitionsize, 8))
ret = -EFAULT;
msdc_ctl->result = ret;
mmc_put_card(mmc->card);
return ret;
}
static int simple_sd_ioctl_set_driving(struct msdc_ioctl *msdc_ctl)
{
void __iomem *base;
struct msdc_host *host;
/* cannot access ioctl except of Engineer Mode */
if (strcmp(current->comm, "em_svr"))
return -EINVAL;
host = mtk_msdc_host[msdc_ctl->host_num];
if (host == NULL)
return -EINVAL;
base = host->base;
msdc_clk_enable(host);
MMC_IOCTL_PR_DBG("set: clk driving is 0x%x\n",
msdc_ctl->clk_pu_driving);
MMC_IOCTL_PR_DBG("set: cmd driving is 0x%x\n",
msdc_ctl->cmd_pu_driving);
MMC_IOCTL_PR_DBG("set: dat driving is 0x%x\n",
msdc_ctl->dat_pu_driving);
MMC_IOCTL_PR_DBG("set: rst driving is 0x%x\n",
msdc_ctl->rst_pu_driving);
MMC_IOCTL_PR_DBG("set: ds driving is 0x%x\n",
msdc_ctl->ds_pu_driving);
host->hw->driving_applied->clk_drv = msdc_ctl->clk_pu_driving;
host->hw->driving_applied->cmd_drv = msdc_ctl->cmd_pu_driving;
host->hw->driving_applied->dat_drv = msdc_ctl->dat_pu_driving;
host->hw->driving_applied->rst_drv = msdc_ctl->rst_pu_driving;
host->hw->driving_applied->ds_drv = msdc_ctl->ds_pu_driving;
msdc_set_driving(host, host->hw->driving_applied);
msdc_clk_disable(host);
return 0;
}
static int simple_sd_ioctl_get_driving(struct msdc_ioctl *msdc_ctl)
{
void __iomem *base;
struct msdc_host *host;
struct msdc_hw_driving driving = {0};
host = mtk_msdc_host[msdc_ctl->host_num];
if (host == NULL)
return -EINVAL;
base = host->base;
msdc_clk_enable(host);
msdc_get_driving(host, &driving);
msdc_ctl->clk_pu_driving = driving.clk_drv;
msdc_ctl->cmd_pu_driving = driving.cmd_drv;
msdc_ctl->dat_pu_driving = driving.dat_drv;
if (host->id == 0) {
msdc_ctl->rst_pu_driving = driving.rst_drv;
msdc_ctl->ds_pu_driving = driving.ds_drv;
} else {
msdc_ctl->rst_pu_driving = 0;
msdc_ctl->ds_pu_driving = 0;
}
MMC_IOCTL_PR_DBG("read: clk driving is 0x%x\n",
msdc_ctl->clk_pu_driving);
MMC_IOCTL_PR_DBG("read: cmd driving is 0x%x\n",
msdc_ctl->cmd_pu_driving);
MMC_IOCTL_PR_DBG("read: dat driving is 0x%x\n",
msdc_ctl->dat_pu_driving);
MMC_IOCTL_PR_DBG("read: rst driving is 0x%x\n",
msdc_ctl->rst_pu_driving);
MMC_IOCTL_PR_DBG("read: ds driving is 0x%x\n",
msdc_ctl->ds_pu_driving);
msdc_clk_disable(host);
return 0;
}
/* to ensure format operate is clean the emmc device fully(partition erase) */
#define MBR_PART_NUM 6
#define __MMC_ERASE_ARG 0x00000000
#define __MMC_TRIM_ARG 0x00000001
#define __MMC_DISCARD_ARG 0x00000003
/* call mmc block layer interface for userspace to do erase operate */
static int simple_mmc_erase_func(unsigned int start, unsigned int size)
{
struct msdc_host *host;
struct mmc_host *mmc;
unsigned int arg;
host = mtk_msdc_host[0];
if (!host || !host->mmc || !host->mmc->card) {
pr_notice("host or mmc or card is NULL\n");
return -EINVAL;
}
mmc = host->mmc;
mmc_get_card(mmc->card);
if (mmc_can_discard(mmc->card)) {
arg = __MMC_DISCARD_ARG;
} else if (mmc_can_trim(mmc->card)) {
arg = __MMC_TRIM_ARG;
} else if (mmc_can_erase(mmc->card)) {
arg = __MMC_ERASE_ARG;
} else {
pr_notice("[%s]: emmc card can't support trim / discard / erase\n",
__func__);
goto end;
}
msdc_switch_part(host, 0);
pr_debug("[%s]: start=0x%x, size=%d, arg=0x%x, can_trim=(0x%x), EXT_CSD_SEC_GB_CL_EN=0x%lx\n",
__func__, start, size, arg,
mmc->card->ext_csd.sec_feature_support,
EXT_CSD_SEC_GB_CL_EN);
mmc_erase(mmc->card, start, size, arg);
MMC_IOCTL_PR_DBG("[%s]: erase done....arg=0x%x\n", __func__, arg);
end:
mmc_put_card(mmc->card);
return 0;
}
#ifdef CONFIG_PWR_LOSS_MTK_TEST
/* These definitiona and functions are coded by reference to
* mmc_blk_issue_discard_rq()@block.c
*/
#define INAND_CMD38_ARG_EXT_CSD 113
#define INAND_CMD38_ARG_ERASE 0x00
#define INAND_CMD38_ARG_TRIM 0x01
#define INAND_CMD38_ARG_SECERASE 0x80
#define INAND_CMD38_ARG_SECTRIM1 0x81
#define INAND_CMD38_ARG_SECTRIM2 0x88
static int simple_sd_ioctl_erase_selected_area(struct msdc_ioctl *msdc_ctl)
{
struct msdc_host *host_ctl;
struct mmc_host *mmc;
unsigned int from, nr, arg;
int err = 0;
host_ctl = mtk_msdc_host[msdc_ctl->host_num];
if (!host_ctl || !host_ctl->mmc || !host_ctl->mmc->card) {
pr_notice("host_ctl or mmc or card is NULL\n");
return -EINVAL;
}
mmc = host_ctl->mmc;
mmc_get_card(mmc->card);
msdc_switch_part(host_ctl, 0);
if (!mmc_can_erase(mmc->card)) {
err = -EOPNOTSUPP;
goto out;
}
from = msdc_ctl->address;
nr = msdc_ctl->total_size;
if (mmc_can_discard(mmc->card))
arg = MMC_DISCARD_ARG;
else if (mmc_can_trim(mmc->card))
arg = MMC_TRIM_ARG;
else
arg = MMC_ERASE_ARG;
MMC_IOCTL_PR_DBG("Erase range %x~%x\n", from, from + nr - 1);
if (mmc_card_mmc(mmc->card)) {
if (mmc->card->quirks & MMC_QUIRK_INAND_CMD38) {
err = mmc_switch(mmc->card, EXT_CSD_CMD_SET_NORMAL,
INAND_CMD38_ARG_EXT_CSD,
arg == MMC_TRIM_ARG ?
INAND_CMD38_ARG_TRIM :
INAND_CMD38_ARG_ERASE,
0);
if (err)
goto out;
}
}
err = mmc_erase(mmc->card, from, nr, arg);
out:
mmc_put_card(mmc->card);
msdc_ctl->result = err;
return msdc_ctl->result;
}
#endif
static int simple_mmc_erase_partition(unsigned char *name)
{
struct hd_struct part = {0};
/* just support erase cache & data partition now */
if (name &&
(strncmp(name, "usrdata", 7) == 0 ||
strncmp(name, "cache", 5) == 0)) {
/* find erase start address and erase size,
* just support high capacity emmc card now
*/
if (msdc_get_part_info(name, &part)) {
pr_debug("erase %s, start sector: 0x%x, size: 0x%x\n",
name, (u32)part.start_sect, (u32)part.nr_sects);
simple_mmc_erase_func(part.start_sect, part.nr_sects);
}
}
return 0;
}
static int simple_mmc_erase_partition_wrap(struct msdc_ioctl *msdc_ctl)
{
unsigned char name[PARTITION_NAME_LENGTH];
if (!msdc_ctl)
return -EINVAL;
if (msdc_ctl->total_size >= PARTITION_NAME_LENGTH)
return -EFAULT;
if (copy_from_user(name, (unsigned char *)msdc_ctl->buffer,
msdc_ctl->total_size))
return -EFAULT;
name[msdc_ctl->total_size] = 0;
return simple_mmc_erase_partition(name);
}
#endif
static long simple_sd_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct msdc_ioctl *msdc_ctl;
#ifdef CONFIG_PWR_LOSS_MTK_TEST
struct msdc_host *host;
#endif
int ret = 0;
if ((struct msdc_ioctl *)arg == NULL) {
switch (cmd) {
#ifdef CONFIG_PWR_LOSS_MTK_TEST
case MSDC_REINIT_SDCARD:
pr_info("sd ioctl re-init!!\n");
ret = sd_ioctl_reinit((struct msdc_ioctl *)arg);
break;
case MSDC_CD_PIN_EN_SDCARD:
pr_info("sd ioctl cd pin\n");
ret = sd_ioctl_cd_pin_en((struct msdc_ioctl *)arg);
break;
case MSDC_SD_POWER_OFF:
pr_info("sd ioctl power off!!!\n");
host = mtk_msdc_host[1];
if (host && host->mmc) {
mmc_claim_host(host->mmc);
mmc_power_off(host->mmc);
mmc_release_host(host->mmc);
}
break;
case MSDC_SD_POWER_ON:
pr_info("sd ioctl power on!!!\n");
host = mtk_msdc_host[1];
/* FIX ME: kernel 3.18 does not provide
* mmc_resume_host,
* check if mmc_power_restore_host can be used
*/
/* ret = mmc_resume_host(host->mmc); */
/* ret = mmc_power_restore_host(host->mmc); */
break;
#endif
default:
pr_notice("mt_sd_ioctl:this opcode value is illegal!!\n");
return -EINVAL;
}
return ret;
}
msdc_ctl = kmalloc(sizeof(*msdc_ctl), GFP_KERNEL);
if (!msdc_ctl)
return -ENOMEM;
if (copy_from_user(msdc_ctl, (struct msdc_ioctl *)arg,
sizeof(struct msdc_ioctl))) {
kfree(msdc_ctl);
return -EFAULT;
}
if (msdc_ctl->opcode != MSDC_ERASE_PARTITION) {
if ((msdc_ctl->host_num < 0)
|| (msdc_ctl->host_num >= HOST_MAX_NUM)) {
pr_notice("invalid host num: %d\n", msdc_ctl->host_num);
kfree(msdc_ctl);
return -EINVAL;
}
}
switch (msdc_ctl->opcode) {
case MSDC_GET_CID:
msdc_ctl->result = simple_sd_ioctl_get_cid(msdc_ctl);
break;
case MSDC_SET_BOOTPART:
msdc_ctl->result =
simple_sd_ioctl_set_bootpart(msdc_ctl);
break;
#ifdef CONFIG_PWR_LOSS_MTK_TEST
case MSDC_SINGLE_READ_WRITE:
case MSDC_MULTIPLE_READ_WRITE:
msdc_ctl->result = simple_sd_ioctl_rw(msdc_ctl);
break;
case MSDC_GET_CSD:
msdc_ctl->result = simple_sd_ioctl_get_csd(msdc_ctl);
break;
case MSDC_DRIVING_SETTING:
if (msdc_ctl->iswrite == 1) {
msdc_ctl->result =
simple_sd_ioctl_set_driving(msdc_ctl);
} else {
msdc_ctl->result =
simple_sd_ioctl_get_driving(msdc_ctl);
}
break;
case MSDC_ERASE_PARTITION:
/* Used by ftp_emmc.c of factory and roots.cpp of recovery */
msdc_ctl->result =
simple_mmc_erase_partition_wrap(msdc_ctl);
break;
#ifdef CONFIG_PWR_LOSS_MTK_TEST
case MSDC_ERASE_SELECTED_AREA:
msdc_ctl->result = simple_sd_ioctl_erase_selected_area(
msdc_ctl);
break;
#endif
case MSDC_SD30_MODE_SWITCH:
pr_notice("obsolete opcode!!\n");
kfree(msdc_ctl);
return -EINVAL;
case MSDC_GET_BOOTPART:
msdc_ctl->result =
simple_sd_ioctl_get_bootpart(msdc_ctl);
break;
case MSDC_GET_PARTSIZE:
msdc_ctl->result =
simple_sd_ioctl_get_partition_size(msdc_ctl);
break;
#endif
default:
pr_notice("%s:invlalid opcode!!\n", __func__);
kfree(msdc_ctl);
return -EINVAL;
}
if (copy_to_user((struct msdc_ioctl *)arg, msdc_ctl,
sizeof(struct msdc_ioctl))) {
kfree(msdc_ctl);
return -EFAULT;
}
ret = msdc_ctl->result;
kfree(msdc_ctl);
return ret;
}
#ifdef CONFIG_COMPAT
struct compat_simple_sd_ioctl {
compat_int_t opcode;
compat_int_t host_num;
compat_int_t iswrite;
compat_int_t trans_type;
compat_uint_t total_size;
compat_uint_t address;
compat_uptr_t buffer;
compat_int_t cmd_pu_driving;
compat_int_t cmd_pd_driving;
compat_int_t dat_pu_driving;
compat_int_t dat_pd_driving;
compat_int_t clk_pu_driving;
compat_int_t clk_pd_driving;
compat_int_t ds_pu_driving;
compat_int_t ds_pd_driving;
compat_int_t rst_pu_driving;
compat_int_t rst_pd_driving;
compat_int_t clock_freq;
compat_int_t partition;
compat_int_t hopping_bit;
compat_int_t hopping_time;
compat_int_t result;
compat_int_t sd30_mode;
compat_int_t sd30_max_current;
compat_int_t sd30_drive;
compat_int_t sd30_power_control;
};
static int compat_get_simple_ion_allocation(
struct compat_simple_sd_ioctl __user *arg32,
struct msdc_ioctl __user *arg64)
{
compat_int_t i;
compat_uint_t u;
compat_uptr_t p;
int err;
err = get_user(i, &(arg32->opcode));
err |= put_user(i, &(arg64->opcode));
err |= get_user(i, &(arg32->host_num));
err |= put_user(i, &(arg64->host_num));
err |= get_user(i, &(arg32->iswrite));
err |= put_user(i, &(arg64->iswrite));
err |= get_user(i, &(arg32->trans_type));
err |= put_user(i, &(arg64->trans_type));
err |= get_user(u, &(arg32->total_size));
err |= put_user(u, &(arg64->total_size));
err |= get_user(u, &(arg32->address));
err |= put_user(u, &(arg64->address));
err |= get_user(p, &(arg32->buffer));
err |= put_user(compat_ptr(p), &(arg64->buffer));
err |= get_user(i, &(arg32->cmd_pu_driving));
err |= put_user(i, &(arg64->cmd_pu_driving));
err |= get_user(i, &(arg32->cmd_pd_driving));
err |= put_user(i, &(arg64->cmd_pd_driving));
err |= get_user(i, &(arg32->dat_pu_driving));
err |= put_user(i, &(arg64->dat_pu_driving));
err |= get_user(i, &(arg32->dat_pd_driving));
err |= put_user(i, &(arg64->dat_pd_driving));
err |= get_user(i, &(arg32->clk_pu_driving));
err |= put_user(i, &(arg64->clk_pu_driving));
err |= get_user(i, &(arg32->clk_pd_driving));
err |= put_user(i, &(arg64->clk_pd_driving));
err |= get_user(i, &(arg32->ds_pu_driving));
err |= put_user(i, &(arg64->ds_pu_driving));
err |= get_user(i, &(arg32->ds_pd_driving));
err |= put_user(i, &(arg64->ds_pd_driving));
err |= get_user(i, &(arg32->rst_pu_driving));
err |= put_user(i, &(arg64->rst_pu_driving));
err |= get_user(i, &(arg32->rst_pd_driving));
err |= put_user(i, &(arg64->rst_pd_driving));
err |= get_user(i, &(arg32->clock_freq));
err |= put_user(i, &(arg64->clock_freq));
err |= get_user(i, &(arg32->partition));
err |= put_user(i, &(arg64->partition));
err |= get_user(i, &(arg32->hopping_bit));
err |= put_user(i, &(arg64->hopping_bit));
err |= get_user(i, &(arg32->hopping_time));
err |= put_user(i, &(arg64->hopping_time));
err |= get_user(i, &(arg32->result));
err |= put_user(i, &(arg64->result));
err |= get_user(i, &(arg32->sd30_mode));
err |= put_user(i, &(arg64->sd30_mode));
err |= get_user(i, &(arg32->sd30_max_current));
err |= put_user(i, &(arg64->sd30_max_current));
err |= get_user(i, &(arg32->sd30_drive));
err |= put_user(i, &(arg64->sd30_drive));
err |= get_user(i, &(arg32->sd30_power_control));
err |= put_user(i, &(arg64->sd30_power_control));
return err;
}
static int compat_put_simple_ion_allocation(
struct compat_simple_sd_ioctl __user *arg32,
struct msdc_ioctl __user *arg64)
{
compat_int_t i;
compat_uint_t u;
int err;
err = get_user(i, &(arg64->opcode));
err |= put_user(i, &(arg32->opcode));
err |= get_user(i, &(arg64->host_num));
err |= put_user(i, &(arg32->host_num));
err |= get_user(i, &(arg64->iswrite));
err |= put_user(i, &(arg32->iswrite));
err |= get_user(i, &(arg64->trans_type));
err |= put_user(i, &(arg32->trans_type));
err |= get_user(u, &(arg64->total_size));
err |= put_user(u, &(arg32->total_size));
err |= get_user(u, &(arg64->address));
err |= put_user(u, &(arg32->address));
err |= get_user(i, &(arg64->cmd_pu_driving));
err |= put_user(i, &(arg32->cmd_pu_driving));
err |= get_user(i, &(arg64->cmd_pd_driving));
err |= put_user(i, &(arg32->cmd_pd_driving));
err |= get_user(i, &(arg64->dat_pu_driving));
err |= put_user(i, &(arg32->dat_pu_driving));
err |= get_user(i, &(arg64->dat_pd_driving));
err |= put_user(i, &(arg32->dat_pd_driving));
err |= get_user(i, &(arg64->clk_pu_driving));
err |= put_user(i, &(arg32->clk_pu_driving));
err |= get_user(i, &(arg64->clk_pd_driving));
err |= put_user(i, &(arg32->clk_pd_driving));
err |= get_user(i, &(arg64->ds_pu_driving));
err |= put_user(i, &(arg32->ds_pu_driving));
err |= get_user(i, &(arg64->ds_pd_driving));
err |= put_user(i, &(arg32->ds_pd_driving));
err |= get_user(i, &(arg64->rst_pu_driving));
err |= put_user(i, &(arg32->rst_pu_driving));
err |= get_user(i, &(arg64->rst_pd_driving));
err |= put_user(i, &(arg32->rst_pd_driving));
err |= get_user(i, &(arg64->clock_freq));
err |= put_user(i, &(arg32->clock_freq));
err |= get_user(i, &(arg64->partition));
err |= put_user(i, &(arg32->partition));
err |= get_user(i, &(arg64->hopping_bit));
err |= put_user(i, &(arg32->hopping_bit));
err |= get_user(i, &(arg64->hopping_time));
err |= put_user(i, &(arg32->hopping_time));
err |= get_user(i, &(arg64->result));
err |= put_user(i, &(arg32->result));
err |= get_user(i, &(arg64->sd30_mode));
err |= put_user(i, &(arg32->sd30_mode));
err |= get_user(i, &(arg64->sd30_max_current));
err |= put_user(i, &(arg32->sd30_max_current));
err |= get_user(i, &(arg64->sd30_drive));
err |= put_user(i, &(arg32->sd30_drive));
err |= get_user(i, &(arg64->sd30_power_control));
err |= put_user(i, &(arg32->sd30_power_control));
return err;
}
static long simple_sd_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct compat_simple_sd_ioctl *arg32;
struct msdc_ioctl *arg64;
compat_int_t k_opcode;
int err, ret;
if (!file->f_op || !file->f_op->unlocked_ioctl) {
pr_notice("f_op or unlocked ioctl is NULL.\n");
return -ENOTTY;
}
if ((struct msdc_ioctl *)arg == NULL) {
ret = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg);
return ret;
}
arg32 = compat_ptr(arg);
arg64 = compat_alloc_user_space(sizeof(*arg64));
if (arg64 == NULL)
return -EFAULT;
if (!access_ok(VERIFY_WRITE, arg32, sizeof(*arg32)) ||
!access_ok(VERIFY_WRITE, arg64, sizeof(*arg64))) {
return -EFAULT;
}
err = compat_get_simple_ion_allocation(arg32, arg64);
if (err)
return err;
err = get_user(k_opcode, &(arg64->opcode));
if (err)
return err;
ret = file->f_op->unlocked_ioctl(file, (unsigned int)k_opcode,
(unsigned long)arg64);
err = compat_put_simple_ion_allocation(arg32, arg64);
return ret ? ret : err;
}
#endif
static const struct file_operations simple_msdc_em_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = simple_sd_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = simple_sd_compat_ioctl,
#endif
.open = simple_sd_open,
};
static struct miscdevice simple_msdc_em_dev[] = {
{
.minor = MISC_DYNAMIC_MINOR,
.name = "misc-sd",
.fops = &simple_msdc_em_fops,
}
};
static int simple_sd_probe(struct platform_device *pdev)
{
int ret = 0;
pr_debug("in misc_sd_probe function\n");
return ret;
}
static int simple_sd_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_driver simple_sd_driver = {
.probe = simple_sd_probe,
.remove = simple_sd_remove,
.driver = {
.name = DRV_NAME_MISC,
.owner = THIS_MODULE,
},
};
static int __init simple_sd_init(void)
{
int ret;
sg_msdc_multi_buffer = kmalloc(SG_MSDC_MULTI_BUFFER_SIZE, GFP_KERNEL);
if (sg_msdc_multi_buffer == NULL)
return 0;
ret = platform_driver_register(&simple_sd_driver);
if (ret) {
pr_notice(DRV_NAME_MISC ": Can't register driver\n");
return ret;
}
pr_debug(DRV_NAME_MISC ": MediaTek simple SD/MMC Card Driver\n");
/*msdc0 is for emmc only, just for emmc */
/* ret = misc_register(&simple_msdc_em_dev[host->id]); */
ret = misc_register(&simple_msdc_em_dev[0]);
if (ret) {
pr_notice("register MSDC Slot[0] misc driver failed (%d)\n",
ret);
return ret;
}
return 0;
}
static void __exit simple_sd_exit(void)
{
if (sg_msdc_multi_buffer != NULL) {
kfree(sg_msdc_multi_buffer);
sg_msdc_multi_buffer = NULL;
}
misc_deregister(&simple_msdc_em_dev[0]);
platform_driver_unregister(&simple_sd_driver);
}
module_init(simple_sd_init);
module_exit(simple_sd_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("simple MediaTek SD/MMC Card Driver");
MODULE_AUTHOR("feifei.wang <feifei.wang@mediatek.com>");