您的位置:首页 > 运维架构 > Linux

uClinux2.6(bf561)的NorFlash驱动实现分析(1): 基本参数

2008-02-21 18:00 465 查看

下文开发环境为:AM29LV320MT(4M,16位),uclinux-2.6,VDSP4.5,bf561

在uClinux启动时可以看到类似的提示:
BF561 EZKIT Map: Found 1 x16 devices at 0x0 in 16-bit bank
它的输出是由:
printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank/n",
map->name, cfi->interleave, cfi->device_type*8, base,
map->bankwidth*8);
进行控制的。这行信息揭示了NorFlash的一些基本参数:
l map->bankwidth:MTD设备的接口总线宽度,其值可以为1,2或4。
l cfi->interleave:交错数,几块芯片平行连接成一块芯片,使bankwidth变大。
l cfi->device_type:单个芯片的总线宽度,其值为1、2或4。
l device_addr:nor flash设备的总线地址(寻址)范围视具体芯片以及其采用的位宽而定。
对于位扩展来讲:bankwidth=device_type*interleave
如富士通的29LV650:其容量是8Mbyte,共128个sector,每个sector的大小是64 kbyte。如果选择位宽为x8,设备总线的每个地址代表了一个byte的存储单元,其总线地址范围为8M(0x000000~0x7fffff)。如果选择位宽为x16,设备总线的每个地址代表了两个byte的存储单元,固其总线地址范围为4M(0x000000~0x3fffff)。
再如intel的E29F128:其容量为16Mbyte,共128个sector,每个sector的大小是128Kbyte。如果选择位宽为x8,设备总线的每个地址代表了一个byte的存储单元,其总线地址范围为16M(0x000000~0xffffff)。如果选择位宽为x16,这时候设备的A0脚不可用,所以不能访问到奇地址的存储单元,而只能0、2、4...地址的来访问,其总线地址范围为8M(0x000000~0xffffff的偶地址)。
在uClinux进行芯片检测时就充分考虑了各种可能的接线方式:
static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,
struct cfi_private *cfi)
{
int min_chips = (map_bankwidth(map)/4?:1); /* At most 4-bytes wide. */
int max_chips = map_bankwidth(map); /* And minimum 1 */
int nr_chips, type;

for (nr_chips = max_chips; nr_chips >= min_chips; nr_chips >>= 1) {

if (!cfi_interleave_supported(nr_chips))
continue;

cfi->interleave = nr_chips;

/* Minimum device size. Don't look for one 8-bit device
in a 16-bit bus, etc.
每个芯片的位宽 = (总线宽度 / 芯片数量)。
*/
type = map_bankwidth(map) / nr_chips;

for (; type <= CFI_DEVICETYPE_X32; type<<=1) {
cfi->device_type = type;

if (cp->probe_chip(map, 0, NULL, cfi))
return 1;
}
}
return 0;
}
在向norflash发送命令并读取flash的响应结果时则考虑到设备地址的问题:

/*
* Returns the command address according to the given geometry.
*/
static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int type)
{
return (cmd_ofs * type) * interleave;
}

/*
* Transforms the CFI command for the given geometry (bus width & interleave).
*/
map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi)
{
map_word val = { {0} };
int wordwidth, words_per_bus, chip_mode, chips_per_word;
unsigned long onecmd;
int i;

/* We do it this way to give the compiler a fighting chance
of optimising away all the crap for 'bankwidth' larger than
an unsigned long, in the common case where that support is
disabled */
if (map_bankwidth_is_large(map)) {
wordwidth = sizeof(unsigned long);
words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
} else {
wordwidth = map_bankwidth(map);
words_per_bus = 1;
}

chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);

/* First, determine what the bit-pattern should be for a single
device, according to chip mode and endianness... */
switch (chip_mode) {
default: BUG();
case 1:
onecmd = cmd;
break;
case 2:
onecmd = cpu_to_cfi16(cmd);
break;
case 4:
onecmd = cpu_to_cfi32(cmd);
break;
}

/* Now replicate it across the size of an unsigned long, or
just to the bus width as appropriate */
switch (chips_per_word) {
default: BUG();
#if BITS_PER_LONG >= 64
case 8:
onecmd |= (onecmd << (chip_mode * 32));
#endif
case 4:
onecmd |= (onecmd << (chip_mode * 16));
case 2:
onecmd |= (onecmd << (chip_mode * 8));
case 1:
;
}

/* And finally, for the multi-word case, replicate it
in all words in the structure */
for (i=0; i < words_per_bus; i++) {
val.x[i] = onecmd;
}

return val;
}

/*
* Sends a CFI command to a bank of flash for the given geometry.
*
* Returns the offset in flash where the command was written.
* If prev_val is non-null, it will be set to the value at the command address,
* before the command was written.
*/
uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
struct map_info *map, struct cfi_private *cfi,
int type, map_word *prev_val)
{
map_word val;
uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, cfi_interleave(cfi), type);

val = cfi_build_cmd(cmd, map, cfi);

if (prev_val)
*prev_val = map_read(map, addr);

map_write(map, val, addr);

return addr - base;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: