Files
nuttx/drivers/mtd/mtd_cfi.c
zhangshoukui 0550355c92 drivers/mtd/mtd_cfi: Add MTDIOC_ERASESTATE
We need to give a default erase value when we register cfi flash for mtd devices
he pseudo-code is as follows:
register_cfi_driver(xxx)
find_mtddriver("xxx", &inode);
mtdconfig_register(inode->u.i_mtd);

https://github.com/Zhangshoukui/nuttx/blob/master/drivers/mtd/mtd_config.c#L1775C28-L1775C45

Signed-off-by: zhangshoukui <zhangshoukui@xiaomi.com>
2025-03-28 19:19:35 +08:00

337 lines
10 KiB
C

/****************************************************************************
* drivers/mtd/mtd_cfi.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/types.h>
#include <stdint.h>
#include <errno.h>
#include <stdio.h>
#include <debug.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/fs/fs.h>
#include <nuttx/mtd/mtd.h>
#include "cfi.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* MTD driver methods */
static int cfi_mtd_erase(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks);
static ssize_t cfi_mtd_bread(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR uint8_t *buf);
static ssize_t cfi_mtd_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR const uint8_t *buf);
static ssize_t cfi_mtd_read(FAR struct mtd_dev_s *dev, off_t offset,
size_t nbytes, FAR uint8_t *buffer);
#ifdef CONFIG_MTD_BYTE_WRITE
static ssize_t cfi_mtd_write(FAR struct mtd_dev_s *dev, off_t offset,
size_t nbytes, FAR const uint8_t *buffer);
#endif
static int cfi_mtd_ioctl(FAR struct mtd_dev_s *dev, int cmd,
unsigned long arg);
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: cfi_mtd_erase
*
* Description:
* Erase several blocks, each of the size previously reported.
*
****************************************************************************/
static int cfi_mtd_erase(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks)
{
FAR struct cfi_dev_s *priv = (FAR struct cfi_dev_s *)dev;
size_t endblock;
/* The interface definition assumes that all erase blocks are the
* same size. If that is not true for this particular device, then
* transform the start block and nblocks as necessary.
*/
endblock = cfi_find_block(priv, (startblock + nblocks) *
cfi_get_blocksize(priv, 0));
startblock = cfi_find_block(priv, startblock *
cfi_get_blocksize(priv, 0));
/* Erase the specified blocks and return status (OK or a negated errno) */
return cfi_erase(priv, startblock, endblock - startblock);
}
/****************************************************************************
* Name: cfi_mtd_bread
*
* Description:
* Read the specified number of blocks into the user provided buffer.
*
****************************************************************************/
static ssize_t cfi_mtd_bread(FAR struct mtd_dev_s *dev, off_t startpage,
size_t npages, FAR uint8_t *buf)
{
FAR struct cfi_dev_s *priv = (FAR struct cfi_dev_s *)dev;
off_t offset = startpage * priv->page_size;
int ret;
ret = cfi_read(priv, offset, npages * priv->page_size, buf);
return ret < 0 ? ret : npages;
}
/****************************************************************************
* Name: cfi_mtd_bwrite
*
* Description:
* Write the specified number of blocks from the user provided buffer.
*
****************************************************************************/
static ssize_t cfi_mtd_bwrite(FAR struct mtd_dev_s *dev, off_t startpage,
size_t npages, FAR const uint8_t *buf)
{
FAR struct cfi_dev_s *priv = (FAR struct cfi_dev_s *)dev;
off_t offset = startpage * priv->page_size;
int ret;
ret = cfi_write(priv, offset, npages * priv->page_size, buf);
return ret < 0 ? ret : npages;
}
/****************************************************************************
* Name: cfi_mtd_read
*
* Description:
* Read the specified number of bytes to the user provided buffer.
*
****************************************************************************/
static ssize_t cfi_mtd_read(FAR struct mtd_dev_s *dev, off_t offset,
size_t nbytes, FAR uint8_t *buffer)
{
FAR struct cfi_dev_s *priv = (FAR struct cfi_dev_s *)dev;
int ret;
ret = cfi_read(priv, offset, nbytes, buffer);
return ret < 0 ? ret : nbytes;
}
/****************************************************************************
* Name: cfi_mtd_write
*
* Description:
* Some FLASH parts have the ability to write an arbitrary number of
* bytes to an arbitrary offset on the device. This method should be
* implement only for devices that support such access.
*
****************************************************************************/
#ifdef CONFIG_MTD_BYTE_WRITE
static ssize_t cfi_mtd_write(FAR struct mtd_dev_s *dev, off_t offset,
size_t nbytes, FAR const uint8_t *buffer)
{
FAR struct cfi_dev_s *priv = (FAR struct cfi_dev_s *)dev;
int ret;
ret = cfi_write(priv, offset, nbytes, buffer);
return ret < 0 ? ret : nbytes;
}
#endif
/****************************************************************************
* Name: cfi_mtd_ioctl
****************************************************************************/
static int cfi_mtd_ioctl(FAR struct mtd_dev_s *dev, int cmd,
unsigned long arg)
{
FAR struct cfi_dev_s *priv = (FAR struct cfi_dev_s *)dev;
int ret = OK;
switch (cmd)
{
case MTDIOC_GEOMETRY:
{
FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)arg;
DEBUGASSERT(geo != NULL);
memset(geo, 0, sizeof(*geo));
geo->blocksize = priv->page_size;
geo->erasesize = cfi_get_blocksize(priv, 0);
geo->neraseblocks = cfi_get_total_blocknum(priv);
}
break;
case BIOC_PARTINFO:
{
FAR struct partition_info_s *info =
(FAR struct partition_info_s *)arg;
DEBUGASSERT(info != NULL);
info->numsectors = cfi_get_total_blocknum(priv);
info->sectorsize = cfi_get_blocksize(priv, 0);
info->startsector = 0;
info->parent[0] = '\0';
}
break;
case BIOC_XIPBASE:
{
FAR void **ppv = (FAR void**)arg;
DEBUGASSERT(ppv != NULL);
*ppv = (FAR void *)priv->base_addr;
}
break;
case MTDIOC_BULKERASE:
{
ret = cfi_mtd_erase(dev, 0, cfi_get_total_blocknum(priv));
}
break;
case MTDIOC_ERASESECTORS:
{
FAR struct mtd_erase_s *erase = (FAR struct mtd_erase_s *)arg;
ret = cfi_mtd_erase(dev, erase->startblock, erase->nblocks);
}
break;
case MTDIOC_ERASESTATE:
{
FAR uint8_t *result = (FAR uint8_t *)arg;
*result = 0xff;
break;
}
default:
ret = -ENOTTY;
break;
}
return ret;
}
/****************************************************************************
* Name: cfi_initialize
*
* Description:
* Create and initialize an MTD device instance. MTD devices are not
* registered in the file system, but are created as instances that can
* be bound to other functions (such as a block or character driver front
* end).
*
****************************************************************************/
static FAR struct mtd_dev_s *
cfi_initialize(uintptr_t addr_base, uintptr_t addr_end, uint32_t bankwidth)
{
FAR struct cfi_dev_s *cfi;
/* Create an instance of the CFI MTD device state structure */
cfi = kmm_zalloc(sizeof(struct cfi_dev_s));
if (cfi == NULL)
{
ferr("ERROR: Failed to allocate the CFI MTD state structure\n");
return NULL;
}
cfi->mtd.erase = cfi_mtd_erase;
cfi->mtd.bread = cfi_mtd_bread;
cfi->mtd.bwrite = cfi_mtd_bwrite;
cfi->mtd.read = cfi_mtd_read;
#ifdef CONFIG_MTD_BYTE_WRITE
cfi->mtd.write = cfi_mtd_write;
#endif
cfi->mtd.ioctl = cfi_mtd_ioctl;
cfi->mtd.name = "cfi-flash";
cfi->base_addr = addr_base;
cfi->end_addr = addr_end;
cfi->bankwidth = bankwidth;
/* Check the CFI, and set other parameters */
if (cfi_check(cfi) == OK)
{
return (FAR struct mtd_dev_s *)cfi;
}
ferr("ERROR: Not a CFI device!\n");
kmm_free(cfi);
return NULL;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: register_cfi_driver
*
* Description:
* Initialize cfi MTD device instance and register it to vfs.
*
****************************************************************************/
int register_cfi_driver(uintptr_t addr_base, uintptr_t addr_end,
uint32_t bankwidth, int id)
{
int ret = -ENXIO;
FAR struct mtd_dev_s *mtd;
mtd = cfi_initialize(addr_base, addr_end, bankwidth);
if (mtd != NULL)
{
char devname[32];
snprintf(devname, sizeof(devname), "/dev/cfi-flash%d", id);
ret = register_mtddriver(devname, mtd, 0777, NULL);
if (ret < 0)
{
kmm_free(mtd);
}
}
return ret;
}