Skip to contentSkip to conversation

Enabling D-cache for OneNAND bufferram

DISCUSSION Leo Barnes 4 participants 6 messages Mar 24, 2010 – Mar 26, 2010 Memory technology devices (MTD)

lore
Share & tools
lore

Leo Barnes

author
#0 lore raw

Hello!

I am currently writing a piece of software that takes advantage of the fact that OneNAND supports read-while-loading. What I am doing is trying to achieve higher efficiency when decoding data from the OneNAND by decoding data that is available in the OneNAND bufferram directly instead of copying it to normal RAM and decoding there. So far, I have gotten quite poor results due to the fact that the bufferrams are not cached (for good reason since it is Device memory). My software will be used on ARM devices of different types, with my testrigs being a Nokia N810 and a N900.

I have exclusive access to the OneNAND chip, and as such know exactly what information is stored in the bufferrams at all times. If I could only enable the D-cache for the bufferrams, I could therefore make sure to invalidate the cache whenever I reprogram the OneNAND chip.

From what I have read so far, it seems that the only way to enable cacheing of the bufferrams would be to somehow change how the memory attributes for the memory region in the MMU. I have however not found any easy way of doing this. Any tips? I dont really have very much experience when it comes to how the MMU works.

Best regards,
Leo

#1 lore raw
Quoted 19 lines

Hello!

I am currently writing a piece of software that takes advantage of the fact that OneNAND supports read-while-loading. What I am doing is trying to achieve higher efficiency when decoding data from the OneNAND by decoding data that is available in the OneNAND bufferram directly instead of copying it to normal RAM and decoding there. So far, I have gotten quite poor results due to the fact that the bufferrams are not cached (for good reason since it is Device memory). My software will be used on ARM devices of different types, with my testrigs being a Nokia N810 and a N900.

I have exclusive access to the OneNAND chip, and as such know exactly what information is stored in the bufferrams at all times. If I could only enable the D-cache for the bufferrams, I could therefore make sure to invalidate the cache whenever I reprogram the OneNAND chip.

Quoted 5 lines

From what I have read so far, it seems that the only way to enable
cacheing of the bufferrams would be to somehow change how the memory attributes for the memory region in the MMU. I have however not found any easy way of doing this. Any tips? I dont really have very much experience when it comes to how the MMU works.

Have a look at ioremap_cached(). Seems to be availabe on ARM Basically you need to create a second virtual address mapping of the devices RAM which is cached.

Jocke

author
#2 lore raw
Quoted 24 lines · Joakim Tjernlund
Quoted 17 lines

Hello!

I am currently writing a piece of software that takes advantage of the fact that OneNAND supports read-while-loading. What I am doing is trying to achieve higher efficiency when decoding data from the OneNAND by decoding data that is available in the OneNAND bufferram directly instead of copying it to normal RAM and decoding there. So far, I have gotten quite poor results due to the fact that the bufferrams are not cached (for good reason since it is Device memory). My software will be used on ARM devices of different types, with my testrigs being a Nokia N810 and a N900.

I have exclusive access to the OneNAND chip, and as such know exactly what information is stored in the bufferrams at all times. If I could only enable the D-cache for the bufferrams, I could therefore make sure to invalidate the cache whenever I reprogram the OneNAND chip.

Quoted 6 lines

From what I have read so far, it seems that the only way to enable
cacheing of the bufferrams would be to somehow change how the memory attributes for the memory region in the MMU. I have however not found any easy way of doing this. Any tips? I dont really have very much experience when it comes to how the MMU works.

Quoted 7 lines

Have a look at ioremap_cached(). Seems to be availabe on ARM Basically you need to create a second virtual address mapping of the devices RAM which is cached.

Jocke

Thanks for the suggestion, but I am having some trouble getting it to work. This is how I tried to use ioremap_cached, along with the problems I got:

Given a pointer to the first byte in the bufferram, I did:
unsigned long int phys_addr = virt_to_phys(bufaddr); void *rmap = ioremap_cached(phys_addr, 2048);
/* Read from rmap */
iounmap(rmap);

This would not compile however since L_PTE_CACHEABLE was not defined. After scanning through the kernel source, I found that L_PTE_CACHEABLE was usually defined as (1 << 3) for arm. With this defined, ioremap_cached returned a new virtual address, but gave a segmentation fault along with "Unhandled fault: external abort on non-linefetch (0x008) at 0xc8accc00" in /proc/kmsg when trying to do a read. Am I using ioremap correctly? Does ioremap change the old mapping, or simply add a second one? If it creates a second one without changing the first, this is illegal on ARM IIRC since two mappings cannot have differing memory attributes...

Any other ideas?

Best regards,
//Leo

#3 lore raw
Quoted 54 lines
Quoted 24 lines · Joakim Tjernlund
Quoted 17 lines

Hello!

I am currently writing a piece of software that takes advantage of the fact that OneNAND supports read-while-loading. What I am doing is trying to achieve higher efficiency when decoding data from the OneNAND by decoding data that is available in the OneNAND bufferram directly instead of copying it to normal RAM and decoding there. So far, I have gotten quite poor results due to the fact that the bufferrams are not cached (for good reason since it is Device memory). My software will be used on ARM devices of different types, with my testrigs being a Nokia N810 and a N900.

I have exclusive access to the OneNAND chip, and as such know exactly what information is stored in the bufferrams at all times. If I could only enable the D-cache for the bufferrams, I could therefore make sure to invalidate the cache whenever I reprogram the OneNAND chip.

Quoted 6 lines

From what I have read so far, it seems that the only way to enable
cacheing of the bufferrams would be to somehow change how the memory attributes for the memory region in the MMU. I have however not found any easy way of doing this. Any tips? I dont really have very much experience when it comes to how the MMU works.

Quoted 7 lines

Have a look at ioremap_cached(). Seems to be availabe on ARM Basically you need to create a second virtual address mapping of the devices RAM which is cached.

Jocke

Thanks for the suggestion, but I am having some trouble getting it to work. This is how I tried to use ioremap_cached, along with the problems I got:

Given a pointer to the first byte in the bufferram, I did:
unsigned long int phys_addr = virt_to_phys(bufaddr); void *rmap = ioremap_cached(phys_addr, 2048);
/* Read from rmap */
iounmap(rmap);

This would not compile however since L_PTE_CACHEABLE was not defined. After scanning through the kernel source, I found that L_PTE_CACHEABLE was usually defined as (1 << 3) for arm. With this defined, ioremap_cached returned a new virtual address, but gave a segmentation fault along with "Unhandled fault: external abort on non-linefetch (0x008) at 0xc8accc00" in /proc/kmsg when trying to do a read. Am I using ioremap correctly? Does ioremap change the old mapping, or simply add a second one? If it creates a second one without changing the first, this is illegal on ARM IIRC since two mappings cannot have differing memory attributes...

Quoted 1 line

Any other ideas?

ioremap returns a *second* mapping to the same RAM so you first original mapping should be fine.

I have used ioremap in 2.4 to access NOR flash in cached mode and it worked fine. one just have to rember to invalidate the cache when needed. This was on PPC and I knonw nothing about ARM.

I can think of two things:
1) check that virt_to_phys(bufaddr) returns the correct physical address for your NAND.
2) have a look at __ioremap instead of ioremap_cached

Perhaps you need to talk to the ARM people, it may very well be a bug in ARM arch code.

Jocke

#4 lore raw

Hi all,
Some time ago I used cache for buffer ram of Numonyx OneNand successfully, getting big improvement of the performance (also thanks to the burst access).
The platform was based on StrongARM processor. I've attached the patch I used. Note that the patch was for 2.6.27 kernel; I've rid it of changes not related to the cache feature, so I think you should apply it "by hands". I've removed also the burst enabling because strictly depending on the platform.
Massimo

2010/3/25 Joakim Tjernlund <[email protected]>:

Quoted 72 lines
Quoted 54 lines
Quoted 24 lines · Joakim Tjernlund
Quoted 17 lines

Hello!

I am currently writing a piece of software that takes advantage of the fact that OneNAND supports read-while-loading. What I am doing is trying to achieve higher efficiency when decoding data from the OneNAND by decoding data that is available in the OneNAND bufferram directly instead of copying it to normal RAM and decoding there. So far, I have gotten quite poor results due to the fact that the bufferrams are not cached (for good reason since it is Device memory). My software will be used on ARM devices of different types, with my testrigs being a Nokia N810 and a N900.

I have exclusive access to the OneNAND chip, and as such know exactly what information is stored in the bufferrams at all times. If I could only enable the D-cache for the bufferrams, I could therefore make sure to invalidate the cache whenever I reprogram the OneNAND chip.

Quoted 6 lines

 From what I have read so far, it seems that the only way to enable cacheing of the bufferrams would be to somehow change how the memory attributes for the memory region in the MMU. I have however not found any easy way of doing this. Any tips? I dont really have very much experience when it comes to how the MMU works.

Quoted 7 lines

Have a look at ioremap_cached(). Seems to be availabe on ARM Basically you need to create a second virtual address mapping of the devices RAM which is cached.

    Jocke

Thanks for the suggestion, but I am having some trouble getting it to work. This is how I tried to use ioremap_cached, along with the problems I got:

Given a pointer to the first byte in the bufferram, I did:
unsigned long int phys_addr = virt_to_phys(bufaddr); void *rmap = ioremap_cached(phys_addr, 2048);
/* Read from rmap */
iounmap(rmap);

This would not compile however since L_PTE_CACHEABLE was not defined. After scanning through the kernel source, I found that L_PTE_CACHEABLE was usually defined as (1 << 3) for arm. With this defined, ioremap_cached returned a new virtual address, but gave a segmentation fault along with "Unhandled fault: external abort on non-linefetch (0x008) at 0xc8accc00" in /proc/kmsg when trying to do a read. Am I using ioremap correctly? Does ioremap change the old mapping, or simply add a second one? If it creates a second one without changing the first, this is illegal on ARM IIRC since two mappings cannot have differing memory attributes...

Quoted 1 line

Any other ideas?

ioremap returns a *second* mapping to the same RAM so you first original mapping should be fine.

I have used ioremap in 2.4 to access NOR flash in cached mode and it worked fine. one just have to rember to invalidate the cache when needed. This was on PPC and I knonw nothing about ARM.

I can think of two things:
 1) check that virt_to_phys(bufaddr) returns the correct physical address for your NAND.
 2) have a look at __ioremap instead of ioremap_cached

Perhaps you need to talk to the ARM people, it may very well be a bug in ARM arch code.

Quoted 8 lines

 Jocke

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

home/cirillom/src/linux-2.6.27-onenand/drivers/mtd/onenand/generic.c renamed from mtd/onenand/generic.c +45 -2
diff -uprN drivers/mtd/onenand/generic.c /home/cirillom/src/linux-2.6.27-onenand/drivers/mtd/onenand/generic.c
@@ -25,17 +25,120 @@
 
 #define DRIVER_NAME	"onenand"
+#define MAXCIR_ONENAND_CACHE
+#define ONENAND_PHYSMAPADDR
+#define ONENAND_SIZE
+static struct flash_platform_data mst_onenand_data = {
+	.nr_parts	= 0,
+};
+
+static struct resource mst_onenand_resource[] = {
+	[0] = {
+		.flags	= IORESOURCE_IO,
+		.start	= 0x04000000,
+		.end	= 0x04000000 + SZ_128K - 1,
+	},
+};
+
+static struct platform_device mst_onenand_device = {
+	.name		= "onenand",
+	.id		= -1,
+	.dev		= {
+		.platform_data	= &mst_onenand_data,
+	},
+	.num_resources	= ARRAY_SIZE(mst_onenand_resource),
+	.resource	= mst_onenand_resource,
+};
+
+#endif
+
+#endif
 
 #ifdef CONFIG_MTD_PARTITIONS
 static const char *part_probes[] = { "cmdlinepart", NULL,  };
 #endif
 
 struct onenand_info {
 	struct mtd_info		mtd;
 	struct mtd_partition	*parts;
 	struct onenand_chip	onenand;
 };
 
+#ifdef MAXCIR_ONENAND_CACHE
+#include <asm/cacheflush.h>
+static void on_inval_cache_range (struct onenand_chip *info, unsigned long offset, ssize_t len) {
+    flush_ioremap_region(NULL, info->cached_rambuff, offset, len);
+}
+#endif
+
@@ -60,25 +163,67 @@ static int __devinit generic_onenand_pro
 		goto out_release_mem_region;
 	}
 
-	info->onenand.mmcontrol = pdata->mmcontrol;
+#ifdef MAXCIR_ONENAND_CACHE
+    info->onenand.cached_rambuff = ioremap_cached(res->start+ONENAND_DATARAM, 2*readw(info->onenand.base + ONENAND_REG_DATA_BUFFER_SIZE));
+    printk("\n ## Phys. address: 0x%X",res->start+ONENAND_DATARAM);    
+    printk("\n ## Virt. address: 0x%X",(int)(info->onenand.base+0x400));    
+    printk("\n ## Cache address: 0x%X",(int)(info->onenand.cached_rambuff));    
+    printk("\n ## Cached range size: %d bytes\n",2*readw(info->onenand.base + ONENAND_REG_DATA_BUFFER_SIZE));    
+    info->onenand.inval_cache = on_inval_cache_range;
+#endif
+
    info->onenand.mmcontrol = pdata->mmcontrol;
 	info->onenand.irq = platform_get_irq(pdev, 0);
 
 	info->mtd.name = pdev->dev.bus_id;
 	info->mtd.priv = &info->onenand;
 	info->mtd.owner = THIS_MODULE;
 
-	if (onenand_scan(&info->mtd, 1)) {
+    if (onenand_scan(&info->mtd, 1)) {
 		err = -ENXIO;
 		goto out_iounmap;
 	}
 
 #ifdef CONFIG_MTD_PARTITIONS
 	err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
 	if (err > 0)
 		add_mtd_partitions(&info->mtd, info->parts, err);
 	else if (err <= 0 && pdata->parts)
 		add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
 	else
 #endif
 		err = add_mtd_device(&info->mtd);

@@ -114,6 +259,10 @@ static int __devexit generic_onenand_rem
onenand_release(&info->mtd); release_mem_region(res->start, size); iounmap(info->onenand.base);
+#ifdef MAXCIR_ONENAND_CACHE
+ if (info->onenand.cached_rambuff != NULL)
+ iounmap(info->onenand.cached_rambuff);
+#endif
kfree(info);
}

home/cirillom/src/linux-2.6.27-onenand/drivers/mtd/onenand/onenand_base.c renamed from mtd/onenand/onenand_base.c +0 -0
diff -uprN drivers/mtd/onenand/onenand_base.c /home/cirillom/src/linux-2.6.27-onenand/drivers/mtd/onenand/onenand_base.c

*
* Get OneNAND density from device ID
*/
+ /* Ritorna un numero x (compreso fra 0 e 5) tale che: chip_size = 16MB * 2^x = 2^(24+x)B
+ */
static inline int onenand_get_density(int dev_id)
{
int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
@@ -496,10 +522,31 @@ static int onenand_read_bufferram(struct
{
struct onenand_chip *this = mtd->priv;
void __iomem *bufferram;
+#ifdef MAXCIR_ONENAND_CACHE
+ int bufferRamShift = onenand_bufferram_offset(mtd, area);
+#endif

- bufferram = this->base + area;

- bufferram += onenand_bufferram_offset(mtd, area);
+#ifdef MAXCIR_ONENAND_CACHE
+ if ((this->cached_rambuff != NULL) &&(area==ONENAND_DATARAM)) {
+ this->inval_cache(this,bufferRamShift+offset,count); /* invalidate the cache lines associated to the onenand ram buffers */
+ bufferram = this->cached_rambuff; /* no need to add area since cached_rambuff is already the cached ram buffer */
+ } else
+ bufferram = this->base + area;
+
+ bufferram += bufferRamShift;
+#else
+ bufferram = this->base + area; /* aggiunge all'indirizzo base lo shift opportuno per bootram, dataram, spareram */
+
+ bufferram += onenand_bufferram_offset(mtd, area); /* aggiunge l'ooportuno shift nel caso il buffer corrente sia dataRAM2 */
+#endif

if (ONENAND_CHECK_BYTE_ACCESS(count)) {
unsigned short word;
@@ -999,7 +1079,12 @@ static int onenand_read_oob_nolock(struc
thislen = oobsize - column;
thislen = min_t(int, thislen, len);

- this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
+ /* Fix hw bug in ST OneNand 1G and 2G rev.A */
+ if ((this->manufacturer_id == ONENAND_MFR_NUMONYX) &&
+ ((this->device_id == 0x0030) || ((this->device_id == 0x0040) && (this->version_id == 0x003E))))
+ this->command(mtd, ONENAND_CMD_READ, from, mtd->oobsize);
+ else
+ this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);

onenand_update_bufferram(mtd, from, 0);
@@ -1189,7 +1274,12 @@ int onenand_bbt_read_oob(struct mtd_info
thislen = mtd->oobsize - column; thislen = min_t(int, thislen, len);

- this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
+ /* Fix hw bug in ST OneNand 1G and 2G rev.A */
+ if ((this->manufacturer_id == ONENAND_MFR_NUMONYX) &&
+ ((this->device_id == 0x0030) || ((this->device_id == 0x0040) && (this->version_id == 0x003E))))
+ this->command(mtd, ONENAND_CMD_READ, from, mtd->oobsize);
+ else
+ this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);

onenand_update_bufferram(mtd, from, 0);
@@ -1232,7 +1322,12 @@ static int onenand_verify_oob(struct mtd
u_char *oob_buf = this->oob_buf;
int status, i;

- this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
+ /* Fix hw bug in ST OneNand 1G and 2G rev.A */
+ if ((this->manufacturer_id == ONENAND_MFR_NUMONYX) &&
+ ((this->device_id == 0x0030) || ((this->device_id == 0x0040) && (this->version_id == 0x003E))))
+ this->command(mtd, ONENAND_CMD_READ, to, mtd->oobsize);
+ else
+ this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
onenand_update_bufferram(mtd, to, 0);
status = this->wait(mtd, FL_READING);
if (status)
@@ -1276,13 +1371,31 @@ static int onenand_verify(struct mtd_inf

onenand_update_bufferram(mtd, addr, 1);

+#ifdef MAXCIR_ONENAND_CACHE
+ dataram=this->cached_rambuff!=NULL ? this->cached_rambuff : (this->base + ONENAND_DATARAM);
+#else
dataram = this->base + ONENAND_DATARAM;
+#endif
+
dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM);

- if (memcmp(buf, dataram + column, thislen))
- return -EBADMSG;
+#ifdef MAXCIR_ONENAND_CACHE
+ if (this->cached_rambuff!=NULL)
+ this->inval_cache(this,onenand_bufferram_offset(mtd, ONENAND_DATARAM)+column,thislen); /* invalidate the cache lines associated to the onenand ram buffers */
+#endif
+ if (memcmp(buf, dataram + column, thislen))
+ return -EBADMSG;

		len -= thislen;

buf += thislen;
addr += thislen;
}
@@ -2529,6 +2680,7 @@ static void onenand_print_device_info(in

static const struct onenand_manufacturers onenand_manuf_ids[] = {
{ONENAND_MFR_SAMSUNG, "Samsung"},
+ {ONENAND_MFR_NUMONYX, "Numonyx"},
};

/**

home/cirillom/src/linux-2.6.27-onenand/include/linux/mtd/onenand.h renamed from linux/mtd/onenand.h +15 -0
diff -uprN include/linux/mtd/onenand.h /home/cirillom/src/linux-2.6.27-onenand/include/linux/mtd/onenand.h	
@@ -12,6 +12,11 @@
 #ifndef __LINUX_MTD_ONENAND_H
 #define __LINUX_MTD_ONENAND_H
 
+#define MAXCIR_ONENAND_CACHE
+
 #include <linux/spinlock.h>
 #include <linux/completion.h>
 #include <linux/mtd/onenand_regs.h>
@@ -95,6 +100,7 @@ struct onenand_chip {
 	unsigned int		chipsize;
 	unsigned int		device_id;
 	unsigned int		version_id;
+    unsigned int        manufacturer_id;
 	unsigned int		density_mask;
 	unsigned int		options;
 
@@ -133,6 +139,17 @@ struct onenand_chip {
 	void			*bbm;
 
 	void			*priv;
+#ifdef MAXCIR_ONENAND_CACHE
+    /* cached address for ram buffer */
+    void *cached_rambuff;
+	/* It's possible for the map driver to use cached memory in its
+	   copy_from implementation (and _only_ with copy_from).  However,
+	   when the chip driver knows some flash area has changed contents,
+	   it will signal it to the map driver through this routine to let
+	   the map driver invalidate the corresponding cache as needed.
+	   If there is no cache to care about this can be set to NULL. */
+	void (*inval_cache)(struct onenand_chip*, unsigned long, ssize_t);
+#endif
 };
 
 /*
@@ -176,6 +193,7 @@ struct onenand_chip {
  * OneNAND Flash Manufacturer ID Codes
  */
 #define ONENAND_MFR_SAMSUNG	0xec
+#define ONENAND_MFR_NUMONYX	0x20
 
 /**
  * struct onenand_manufacturers - NAND Flash Manufacturer ID Structure
#5 lore raw
In reply to Joakim Tjernlund
Quoted 2 lines · Joakim Tjernlund

ioremap returns a *second* mapping to the same RAM so you first original mapping should be fine.

Note that having two mappings with different attributes to the same address is architecturally prohibited on (at least) some ARMs. I.e. it may not behave correctly.

Then again, dma_alloc_coherent has to do it. But be careful! Remember to flush the cache before unmapping the cached area if the driver has an exit function.

-- Jamie