[NAND] continue to make it work. Now works partly on at91sam9260
This commit is contained in:
parent
f3351ebd78
commit
3d45abb7c4
|
@ -175,6 +175,9 @@ config CMD_FLASH
|
|||
tristate
|
||||
prompt "protect/erase"
|
||||
|
||||
config CMD_NAND
|
||||
tristate
|
||||
prompt "nand"
|
||||
endmenu
|
||||
|
||||
|
||||
|
|
|
@ -38,3 +38,4 @@ obj-$(CONFIG_CMD_EXPORT) += export.o
|
|||
obj-$(CONFIG_CMD_PRINTENV) += printenv.o
|
||||
obj-$(CONFIG_CMD_SAVEENV) += saveenv.o
|
||||
obj-$(CONFIG_CMD_LOADENV) += loadenv.o
|
||||
obj-$(CONFIG_CMD_NAND) += nand.o
|
||||
|
|
|
@ -51,14 +51,16 @@ static ssize_t nand_bb_read(struct device_d *dev, void *buf, size_t count,
|
|||
|
||||
printf("%s %d %d\n", __func__, offset, count);
|
||||
|
||||
ret = dev_ioctl(bb->physdev, MEMGETBADBLOCK, (void *)bb->offset);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
do {
|
||||
ret = dev_ioctl(bb->physdev, MEMGETBADBLOCK, (void *)bb->offset);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret) {
|
||||
printf("block is bad\n");
|
||||
bb->offset += bb->info.erasesize;
|
||||
}
|
||||
if (ret) {
|
||||
printf("block is bad\n");
|
||||
bb->offset += bb->info.erasesize;
|
||||
}
|
||||
} while (ret);
|
||||
|
||||
ret = dev_read(bb->physdev, buf, count, bb->offset, flags);
|
||||
if (ret > 0)
|
||||
|
@ -75,14 +77,16 @@ static ssize_t nand_bb_write(struct device_d *dev, const void *buf, size_t count
|
|||
|
||||
printf("%s %d %d\n", __func__, offset, count);
|
||||
|
||||
ret = dev_ioctl(bb->physdev, MEMGETBADBLOCK, (void *)bb->offset);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
do {
|
||||
ret = dev_ioctl(bb->physdev, MEMGETBADBLOCK, (void *)bb->offset);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret) {
|
||||
printf("block is bad\n");
|
||||
bb->offset += bb->info.erasesize;
|
||||
}
|
||||
if (ret) {
|
||||
printf("block is bad\n");
|
||||
bb->offset += bb->info.erasesize;
|
||||
}
|
||||
} while (ret);
|
||||
|
||||
ret = dev_write(bb->physdev, buf, count, bb->offset, flags);
|
||||
if (ret > 0)
|
||||
|
|
|
@ -4,5 +4,4 @@ obj-$(CONFIG_NAND) += nand_bbt.o
|
|||
obj-$(CONFIG_NAND) += nand.o
|
||||
obj-$(CONFIG_NAND) += nand_ecc.o
|
||||
obj-$(CONFIG_NAND) += nand_ids.o
|
||||
obj-$(CONFIG_NAND) += imx_nand.o
|
||||
#obj-$(CONFIG_NAND) += nand_util.o
|
||||
|
|
|
@ -23,58 +23,189 @@
|
|||
|
||||
#include <common.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <init.h>
|
||||
#include <xfuncs.h>
|
||||
#include <driver.h>
|
||||
#include <malloc.h>
|
||||
#include <ioctl.h>
|
||||
#include <nand.h>
|
||||
|
||||
static ssize_t nand_read(struct device_d *dev, void* buf, size_t count, ulong offset, ulong flags)
|
||||
{
|
||||
struct nand_chip *nand = dev->priv;
|
||||
size_t retlen;
|
||||
int ret;
|
||||
char oobuf[NAND_MAX_OOBSIZE];
|
||||
|
||||
printf("%s\n", __FUNCTION__);
|
||||
printf("nand_read: 0x%08x 0x%08x\n", offset, count);
|
||||
|
||||
ret = nand->read_ecc(nand, offset, count, &retlen, buf, oobuf, &nand->oobinfo);
|
||||
ret = nand->read(nand, offset, count, &retlen, buf);
|
||||
|
||||
if(ret)
|
||||
return ret;
|
||||
return retlen;
|
||||
}
|
||||
|
||||
static ssize_t nand_write(struct device_d* dev, const void* buf, size_t count, ulong offset, ulong flags)
|
||||
#define NOTALIGNED(x) (x & (nand->oobblock-1)) != 0
|
||||
|
||||
static ssize_t nand_write(struct device_d* dev, const void* buf, size_t _count, ulong offset, ulong flags)
|
||||
{
|
||||
struct nand_chip *nand = dev->priv;
|
||||
size_t retlen;
|
||||
size_t retlen, now;
|
||||
int ret;
|
||||
void *wrbuf = NULL;
|
||||
size_t count = _count;
|
||||
|
||||
ret = nand->write_ecc(nand, offset, count, &retlen, buf, NULL, &nand->oobinfo);
|
||||
if (ret)
|
||||
return ret;
|
||||
return retlen;
|
||||
printf("write: 0x%08x 0x%08x\n", offset, count);
|
||||
|
||||
while (count) {
|
||||
now = count > nand->oobblock ? nand->oobblock : count;
|
||||
|
||||
if (NOTALIGNED(now) || NOTALIGNED(offset)) {
|
||||
printf("not aligned: %d %d\n", nand->oobblock, (offset % nand->oobblock));
|
||||
wrbuf = xmalloc(nand->oobblock);
|
||||
memset(wrbuf, 0xff, nand->oobblock);
|
||||
memcpy(wrbuf + (offset % nand->oobblock), buf, now);
|
||||
ret = nand->write_ecc(nand, offset & ~(nand->oobblock - 1), nand->oobblock, &retlen, wrbuf,
|
||||
NULL, &nand->oobinfo);
|
||||
} else {
|
||||
ret = nand->write_ecc(nand, offset, now, &retlen, buf,
|
||||
NULL, &nand->oobinfo);
|
||||
printf("huhu offset: 0x%08x now: 0x%08x retlen: 0x%08x\n", offset, now, retlen);
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
offset += now;
|
||||
count -= now;
|
||||
buf += now;
|
||||
}
|
||||
|
||||
out:
|
||||
if (wrbuf)
|
||||
free(wrbuf);
|
||||
|
||||
return ret ? ret : _count;
|
||||
}
|
||||
|
||||
static int nand_probe (struct device_d *dev)
|
||||
static int nand_ioctl(struct device_d *dev, int request, void *buf)
|
||||
{
|
||||
printf("%s\n", __FUNCTION__);
|
||||
struct nand_chip *nand = dev->priv;
|
||||
struct mtd_info_user *info = buf;
|
||||
|
||||
switch (request) {
|
||||
case MEMGETBADBLOCK:
|
||||
printf("MEMGETBADBLOCK: 0x%08x\n", (off_t)buf);
|
||||
return nand->block_isbad(nand, (off_t)buf);
|
||||
case MEMGETINFO:
|
||||
info->type = nand->type;
|
||||
info->flags = nand->flags;
|
||||
info->size = nand->size;
|
||||
info->erasesize = nand->erasesize;
|
||||
info->oobsize = nand->oobsize;
|
||||
/* The below fields are obsolete */
|
||||
info->ecctype = -1;
|
||||
info->eccsize = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct driver_d nand_driver = {
|
||||
.name = "nand_flash",
|
||||
.probe = nand_probe,
|
||||
static ssize_t nand_erase(struct device_d *dev, size_t count, unsigned long offset)
|
||||
{
|
||||
struct nand_chip *nand = dev->priv;
|
||||
struct erase_info erase;
|
||||
int ret;
|
||||
|
||||
memset(&erase, 0, sizeof(erase));
|
||||
erase.nand = nand;
|
||||
erase.addr = offset;
|
||||
erase.len = nand->erasesize;
|
||||
|
||||
while (count > 0) {
|
||||
debug("erase %d %d\n", erase.addr, erase.len);
|
||||
|
||||
ret = nand->block_isbad(nand, erase.addr);
|
||||
if (ret > 0) {
|
||||
printf("Skipping bad block at 0x%08x\n", erase.addr);
|
||||
} else {
|
||||
ret = nand->erase(nand, &erase);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
erase.addr += nand->erasesize;
|
||||
count -= count > nand->erasesize ? nand->erasesize : count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nand_controller_probe(struct device_d *dev)
|
||||
{
|
||||
struct nand_chip *nand;
|
||||
struct nand_platform_data *pdata = dev->platform_data;
|
||||
int ret;
|
||||
|
||||
nand = xzalloc(sizeof(*nand));
|
||||
dev->priv = nand;
|
||||
|
||||
nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)dev->map_base;
|
||||
nand->hwcontrol = pdata->hwcontrol;
|
||||
nand->eccmode = pdata->eccmode;
|
||||
nand->dev_ready = pdata->dev_ready;
|
||||
nand->chip_delay = pdata->chip_delay;
|
||||
|
||||
ret = nand_scan(nand, 1);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
strcpy(nand->dev.name, "nand_device");
|
||||
get_free_deviceid(nand->dev.id, "nand");
|
||||
nand->dev.size = nand->chipsize;
|
||||
nand->dev.priv = nand;
|
||||
|
||||
ret = register_device(&nand->dev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
free(nand);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct driver_d nand_controller_driver = {
|
||||
.name = "nand_controller",
|
||||
.probe = nand_controller_probe,
|
||||
};
|
||||
|
||||
static int nand_device_probe(struct device_d *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct driver_d nand_device_driver = {
|
||||
.name = "nand_device",
|
||||
.probe = nand_device_probe,
|
||||
.read = nand_read,
|
||||
.write = nand_write,
|
||||
// .erase = nand_erase,
|
||||
// .protect= nand_protect,
|
||||
// .memmap = generic_memmap_ro,
|
||||
.ioctl = nand_ioctl,
|
||||
.open = dev_open_default,
|
||||
.close = dev_close_default,
|
||||
.lseek = dev_lseek_default,
|
||||
.erase = nand_erase,
|
||||
// .info = nand_info,
|
||||
};
|
||||
|
||||
static int nand_init(void)
|
||||
{
|
||||
return register_driver(&nand_driver);
|
||||
register_driver(&nand_device_driver);
|
||||
register_driver(&nand_controller_driver);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
device_initcall(nand_init);
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
#ifndef __NAND_H__
|
||||
#define __NAND_H__
|
||||
|
||||
struct nand_chip;
|
||||
|
||||
struct nand_platform_data {
|
||||
void (*hwcontrol)(struct nand_chip *, int cmd);
|
||||
int eccmode;
|
||||
int (*dev_ready)(struct nand_chip *);
|
||||
int chip_delay;
|
||||
};
|
||||
|
||||
#endif /* __NAND_H__ */
|
||||
|
Loading…
Reference in New Issue