drivers/mmcsd: Add MMC_IOC_CMD ioctl

Signed-off-by: helei8 <helei8@xiaomi.com>
This commit is contained in:
helei8
2022-12-05 19:54:31 +08:00
committed by Alan Carvalho de Assis
parent ff68d9ac04
commit 4b4004b874
3 changed files with 438 additions and 7 deletions
+6
View File
@@ -27,6 +27,12 @@ menuconfig MMCSD
if MMCSD
config MMCSD_IOCSUPPORT
int "Enable MMC/SD ioctl support"
default y
---help---
Disable it to save some code size if required.
config MMCSD_NSLOTS
int "Number of MMC/SD slots"
default 1
+383
View File
@@ -221,6 +221,12 @@ static int mmcsd_probe(FAR struct mmcsd_state_s *priv);
static int mmcsd_removed(FAR struct mmcsd_state_s *priv);
static int mmcsd_hwinitialize(FAR struct mmcsd_state_s *priv);
static void mmcsd_hwuninitialize(FAR struct mmcsd_state_s *priv);
#ifdef CONFIG_MMCSD_IOCSUPPORT
static int mmcsd_iocmd(FAR struct mmcsd_state_s *priv,
FAR struct mmc_ioc_cmd *ic_ptr);
static int mmcsd_multi_iocmd(FAR struct mmcsd_state_s *priv,
FAR struct mmc_ioc_multi_cmd *imc_ptr);
#endif
/****************************************************************************
* Private Data
@@ -2342,6 +2348,30 @@ static int mmcsd_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
}
break;
#ifdef CONFIG_MMCSD_IOCSUPPORT
case MMC_IOC_CMD: /* MMCSD device ioctl commands */
{
finfo("MMC_IOC_CMD\n");
ret = mmcsd_iocmd(priv, (FAR struct mmc_ioc_cmd *)arg);
if (ret != OK)
{
ferr("ERROR: mmcsd_iocmd failed: %d\n", ret);
}
}
break;
case MMC_IOC_MULTI_CMD: /* MMCSD device ioctl muti commands */
{
finfo("MMC_IOC_MULTI_CMD\n");
ret = mmcsd_multi_iocmd(priv, (FAR struct mmc_ioc_multi_cmd *)arg);
if (ret != OK)
{
ferr("ERROR: mmcsd_iocmd failed: %d\n", ret);
}
}
break;
#endif
default:
ret = -ENOTTY;
break;
@@ -2760,6 +2790,359 @@ static int mmcsd_read_csd(FAR struct mmcsd_state_s *priv)
}
#endif
/****************************************************************************
* Name: mmcsd_general_cmd_write
*
* Description:
* Send cmd56 data, one sector size
*
****************************************************************************/
static int mmcsd_general_cmd_write(FAR struct mmcsd_state_s *priv,
FAR const uint8_t *buffer,
off_t startblock)
{
int ret;
DEBUGASSERT(priv != NULL && buffer != NULL);
/* Check if the card is locked or write protected (either via software or
* via the mechanical write protect on the card)
*/
if (mmcsd_wrprotected(priv))
{
ferr("ERROR: Card is locked or write protected\n");
return -EPERM;
}
#if defined(CONFIG_SDIO_DMA) && defined(CONFIG_ARCH_HAVE_SDIO_PREFLIGHT)
/* If we think we are going to perform a DMA transfer, make sure that we
* will be able to before we commit the card to the operation.
*/
if ((priv->caps & SDIO_CAPS_DMASUPPORTED) != 0)
{
ret = SDIO_DMAPREFLIGHT(priv->dev, buffer, priv->blocksize);
if (ret != OK)
{
return ret;
}
}
#endif
/* Verify that the card is ready for the transfer. The card may still be
* busy from the preceding write transfer. It would be simpler to check
* for write busy at the end of each write, rather than at the beginning of
* each read AND write, but putting the busy-wait at the beginning of the
* transfer allows for more overlap and, hopefully, better performance
*/
ret = mmcsd_transferready(priv);
if (ret != OK)
{
ferr("ERROR: Card not ready: %d\n", ret);
return ret;
}
/* Select the block size for the card */
ret = mmcsd_setblocklen(priv, priv->blocksize);
if (ret != OK)
{
ferr("ERROR: mmcsd_setblocklen failed: %d\n", ret);
return ret;
}
/* If Controller does not need DMA setup before the write then send CMD56
* now.
*/
if ((priv->caps & SDIO_CAPS_DMABEFOREWRITE) == 0)
{
/* Send CMD56, WRITE_BLOCK, and verify good R1 status is returned */
mmcsd_sendcmdpoll(priv, MMCSD_CMD56WR, startblock);
ret = mmcsd_recv_r1(priv, MMCSD_CMD56WR);
if (ret != OK)
{
ferr("ERROR: mmcsd_recv_r1 for CMD56 failed: %d\n", ret);
return ret;
}
}
/* Configure SDIO controller hardware for the write transfer */
SDIO_BLOCKSETUP(priv->dev, priv->blocksize, 1);
SDIO_WAITENABLE(priv->dev,
SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT | SDIOWAIT_ERROR,
MMCSD_BLOCK_WDATADELAY);
#ifdef CONFIG_SDIO_DMA
if ((priv->caps & SDIO_CAPS_DMASUPPORTED) != 0)
{
ret = SDIO_DMASENDSETUP(priv->dev, buffer, priv->blocksize);
if (ret != OK)
{
finfo("SDIO_DMASENDSETUP: error %d\n", ret);
SDIO_CANCEL(priv->dev);
return ret;
}
}
else
#endif
{
SDIO_SENDSETUP(priv->dev, buffer, priv->blocksize);
}
/* If Controller needs DMA setup before write then only send CMD24 now. */
if ((priv->caps & SDIO_CAPS_DMABEFOREWRITE) != 0)
{
/* Send CMD56, WRITE_BLOCK, and verify good R1 status is returned */
mmcsd_sendcmdpoll(priv, MMCSD_CMD56WR, startblock);
ret = mmcsd_recv_r1(priv, MMCSD_CMD56WR);
if (ret != OK)
{
ferr("ERROR: mmcsd_recv_r1 for CMD56 failed: %d\n", ret);
SDIO_CANCEL(priv->dev);
return ret;
}
}
/* Wait for the transfer to complete */
ret = mmcsd_eventwait(priv, SDIOWAIT_TIMEOUT | SDIOWAIT_ERROR);
if (ret != OK)
{
ferr("ERROR: CMD56 transfer failed: %d\n", ret);
return ret;
}
/* Flag that a write transfer is pending that we will have to check for
* write complete at the beginning of the next transfer.
*/
priv->wrbusy = true;
#if defined(CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE)
/* Arm the write complete detection with timeout */
SDIO_WAITENABLE(priv->dev, SDIOWAIT_WRCOMPLETE | SDIOWAIT_TIMEOUT,
MMCSD_BLOCK_WDATADELAY);
#endif
/* On success, return OK */
return OK;
}
/****************************************************************************
* Name: mmcsd_general_cmd_read
*
* Description:
* Read cmd56 data, one sector size
*
****************************************************************************/
static int mmcsd_general_cmd_read(FAR struct mmcsd_state_s *priv,
FAR uint8_t *buffer, off_t startblock)
{
int ret;
DEBUGASSERT(priv != NULL && buffer != NULL);
/* Check if the card is locked */
if (priv->locked)
{
ferr("ERROR: Card is locked\n");
return -EPERM;
}
#if defined(CONFIG_SDIO_DMA) && defined(CONFIG_ARCH_HAVE_SDIO_PREFLIGHT)
/* If we think we are going to perform a DMA transfer, make sure that we
* will be able to before we commit the card to the operation.
*/
if ((priv->caps & SDIO_CAPS_DMASUPPORTED) != 0)
{
ret = SDIO_DMAPREFLIGHT(priv->dev, buffer, priv->blocksize);
if (ret != OK)
{
return ret;
}
}
#endif
/* Verify that the card is ready for the transfer. The card may still be
* busy from the preceding write transfer. It would be simpler to check
* for write busy at the end of each write, rather than at the beginning of
* each read AND write, but putting the busy-wait at the beginning of the
* transfer allows for more overlap and, hopefully, better performance
*/
ret = mmcsd_transferready(priv);
if (ret != OK)
{
ferr("ERROR: Card not ready: %d\n", ret);
return ret;
}
/* Select the block size for the card */
ret = mmcsd_setblocklen(priv, priv->blocksize);
if (ret != OK)
{
ferr("ERROR: mmcsd_setblocklen failed: %d\n", ret);
return ret;
}
/* Configure SDIO controller hardware for the read transfer */
SDIO_BLOCKSETUP(priv->dev, priv->blocksize, 1);
SDIO_WAITENABLE(priv->dev,
SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT | SDIOWAIT_ERROR,
MMCSD_BLOCK_RDATADELAY);
#ifdef CONFIG_SDIO_DMA
if ((priv->caps & SDIO_CAPS_DMASUPPORTED) != 0)
{
ret = SDIO_DMARECVSETUP(priv->dev, buffer, priv->blocksize);
if (ret != OK)
{
finfo("SDIO_DMARECVSETUP: error %d\n", ret);
SDIO_CANCEL(priv->dev);
return ret;
}
}
else
#endif
{
SDIO_RECVSETUP(priv->dev, buffer, priv->blocksize);
}
/* Send CMD56: Read a sector size data and verify that good R1
* status is returned.
*/
mmcsd_sendcmdpoll(priv, MMCSD_CMD56RD, startblock);
ret = mmcsd_recv_r1(priv, MMCSD_CMD56RD);
if (ret != OK)
{
ferr("ERROR: mmcsd_recv_r1 for CMD56 failed: %d\n", ret);
SDIO_CANCEL(priv->dev);
return ret;
}
/* Then wait for the data transfer to complete */
ret = mmcsd_eventwait(priv, SDIOWAIT_TIMEOUT | SDIOWAIT_ERROR);
if (ret != OK)
{
ferr("ERROR: CMD56 transfer failed: %d\n", ret);
return ret;
}
/* Return value: OK */
return OK;
}
#ifdef CONFIG_MMCSD_IOCSUPPORT
/****************************************************************************
* Name: mmcsd_iocmd
*
* Description:
* MMCSD device ioctl commands.
*
****************************************************************************/
static int mmcsd_iocmd(FAR struct mmcsd_state_s *priv,
FAR struct mmc_ioc_cmd *ic_ptr)
{
int ret;
DEBUGASSERT(priv != NULL && ic_ptr != NULL);
if (!ic_ptr->is_acmd)
{
uint32_t opcode = ic_ptr->opcode & MMCSD_CMDIDX_MASK;
switch (opcode)
{
case MMCSD_CMDIDX56: /* support general commands */
{
if (ic_ptr->write_flag)
{
ret = mmcsd_general_cmd_write(priv,
(FAR uint8_t *)(uintptr_t)(ic_ptr->data_ptr),
ic_ptr->arg);
if (ret != OK)
{
ferr("mmcsd_iocmd MMCSD_CMDIDX56 write failed.\n");
return ret;
}
}
else
{
ret = mmcsd_general_cmd_read(priv,
(FAR uint8_t *)(uintptr_t)(ic_ptr->data_ptr),
ic_ptr->arg);
if (ret != OK)
{
ferr("mmcsd_iocmd MMCSD_CMDIDX56 read failed.\n");
return ret;
}
}
}
break;
default:
ferr("mmcsd_iocmd opcode unsupported.\n");
return -EINVAL;
}
}
return OK;
}
/****************************************************************************
* Name: mmcsd_multi_iocmd
*
* Description:
* MMCSD device ioctl multi commands.
*
****************************************************************************/
static int mmcsd_multi_iocmd(FAR struct mmcsd_state_s *priv,
FAR struct mmc_ioc_multi_cmd *imc_ptr)
{
int ret;
int i;
DEBUGASSERT(priv != NULL && imc_ptr != NULL);
if (imc_ptr->num_of_cmds > MMC_IOC_MAX_CMDS)
{
ferr("mmcsd_multi_iocmd too many cmds.\n");
return -EINVAL;
}
for (i = 0; i < imc_ptr->num_of_cmds; ++i)
{
ret = mmcsd_iocmd(priv, &imc_ptr->cmds[i]);
if (ret != OK)
{
ferr("cmds %d failed.\n", i);
return ret;
}
}
return OK;
}
#endif
/****************************************************************************
* Name: mmcsd_sdinitialize
*
+49 -7
View File
@@ -36,7 +36,12 @@
/* mmcsd ioctl */
#define MMC_RPMB_TRANSFER _MMCSDIOC(0x0000)
#define MMC_IOC_CMD _MMCSDIOC(0x0000)
#define MMC_IOC_MULTI_CMD _MMCSDIOC(0x0001)
#define MMC_IOC_MAX_BYTES (512L * 1024)
#define MMC_IOC_MAX_CMDS 255
#define mmc_ioc_cmd_set_data(ic, ptr) (ic).data_ptr = (uint64_t)(unsigned long)(ptr)
/* rpmb request */
@@ -49,6 +54,49 @@
* Public Types
****************************************************************************/
struct mmc_ioc_cmd
{
/* Direction of data: nonzero = write, zero = read.
* Bit 31 selects 'Reliable Write' for RPMB.
*/
int write_flag;
/* Application-specific command. true = precede with CMD55 */
int is_acmd;
uint32_t opcode;
uint32_t arg;
uint32_t response[4]; /* CMD response */
unsigned int flags;
unsigned int blksz;
unsigned int blocks;
/* For 64-bit machines, the next member, ``uint64_t data_ptr``, wants to
* be 8-byte aligned. Make sure this struct is the same size when
* built for 32-bit.
*/
uint32_t pad;
/* DAT buffer */
uint64_t data_ptr;
};
/* struct mmc_ioc_multi_cmd - multi command information
* @num_of_cmds: Number of commands to send. Must be equal to or less than
* MMC_IOC_MAX_CMDS.
* @cmds: Array of commands with length equal to 'num_of_cmds'
*/
struct mmc_ioc_multi_cmd
{
uint64_t num_of_cmds;
struct mmc_ioc_cmd cmds[1];
};
struct mmc_rpmb_frame_s
{
uint8_t stuff[196];
@@ -62,12 +110,6 @@ struct mmc_rpmb_frame_s
uint16_t req_resp;
};
struct mmc_rpmb_transfer_s
{
unsigned int num_of_frames;
struct mmc_rpmb_frame_s frames[0];
};
/****************************************************************************
* Public Functions Definitions
****************************************************************************/