Mercurial > nand-ecoscentric
view packages/io/nand/current/include/nand_device.h @ 3398:aecb165864c8
io/nand: Make interface richer - Add plf_priv member of device struct, CYG_NAND_DEVICE_2, CYG_NAND_ECC_ALG_DECLARATION
| author | Ross Younger <wry@ecoscentric.com> |
|---|---|
| date | Mon, 12 Jan 2015 17:42:41 +1300 |
| parents | 4cce65f46b89 |
| children |
line wrap: on
line source
#ifndef CYGONCE_NAND_DEVICE_H # define CYGONCE_NAND_DEVICE_H //============================================================================= // // nand_device.h // // Device driver interface for the eCos NAND flash library // //============================================================================= // ####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 2009 eCosCentric Limited. // // eCos is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 2 or (at your option) any later // version. // // eCos is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License // along with eCos; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // As a special exception, if other files instantiate templates or use // macros or inline functions from this file, or you compile this file // and link it with other works to produce a work based on this file, // this file does not by itself cause the resulting work to be covered by // the GNU General Public License. However the source code for this file // must still be made available in accordance with section (3) of the GNU // General Public License v2. // // This exception does not invalidate any other reasons why a work based // on this file might be covered by the GNU General Public License. // ------------------------------------------- // ####ECOSGPLCOPYRIGHTEND#### //============================================================================= //#####DESCRIPTIONBEGIN#### // // Author(s): wry // Date: 2009-02-23 // //####DESCRIPTIONEND#### //============================================================================= #include <pkgconf/io_nand.h> #include <pkgconf/isoinfra.h> #include <cyg/infra/cyg_type.h> #include <cyg/hal/drv_api.h> // mutexes #include <cyg/hal/hal_tables.h> #include CYGBLD_ISO_ERRNO_CODES_HEADER #include <cyg/nand/nand_ecc.h> #include <cyg/nand/nand_oob.h> #if (defined CYGSEM_IO_NAND_DEBUG_LEVEL) && (CYGSEM_IO_NAND_DEBUG_LEVEL > 0) // Usage: NAND_CHATTER(level, device, fmt, printf args..) // Important messages should be level 1; inane drivel should be level 9. #define NAND_CHATTER(_l_, _dev_, _fmt_, ...) \ do { \ if (_l_ <= CYGSEM_IO_NAND_DEBUG_LEVEL && *(_dev_)->pf) \ (*(_dev_)->pf)((_fmt_), ## __VA_ARGS__); \ } while(0) #else #define NAND_CHATTER(_l_, _dev_, _fmt_, ...) CYG_EMPTY_STATEMENT #endif #ifdef CYGSEM_IO_NAND_DEBUG #define NAND_ERROR(_dev_, _fmt_, ...) \ do { \ if (*(_dev_)->pf) \ (*(_dev_)->pf)((_fmt_), ## __VA_ARGS__); \ } while(0) #else #define NAND_ERROR(_dev_, _fmt_, ...) CYG_EMPTY_STATEMENT #endif typedef int (*cyg_nand_printf)(const char *fmt, ...) CYGBLD_ATTRIB_PRINTF_FORMAT(1,2); typedef cyg_uint32 cyg_nand_page_addr, cyg_nand_block_addr, cyg_nand_column_addr; /* ================================================================= */ /* Fwd def. A physical NAND device. */ struct _cyg_nand_device_t; typedef struct _cyg_nand_device_t cyg_nand_device; /* A partition of a NAND chip */ struct _cyg_nand_partition_t { cyg_nand_device *dev; /* The parent device. If NULL, this partition is inactive. */ cyg_nand_block_addr first; cyg_nand_block_addr last; }; typedef struct _cyg_nand_partition_t cyg_nand_partition; /* A set of device functions. These are intended to be abstractable * and transplantable amongst different boards using the same (or even * only similar) chip by only changing the 'priv' struct. * * Notes about device functions: * 1. The NAND library is responsible for checking that the input * page/block address is valid; device drivers need not. Hence, the * drivers are not given partition information. * 2. These functions all return 0 on success (positive if a value * is required), or a negative error code. * 3. The read and write sequence from the library is strictly: * (a) _begin * (b) Call _stride a number of times, for (at most) a whole page * (c) _finish * (d) If another page is to be read, repeat from (a). */ struct cyg_nand_dev_fns_v2 { /* Initialises the device, confirms its presence, * interrogates it as necessary, * sets up the in-memory partition array (if configured), * and populates other members of the _cyg_nand_device_t. * * If the device requires any locking on top of the per-device mutex * that this library provides, it should be set up by this call. * * Note that: * 1. The driver MUST NOT malloc; the NAND layer may run without * dynamic memory allocation, so anything the device requires should * be set up as a static global. Implementers should use the * driver API (cyg/hal/drv_api.h) as far as possible. * 2. We do not define at which level mutexes should apply, as it * may differ from device to device - they may need to lock out just * a single device, or an entire bus, or something more esoteric. */ int (*devinit) (cyg_nand_device *dev); /* NOTE: read_page and write_page were replaced in interface v2 * with the following stride-based functions. * The "striding" interface allows easy interfacing with hardware * ECC logic present in some NAND controllers. */ /* Initialises a page read operation, but does not actually read any * data. */ int (*read_begin)(cyg_nand_device *dev, cyg_nand_page_addr page); /* Reads (up to) a stride of data from the chip. * This call may wait for data to become available. * * The number of bytes to read - up to a stride - is given as `size'. * Only that many bytes will be written to dest. * * The size of a stride is implicit from the ECC algorithm used. */ int (*read_stride)(cyg_nand_device *dev, void * dest, size_t size); /* Reads out the OOB and (if necessary) finalises the read. * NOTE that this may be called without having read a whole page: * if so, use the Change Read Column command as appropriate to get * at the OOB area. If that proves impossible, return -ENOSYS. */ int (*read_finish)(cyg_nand_device *dev, void * spare, size_t spare_size); /* Sub-page reading. This is a strictly OPTIONAL operation and * may be given as NULL in the device struct. * If not present, the NAND layer will emulate it by reading the * whole page into a temporary buffer and copying out to the client, * which impacts on performance. * * @offset@ is in bytes relative to the start of the page; * @length@ is the number of bytes to read. * * N.B. This is a "standalone" call, i.e. will NOT be punctuated * by calls to read_begin and read_finish. */ int (*read_part_page)(cyg_nand_device *dev, void *dest, cyg_nand_page_addr page, size_t offset, size_t length); /* Initialises a write operation, but does not actually write any data. */ int (*write_begin)(cyg_nand_device *dev, cyg_nand_page_addr page); /* Writes (up to) a stride of data from the chip. * * The number of bytes to written - up to a stride - is given as `size'. * Only that many bytes will be written to the chip. * * The size of a stride is implicit from the ECC algorithm used. */ int (*write_stride)(cyg_nand_device *dev, const void * src, size_t size); /* Sends the OOB data to the chip and finalises the write. * NOTE that this may be called without having written a whole page: * if so, use the Change Write Column command as appropriate to get * at the OOB area. If that proves impossible, return -ENOSYS. */ int (*write_finish)(cyg_nand_device *dev, const void * spare, size_t spare_size); /* Erases an eraseblock. See cyg_nand_erase_block(). */ int (*erase_block)(cyg_nand_device *dev, cyg_nand_block_addr blk); /* Looks to see if a block contains the chip-specific factory-bad * marker. Returns 1 if bad, 0 if OK, or a -ve error code if * something went really wrong. */ int (*is_factory_bad)(cyg_nand_device *dev, cyg_nand_block_addr blk); }; /* Context for a physical NAND device. * Each device on the system should have its own. */ struct _cyg_nand_device_t { int version; /* Indicates the compatibility level this device supports. */ cyg_drv_mutex_t devlock; // Device-level locking applied by this library. int is_inited; /* Managed by the NAND library. If not set, only calls to devinit() are legal. */ cyg_nand_printf pf; /* Diagnostic printf to use. The default is configured by CDL, but a driver may override it here if desired. */ const char *devname; /* Device name for use by applications, e.g. "onboard" or "bus1". */ struct cyg_nand_dev_fns_v2 *fns; /* Access functions */ void * priv; /* If required, points to private device-specific data. */ void * plf_priv; /* If required, points to private platform-specific data. */ /* Data about the device need not be defined statically. Indeed * it is usually preferable to autodetect parameters when the chip * is initialised. The details here need only be set at some point * before fns->devinit() returns. * Some devices may wish to store a partition table on the device * itself, which is harder as it leads to a chicken-and-egg problem; * in that case, fns->devinit() should set dev->plf_update_partitions, * which the infrastructure will call later. */ cyg_nand_partition partition[CYGNUM_NAND_MAX_PARTITIONS]; /* If present (set up by dev->devinit()) this function will be * called on the first call to cyg_nand_get_partition(). * On exit it is expected to have updated dev->partition[]. * * Return 0 if OK, or a negative error code if something went wrong. * * NOTE: This function will be called WITHOUT the device lock held * and is expected to assert that lock only as far as necessary * (in particular, when updating the partition table contents). * * NOTE: If something happens that makes it desirable to update * the in-RAM partition table at runtime, you can call this * function directly. If you do this, BEWARE: any existing * clients will start using the updated geometry immediately. * If a partition has ceased to be valid then its clients will * immediately start returning errors! */ int (*plf_update_partitions)(cyg_nand_device *dev); /* Used by the infrastructure. */ cyg_bool plf_update_partitions_called; size_t page_bits; /* log2 of no of regular bytes per page */ size_t spare_per_page; /* OOB area size in bytes */ size_t block_page_bits; /* log2 of no of pages per eraseblock */ size_t blockcount_bits; /* log2 of number of blocks */ size_t chipsize_log; /* log2 of total chip size in BYTES. */ #ifdef CYGSEM_IO_NAND_USE_BBT struct { cyg_nand_block_addr primary, mirror; // or 0xFFFFFFFF if not present CYG_BYTE *data; /* in-RAM bad block table. See nand_bbt.c. devinit must set up data: 2 bits * number of blocks. */ size_t datasize; /* size of data in bytes, used to cross-check */ CYG_BYTE version; /* _current_ version tag */ } bbt; #endif cyg_nand_ecc_t *ecc; const cyg_nand_oob_layout *oob; /* Expansion fields go here. */ } CYG_HAL_TABLE_TYPE; #define CYG_NAND_FUNS ERROR_driver_function_update_to_v2_needed #define CYG_NAND_FUNS_V2(_funsv2_, _devinit_, \ _rdbegin_, _rdstride_, _rdfin_, \ _rdpart_, \ _wrbegin_, _wrstride_, _wrfin_, \ _erasebl_, _factorybad_ ) \ struct cyg_nand_dev_fns_v2 _funsv2_ = { \ .devinit = _devinit_, \ .read_begin = _rdbegin_, \ .read_stride = _rdstride_, \ .read_finish = _rdfin_, \ .read_part_page = _rdpart_, \ .write_begin = _wrbegin_, \ .write_stride = _wrstride_, \ .write_finish = _wrfin_, \ .erase_block = _erasebl_, \ .is_factory_bad = _factorybad_, \ } #define CYG_NAND_DEVICE_2(_structname_, _devname_, _funs_, _priv_, _plf_priv_, _ecc_, _oob_)\ struct _cyg_nand_device_t _structname_ CYG_HAL_TABLE_ENTRY(cyg_nand_dev) = \ { \ .version = 2, \ .is_inited = 0, \ .devname = _devname_, \ .fns = _funs_, \ .priv = _priv_, \ .plf_priv = _plf_priv_, \ .ecc = _ecc_, \ .oob = _oob_, \ } #define CYG_NAND_DEVICE(_structname_, _devname_, _funs_, _priv_, _ecc_, _oob_)\ CYG_NAND_DEVICE_2(_structname_, _devname_, _funs_, _priv_, NULL, _ecc_, _oob_) /* To declare a new NAND device, * first provide its functions and tie them together with CYG_NAND_FUNCTIONS_V2, * then instantiate it with CYG_NAND_DEVICE_V2. * - You can optionally provide private data in 'priv' which will be passed * to the driver functions (e.g. for register addresses or GPIO config). * - You can use a static ECC param block, or pass it as NULL in the macro * and set it up in devinit(). * * You also must decide on the partition layout for the device. * This could be hard-coded, defined in CDL, read from a "partition table", * user-supplied at runtime or something more esoteric. * Whatever you do, the partition table in the nand_device struct has to * be set up by the time devinit returns. */ /* The HAL table itself lives in nand.c. */ __externC cyg_nand_device cyg_nanddevtab[]; __externC cyg_nand_device cyg_nanddevtab_end; #endif
