cfi_flash: Fix unaligned accesses to cfi_qry structure
authorAndrew Gabbasov <andrew_gabbasov@mentor.com>
Tue, 14 May 2013 17:27:52 +0000 (12:27 -0500)
committerStefan Roese <sr@denx.de>
Thu, 23 May 2013 07:47:59 +0000 (09:47 +0200)
Packed structure cfi_qry contains unaligned 16- and 32-bits members,
accessing which causes problems when cfi_flash driver is compiled with
-munaligned-access option: flash initialization hangs, probably
due to data error.

Since the structure is supposed to replicate the actual data layout
in CFI Flash chips, the alignment issue can't be fixed in the structure.
So, unaligned fields need using of explicit unaligned access macros.

Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Reviewed-By: Albert ARIBAUD <albert.u.boot@aribaud.net>
Signed-off-by: Stefan Roese <sr@denx.de>
drivers/mtd/cfi_flash.c
include/mtd/cfi_flash.h

index 22d84407dd4a6513eaed35bb029c009c86d40536..f6759a80ea57ad57a8d9c725e0a599326c59d62b 100644 (file)
@@ -38,6 +38,7 @@
 #include <asm/processor.h>
 #include <asm/io.h>
 #include <asm/byteorder.h>
+#include <asm/unaligned.h>
 #include <environment.h>
 #include <mtd/cfi_flash.h>
 #include <watchdog.h>
@@ -1640,9 +1641,10 @@ static void cfi_reverse_geometry(struct cfi_qry *qry)
        u32 tmp;
 
        for (i = 0, j = qry->num_erase_regions - 1; i < j; i++, j--) {
-               tmp = qry->erase_region_info[i];
-               qry->erase_region_info[i] = qry->erase_region_info[j];
-               qry->erase_region_info[j] = tmp;
+               tmp = get_unaligned(&(qry->erase_region_info[i]));
+               put_unaligned(get_unaligned(&(qry->erase_region_info[j])),
+                             &(qry->erase_region_info[i]));
+               put_unaligned(tmp, &(qry->erase_region_info[j]));
        }
 }
 
@@ -2073,8 +2075,8 @@ ulong flash_get_size (phys_addr_t base, int banknum)
        info->start[0] = (ulong)map_physmem(base, info->portwidth, MAP_NOCACHE);
 
        if (flash_detect_cfi (info, &qry)) {
-               info->vendor = le16_to_cpu(qry.p_id);
-               info->ext_addr = le16_to_cpu(qry.p_adr);
+               info->vendor = le16_to_cpu(get_unaligned(&(qry.p_id)));
+               info->ext_addr = le16_to_cpu(get_unaligned(&(qry.p_adr)));
                num_erase_regions = qry.num_erase_regions;
 
                if (info->ext_addr) {
@@ -2163,7 +2165,8 @@ ulong flash_get_size (phys_addr_t base, int banknum)
                                break;
                        }
 
-                       tmp = le32_to_cpu(qry.erase_region_info[i]);
+                       tmp = le32_to_cpu(get_unaligned(
+                                               &(qry.erase_region_info[i])));
                        debug("erase region %u: 0x%08lx\n", i, tmp);
 
                        erase_region_count = (tmp & 0xffff) + 1;
index 966b5e00cac20a3cd36a60917ff226980908ffee..b644b91773f21e7576d253ec8d16786cf432670d 100644 (file)
@@ -129,12 +129,16 @@ typedef union {
 } cfiword_t;
 
 /* CFI standard query structure */
+/* The offsets and sizes of this packed structure members correspond
+ * to the actual layout in CFI Flash chips. Some 16- and 32-bit members
+ * are unaligned and must be accessed with explicit unaligned access macros.
+ */
 struct cfi_qry {
        u8      qry[3];
-       u16     p_id;
-       u16     p_adr;
-       u16     a_id;
-       u16     a_adr;
+       u16     p_id;                   /* unaligned */
+       u16     p_adr;                  /* unaligned */
+       u16     a_id;                   /* unaligned */
+       u16     a_adr;                  /* unaligned */
        u8      vcc_min;
        u8      vcc_max;
        u8      vpp_min;
@@ -148,10 +152,10 @@ struct cfi_qry {
        u8      block_erase_timeout_max;
        u8      chip_erase_timeout_max;
        u8      dev_size;
-       u16     interface_desc;
-       u16     max_buf_write_size;
+       u16     interface_desc;         /* aligned */
+       u16     max_buf_write_size;     /* aligned */
        u8      num_erase_regions;
-       u32     erase_region_info[NUM_ERASE_REGIONS];
+       u32     erase_region_info[NUM_ERASE_REGIONS];   /* unaligned */
 } __attribute__((packed));
 
 struct cfi_pri_hdr {