summaryrefslogtreecommitdiffstats
path: root/drivers/nand/nand.c
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2008-06-04 09:40:44 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2008-06-06 09:30:39 +0200
commit3d45abb7c4be454b6f2a7ede06fe961a04131083 (patch)
treef2e4581d485f0a6f285f3c6f62dd973330743e20 /drivers/nand/nand.c
parentf3351ebd7864b83b1f5f3cdc7c8a9f9110cb562f (diff)
downloadbarebox-3d45abb7c4be454b6f2a7ede06fe961a04131083.tar.gz
barebox-3d45abb7c4be454b6f2a7ede06fe961a04131083.tar.xz
[NAND] continue to make it work. Now works partly on at91sam9260
Diffstat (limited to 'drivers/nand/nand.c')
-rw-r--r--drivers/nand/nand.c165
1 files changed, 148 insertions, 17 deletions
diff --git a/drivers/nand/nand.c b/drivers/nand/nand.c
index 6700655d96..4bcd808fbb 100644
--- a/drivers/nand/nand.c
+++ b/drivers/nand/nand.c
@@ -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;
+
+ 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_ioctl(struct device_d *dev, int request, void *buf)
+{
+ 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 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;
- ret = nand->write_ecc(nand, offset, count, &retlen, buf, NULL, &nand->oobinfo);
+ 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)
- return ret;
- return retlen;
+ 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 int nand_probe (struct device_d *dev)
+static struct driver_d nand_controller_driver = {
+ .name = "nand_controller",
+ .probe = nand_controller_probe,
+};
+
+static int nand_device_probe(struct device_d *dev)
{
- printf("%s\n", __FUNCTION__);
return 0;
}
-static struct driver_d nand_driver = {
- .name = "nand_flash",
- .probe = nand_probe,
+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);