blob: a4c670ea1ef161439e5cb9d524eab626c93c1115 [file] [log] [blame]
/****************************************************************************
* arch/arm/src/cxd56xx/cxd56_emmc.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/param.h>
#include <sys/types.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/fs.h>
#include <nuttx/irq.h>
#include <nuttx/mutex.h>
#include <nuttx/semaphore.h>
#include <arch/board/board.h>
#include "chip.h"
#include "arm_internal.h"
#include "cxd56_clock.h"
#include "cxd56_emmc.h"
#include "hardware/cxd56_emmc.h"
#include "cxd56_pinconfig.h"
/****************************************************************************
* Pre-processoro Definitions
****************************************************************************/
#define SECTOR_SIZE (512)
#define EMMC_DATA_WRITE 0
#define EMMC_DATA_READ 1
#define EMMC_NON_DATA 2
#define EMMC_NON_RESP 0
#define EMMC_RESP_R1 1
#define EMMC_RESP_R1B 2
#define EMMC_RESP_R2 3
#define EMMC_RESP_R3 4
#define EMMC_CLKDIV_UNDER_400KHZ (32u)
#define EMMC_CLKDIV_NON_DIV (0u)
#define EMMC_RCA (2) /* greater than 1 */
#define EMMC_DATA_TIMEOUT (0xFFFFFFu) /* max reg value */
#define EMMC_RESP_TIMEOUT (0xFFu) /* max reg value */
#define EMMC_MSIZE (6) /* Burst size is 512B */
#define EMMC_FIFO_DEPTH (0x100) /* FIFO size is 1KB */
/****************************************************************************
* Private Types
****************************************************************************/
struct emmc_dma_desc_s
{
uint32_t ctrl;
uint32_t size;
uint32_t addr;
uint32_t next;
};
struct cxd56_emmc_state_s
{
mutex_t lock;
int crefs;
uint32_t total_sectors;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Block driver interfaces **************************************************/
static int cxd56_emmc_open(struct inode *inode);
static int cxd56_emmc_close(struct inode *inode);
static ssize_t cxd56_emmc_read(struct inode *inode,
unsigned char *buffer,
blkcnt_t start_sector,
unsigned int nsectors);
#if !defined(CONFIG_MMCSD_READONLY)
static ssize_t cxd56_emmc_write(struct inode *inode,
const unsigned char *buffer,
blkcnt_t start_sector,
unsigned int nsectors);
#endif
static int cxd56_emmc_geometry(struct inode *inode,
struct geometry *geometry);
static int emmc_interrupt(int irq, void *context, void *arg);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct block_operations g_bops =
{
cxd56_emmc_open, /* open */
cxd56_emmc_close, /* close */
cxd56_emmc_read, /* read */
#if !defined(CONFIG_MMCSD_READONLY)
cxd56_emmc_write, /* write */
#else
NULL, /* write */
#endif
cxd56_emmc_geometry, /* geometry */
NULL /* ioctl */
};
static sem_t g_waitsem = SEM_INITIALIZER(0);
struct cxd56_emmc_state_s g_emmcdev =
{
.lock = NXMUTEX_INITIALIZER,
};
/****************************************************************************
* Private Functions
****************************************************************************/
static void emmc_cmdstarted(void)
{
uint32_t val;
do
{
val = getreg32(EMMC_CMD);
}
while (val & EMMC_CMD_START_CMD);
}
static void emmc_reset(uint32_t reg, uint32_t bits)
{
uint32_t val;
val = getreg32(reg);
putreg32((val | bits), reg);
do
{
val = getreg32(reg);
}
while (val & bits);
}
#if !defined(CONFIG_MMCSD_READONLY)
static void emmc_flushwritefifo(void)
{
/* eMMC host controller has a problem that invalid data is still remained
* in the FIFO after data write done,
* therefore, invalid data will be written to eMMC in the next data write.
* So, we need to reset FIFO after data write.
* And invalid data remain in FIFO when setting DDR_REG, too
*/
emmc_reset(EMMC_CTRL, EMMC_CTRL_FIFO_RESET | EMMC_CTRL_CTRL_RESET);
putreg32(0, EMMC_CMDARG);
putreg32(EMMC_CMD_START_CMD | EMMC_CMD_USE_HOLDREG |
EMMC_CMD_UPDATE_CLKREG | EMMC_CMD_WAIT_PRE_DATA,
EMMC_CMD);
emmc_cmdstarted();
emmc_reset(EMMC_CTRL, EMMC_CTRL_FIFO_RESET);
}
#endif
static void emmc_initregister(void)
{
uint32_t rxwmark;
uint32_t txwmark;
uint32_t blksize;
uint32_t blksizeperhdatawidth;
uint32_t rdthr;
emmc_reset(EMMC_CTRL, EMMC_CTRL_CTRL_RESET);
emmc_reset(EMMC_CTRL, EMMC_CTRL_FIFO_RESET);
emmc_reset(EMMC_CTRL, EMMC_CTRL_DMA_RESET);
emmc_reset(EMMC_BMOD, EMMC_BMOD_SW_RESET);
putreg32(EMMC_CTRL_USE_IDMAC | EMMC_CTRL_INT_ENABLE, EMMC_CTRL);
putreg32((EMMC_DATA_TIMEOUT << EMMC_TMOUT_DATA_SHIFT) |
(EMMC_RESP_TIMEOUT << EMMC_TMOUT_RESP_SHIFT),
EMMC_TMOUT);
rxwmark = EMMC_FIFO_DEPTH / 2 - 1;
txwmark = EMMC_FIFO_DEPTH / 2;
putreg32((EMMC_MSIZE << EMMC_FIFOTH_MSIZE_SHIFT) |
(rxwmark << EMMC_FIFOTH_RX_SHIFT) |
(txwmark << EMMC_FIFOTH_TX_SHIFT),
EMMC_FIFOTH);
putreg32(EMMC_BMOD_IDMAC_EN | EMMC_BMOD_FIXED_BURST, EMMC_BMOD);
blksize = SECTOR_SIZE;
blksizeperhdatawidth = blksize / 4;
if (blksize < EMMC_FIFO_DEPTH / 2)
{
rdthr = blksizeperhdatawidth;
}
else
{
rdthr = blksizeperhdatawidth / 2;
}
putreg32((rdthr << EMMC_CARD_RD_THR_SHIFT |
EMMC_BSYCLR_INT_EN | EMMC_CARD_RD_THE_EN),
EMMC_CARDTHRCTL);
putreg32(0, EMMC_INTMASK);
}
static void emmc_changeclock(int clkdiv)
{
uint32_t cmd;
cmd = EMMC_CMD_START_CMD | EMMC_CMD_UPDATE_CLKREG |
EMMC_CMD_USE_HOLDREG | EMMC_CMD_WAIT_PRE_DATA;
/* disable clock */
putreg32(EMMC_CLKENA_DIS, EMMC_CLKENA);
putreg32(cmd, EMMC_CMD);
emmc_cmdstarted();
/* change clock */
putreg32(clkdiv, EMMC_CLKDIV);
putreg32(cmd, EMMC_CMD);
emmc_cmdstarted();
/* enable clock */
putreg32(EMMC_CLKENA_ENA, EMMC_CLKENA);
putreg32(cmd, EMMC_CMD);
emmc_cmdstarted();
}
static struct emmc_dma_desc_s *emmc_setupdma(void *buf, unsigned int nbytes)
{
int i;
int ndescs;
struct emmc_dma_desc_s *descs;
struct emmc_dma_desc_s *d;
uint32_t addr;
uint32_t size;
unsigned int remain;
if (nbytes == 0)
{
return NULL;
}
ndescs = nbytes / 4096;
if ((nbytes & (4096 - 1)) != 0)
{
ndescs++;
}
descs = (struct emmc_dma_desc_s *)
kmm_malloc(ndescs * sizeof(struct emmc_dma_desc_s));
if (!descs)
{
return NULL;
}
remain = nbytes;
addr = CXD56_PHYSADDR(buf);
for (i = 0, d = descs; i < ndescs; i++, d++)
{
d->ctrl = EMMC_IDMAC_DES0_OWN | EMMC_IDMAC_DES0_CH |
EMMC_IDMAC_DES0_DIC;
size = MIN(remain, 4096);
d->size = size;
d->addr = addr;
d->next = CXD56_PHYSADDR(d + 1);
remain -= size;
addr += size;
}
ASSERT(remain == 0);
/* Adjust first and last descriptor members */
descs[0].ctrl |= EMMC_IDMAC_DES0_FD;
descs[ndescs - 1].ctrl |= EMMC_IDMAC_DES0_LD;
descs[ndescs - 1].next = 0;
#ifdef CONFIG_DEBUG_FS_INFO
for (i = 0, d = descs; i < ndescs; i++, d++)
{
finfo("desc %p = ctrl 0x%x, size 0x%x, addr 0x%x, next 0x%x\n",
d, d->ctrl, d->size, d->addr, d->next);
}
#endif
putreg32(CXD56_PHYSADDR(descs), EMMC_DBADDR);
return descs;
}
static int emmc_checkresponse(void)
{
uint32_t resp = getreg32(EMMC_RESP0);
uint32_t intsts = getreg32(EMMC_RINTSTS);
if (intsts & EMMC_INTSTS_RTO)
{
ferr("Response timed out.\n");
return -EIO;
}
if (resp & R1STATUS_ALL_ERR)
{
ferr("Response error %08" PRIx32 "\n", resp);
return -EIO;
}
return OK;
}
static void emmc_send(int datatype, uint32_t opcode, uint32_t arg,
int resptype)
{
uint32_t prev;
uint32_t mask;
uint32_t cmd;
uint32_t status;
int ret;
/* Get current interrupt mask, leave SDIO relative bits. */
prev = mask = getreg32(EMMC_INTMASK) & EMMC_INTSTS_SDIO;
cmd = EMMC_CMD_START_CMD | EMMC_CMD_USE_HOLDREG |
EMMC_CMD_CHK_RESP_CRC | EMMC_CMD_RESP_EXPECTED |
EMMC_CMD_WAIT_PRE_DATA | opcode;
switch (datatype)
{
case EMMC_DATA_WRITE:
cmd |= EMMC_CMD_WRITE | EMMC_CMD_DATA_EXPECTED;
mask |= EMMC_INTSTS_DTO;
break;
case EMMC_DATA_READ:
cmd |= EMMC_CMD_READ | EMMC_CMD_DATA_EXPECTED;
mask |= EMMC_INTSTS_DTO;
break;
case EMMC_NON_DATA:
mask |= EMMC_INTSTS_CD;
if (opcode == GO_IDLE_STATE)
{
cmd |= EMMC_CMD_SEND_INIT;
cmd &= ~EMMC_CMD_CHK_RESP_CRC;
cmd &= ~EMMC_CMD_RESP_EXPECTED;
}
else if (opcode == SEND_OP_COND)
{
cmd &= ~EMMC_CMD_CHK_RESP_CRC;
}
else if (opcode == ALL_SEND_CID)
{
cmd |= EMMC_CMD_RESP_LENGTH;
}
else if (opcode == SEND_CSD)
{
cmd |= EMMC_CMD_RESP_LENGTH;
}
else if (opcode == STOP_TRANS)
{
cmd |= EMMC_CMD_STOP_CMD;
}
break;
}
/* Enable error interrupts */
mask |= EMMC_INTSTS_RCRC | EMMC_INTSTS_DCRC |
EMMC_INTSTS_DRTO | EMMC_INTSTS_HTO | EMMC_INTSTS_FRUN |
EMMC_INTSTS_HLE | EMMC_INTSTS_EBE;
putreg32(0xffffu, EMMC_RINTSTS);
putreg32(0x1ff37u, EMMC_IDSTS);
putreg32(mask, EMMC_INTMASK);
/* Send command */
putreg32(arg, EMMC_CMDARG);
putreg32(cmd, EMMC_CMD);
/* Wait for command or data transfer done */
ret = nxsem_wait_uninterruptible(&g_waitsem);
if (ret < 0)
{
return;
}
/* Restore interrupt mask */
putreg32(prev, EMMC_INTMASK);
/* Waiting for device ready */
do
{
status = getreg32(EMMC_STATUS);
}
while (status & EMMC_STATUS_DATA_BUSY);
}
static int emmc_is_powerup(void)
{
int retry;
/* 5ms * 1000 times */
retry = 1000;
do
{
uint32_t response;
uint32_t intsts;
emmc_send(EMMC_NON_DATA, SEND_OP_COND,
OCR_SECTOR_MODE | OCR_DUAL_VOLT, EMMC_RESP_R3);
intsts = getreg32(EMMC_RINTSTS);
if (intsts & EMMC_INTSTS_RTO)
{
return -EIO;
}
response = getreg32(EMMC_RESP0);
if (response == (OCR_SECTOR_MODE | OCR_DUAL_VOLT | OCR_POWER_UP))
{
return 0;
}
up_mdelay(5);
}
while (--retry);
return -ETIMEDOUT;
}
static int emmc_switchcmd(uint8_t index, uint8_t val)
{
emmc_send(EMMC_NON_DATA, SWITCH,
(uint32_t)((3u << 24) | (index << 16) | (val << 8)),
EMMC_RESP_R1B);
if (emmc_checkresponse())
{
return -EIO;
}
emmc_send(EMMC_NON_DATA, SEND_STATUS, EMMC_RCA << 16, EMMC_RESP_R1);
if (emmc_checkresponse())
{
return -EIO;
}
return OK;
}
/****************************************************************************
* Name: emmc_interrupt
*
* Description:
* The eMMC Interrupt Handler
*
****************************************************************************/
static int emmc_interrupt(int irq, void *context, void *arg)
{
uint32_t intr;
/* Get interrupt status and clear eMMC bits */
intr = getreg32(EMMC_MINTSTS) & ~EMMC_INTSTS_SDIO;
putreg32(intr, EMMC_RINTSTS);
if (intr & EMMC_INTSTS_RE)
{
ferr("Response error.\n");
}
if (intr & EMMC_INTSTS_CD)
{
/* Command done */
}
if (intr & EMMC_INTSTS_DTO)
{
/* Data transfer over */
}
if (intr & EMMC_INTSTS_TXDR)
{
ferr("Transmit FIFO data request.\n");
}
if (intr & EMMC_INTSTS_RXDR)
{
ferr("Receive FIFO data request.\n");
}
if (intr & EMMC_INTSTS_RCRC)
{
ferr("Response CRC error.\n");
}
if (intr & EMMC_INTSTS_DCRC)
{
ferr("Data CRC error.\n");
}
if (intr & EMMC_INTSTS_RTO)
{
ferr("Response timeout/Boot Ack Received.\n");
}
if (intr & EMMC_INTSTS_DRTO)
{
ferr("Data read timeout/Boot Data Start.\n");
}
if (intr & EMMC_INTSTS_HTO)
{
ferr("Data stavation-by-host timeout/Volt_switch_int.\n");
}
if (intr & EMMC_INTSTS_FRUN)
{
ferr("FIFO underrun/overrun error.\n");
}
if (intr & EMMC_INTSTS_HLE)
{
ferr("Hardware locked write error.\n");
}
if (intr & EMMC_INTSTS_BCI)
{
/* Start-bit error/Busy clear interrupt. */
}
if (intr & EMMC_INTSTS_ACD)
{
/* Auto command done */
}
if (intr & EMMC_INTSTS_EBE)
{
ferr("End-bit error/write no CRC.\n");
}
nxsem_post(&g_waitsem);
return OK;
}
static void emmc_pincontrol(bool on)
{
if (on)
{
CXD56_PIN_CONFIGS(PINCONFS_EMMC);
}
else
{
CXD56_PIN_CONFIGS(PINCONFS_EMMC_GPIO);
}
}
static int emmc_hwinitialize(void)
{
int ret = OK;
cxd56_emmc_clock_enable(1, 1, 0);
/* Configure pin */
emmc_pincontrol(true);
/* Setup IRQ before command send */
emmc_initregister();
emmc_changeclock(EMMC_CLKDIV_UNDER_400KHZ);
irq_attach(CXD56_IRQ_EMMC, emmc_interrupt, NULL);
up_enable_irq(CXD56_IRQ_EMMC);
emmc_send(EMMC_NON_DATA, GO_IDLE_STATE, 0, EMMC_NON_RESP);
if ((ret = emmc_is_powerup()) != 0)
{
goto errout;
}
emmc_send(EMMC_NON_DATA, ALL_SEND_CID, 0, EMMC_RESP_R2);
emmc_send(EMMC_NON_DATA, SET_RELATIVE_ADDR, EMMC_RCA << 16, EMMC_RESP_R1);
if (emmc_checkresponse())
{
goto errout;
}
#ifdef EMMC_USE_SEND_CSD
emmc_send(EMMC_NON_DATA, SEND_CSD, (EMMC_RCA << 16), EMMC_RESP_R2);
#endif
emmc_send(EMMC_NON_DATA, SELECT_DESELECT, (EMMC_RCA << 16), EMMC_RESP_R1);
if (emmc_checkresponse())
{
goto errout;
}
ret = emmc_switchcmd(EXTCSD_HS_TIMING, EXTCSD_HS_TIMING_HIGH_SPEED);
if (ret)
{
goto errout;
}
ret = emmc_switchcmd(EXTCSD_BUS_WIDTH, EXTCSD_BUS_WIDTH_4BIT_SDR);
if (ret)
{
goto errout;
}
putreg32(EMMC_CTYPE_4BIT_MODE, EMMC_CTYPE);
emmc_changeclock(EMMC_CLKDIV_NON_DIV);
#ifdef CONFIG_CXD56_EMMC_VENDOR_TOSHIBA
/* Vendor-specific command */
ret = emmc_switchcmd(EXTCSD_PON, EXTCSD_PON_POWERED_ON);
if (ret)
{
goto errout;
}
#endif
return OK;
errout:
up_disable_irq(CXD56_IRQ_EMMC);
emmc_pincontrol(false);
cxd56_emmc_clock_disable();
return ret;
}
static int cxd56_emmc_readsectors(struct cxd56_emmc_state_s *priv,
void *buf,
size_t start_sector,
unsigned int nsectors)
{
struct emmc_dma_desc_s *descs;
uint32_t idsts;
int ret = OK;
descs = emmc_setupdma(buf, nsectors * SECTOR_SIZE);
if (!descs)
{
ferr("Building descriptor failed.\n");
return -ENOMEM;
}
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
kmm_free(descs);
return ret;
}
putreg32(nsectors * SECTOR_SIZE, EMMC_BYTCNT);
emmc_send(EMMC_NON_DATA, SET_BLOCK_COUNT, nsectors, EMMC_RESP_R1);
/* Check command error */
if (emmc_checkresponse())
{
ferr("SET_BLOCK_COUNT failed.\n");
ret = -EIO;
goto finish;
}
emmc_send(EMMC_DATA_READ, READ_MULTIPLE_BLOCK, start_sector, EMMC_RESP_R1);
/* Check command error */
if (emmc_checkresponse())
{
ferr("READ_MULTIPLE_BLOCK failed.\n");
ret = -EIO;
goto finish;
}
/* Check DMA status */
idsts = getreg32(EMMC_IDSTS);
if (idsts &
(EMMC_IDSTS_FBE | EMMC_IDSTS_DU | EMMC_IDSTS_CES | EMMC_IDSTS_AIS))
{
ferr("DMA status failed. %08" PRIx32 "\n", idsts);
ret = -EIO;
}
finish:
nxmutex_unlock(&priv->lock);
kmm_free(descs);
return ret;
}
#if !defined(CONFIG_MMCSD_READONLY)
static int cxd56_emmc_writesectors(struct cxd56_emmc_state_s *priv,
const void *buf, blkcnt_t start_sector,
unsigned int nsectors)
{
struct emmc_dma_desc_s *descs;
uint32_t idsts;
int ret = OK;
descs = emmc_setupdma((void *)buf, nsectors * SECTOR_SIZE);
if (!descs)
{
return -ENOMEM;
}
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
kmm_free(descs);
return ret;
}
putreg32(nsectors * SECTOR_SIZE, EMMC_BYTCNT);
emmc_send(EMMC_NON_DATA, SET_BLOCK_COUNT, nsectors, EMMC_RESP_R1);
/* Check command error */
if (emmc_checkresponse())
{
ferr("SET_BLOCK_COUNT failed.\n");
ret = -EIO;
goto finish;
}
emmc_send(EMMC_DATA_WRITE, WRITE_MULTIPLE_BLOCK,
start_sector, EMMC_RESP_R1);
/* Check command error */
if (emmc_checkresponse())
{
ferr("WRITE_MULTIPLE_BLOCK failed.\n");
ret = -EIO;
goto finish;
}
/* Check DMA status */
idsts = getreg32(EMMC_IDSTS);
if (idsts &
(EMMC_IDSTS_FBE | EMMC_IDSTS_DU | EMMC_IDSTS_CES | EMMC_IDSTS_AIS))
{
ferr("DMA status error. %08" PRIx32 "\n", idsts);
ret = -EIO;
}
emmc_send(EMMC_NON_DATA, SEND_STATUS, EMMC_RCA << 16, EMMC_RESP_R1);
/* Check command error */
if (emmc_checkresponse())
{
ferr("SEND_STATUS failed.\n");
ret = -EIO;
goto finish;
}
emmc_flushwritefifo();
finish:
nxmutex_unlock(&priv->lock);
kmm_free(descs);
return ret;
}
#endif
static int cxd56_emmc_open(struct inode *inode)
{
struct cxd56_emmc_state_s *priv;
int ret;
DEBUGASSERT(inode->i_private);
priv = inode->i_private;
/* Just increment the reference count on the driver */
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
priv->crefs++;
nxmutex_unlock(&priv->lock);
return OK;
}
static int cxd56_emmc_close(struct inode *inode)
{
struct cxd56_emmc_state_s *priv;
int ret;
DEBUGASSERT(inode->i_private);
priv = inode->i_private;
/* Decrement the reference count on the block driver */
DEBUGASSERT(priv->crefs > 0);
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
priv->crefs--;
nxmutex_unlock(&priv->lock);
return OK;
}
static ssize_t cxd56_emmc_read(struct inode *inode,
unsigned char *buffer, blkcnt_t start_sector,
unsigned int nsectors)
{
struct cxd56_emmc_state_s *priv;
int ret;
DEBUGASSERT(inode->i_private);
priv = inode->i_private;
finfo("Read sector %" PRIuOFF " (%u sectors) to %p\n",
start_sector, nsectors, buffer);
ret = cxd56_emmc_readsectors(priv, buffer, start_sector, nsectors);
if (ret)
{
ferr("Read sector failed. %d\n", ret);
return 0;
}
return nsectors;
}
#if !defined(CONFIG_MMCSD_READONLY)
static ssize_t cxd56_emmc_write(struct inode *inode,
const unsigned char *buffer,
blkcnt_t start_sector,
unsigned int nsectors)
{
struct cxd56_emmc_state_s *priv;
int ret;
DEBUGASSERT(inode->i_private);
priv = inode->i_private;
finfo("Write %p to sector %" PRIu32 " (%u sectors)\n", buffer,
start_sector, nsectors);
ret = cxd56_emmc_writesectors(priv, buffer, start_sector, nsectors);
if (ret)
{
ferr("Write sector failed. %d\n", ret);
return 0;
}
return nsectors;
}
#endif
static int cxd56_emmc_geometry(struct inode *inode,
struct geometry *geometry)
{
struct cxd56_emmc_state_s *priv;
DEBUGASSERT(inode->i_private);
priv = inode->i_private;
memset(geometry, 0, sizeof(*geometry));
geometry->geo_available = true;
geometry->geo_mediachanged = false;
#if !defined(CONFIG_MMCSD_READONLY)
geometry->geo_writeenabled = true;
#else
geometry->geo_writeenabled = false;
#endif
geometry->geo_nsectors = priv->total_sectors;
geometry->geo_sectorsize = SECTOR_SIZE;
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
int cxd56_emmcinitialize(void)
{
struct cxd56_emmc_state_s *priv = &g_emmcdev;
uint8_t *buf;
struct emmc_dma_desc_s *descs;
int ret;
ret = emmc_hwinitialize();
if (ret != OK)
{
return -EIO;
}
buf = kmm_malloc(SECTOR_SIZE);
if (buf)
{
putreg32(SECTOR_SIZE, EMMC_BYTCNT);
descs = emmc_setupdma(buf, SECTOR_SIZE);
if (descs)
{
emmc_send(EMMC_DATA_READ, SEND_EXT_CSD, 0, EMMC_RESP_R1);
if (emmc_checkresponse())
{
kmm_free(buf);
return -EIO;
}
priv->total_sectors = *(uint32_t *)&buf[EXTCSD_SEC_COUNT];
kmm_free(descs);
}
kmm_free(buf);
}
ret = register_blockdriver("/dev/emmc0", &g_bops, 0, priv);
if (ret < 0)
{
ferr("register_blockdriver failed: %d\n", -ret);
}
return ret;
}
int cxd56_emmcuninitialize(void)
{
int ret;
ret = unregister_blockdriver("/dev/emmc0");
if (ret < 0)
{
ferr("unregister_blockdriver failed: %d\n", -ret);
return ret;
}
/* Send power off command */
emmc_switchcmd(EXTCSD_PON, EXTCSD_PON_POWERED_OFF_LONG);
up_disable_irq(CXD56_IRQ_EMMC);
/* Configure pin */
emmc_pincontrol(false);
cxd56_emmc_clock_disable();
return 0;
}