in chips/cfi_cmdset_0001.c [1706:1870]
static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
unsigned long adr, const struct kvec **pvec,
unsigned long *pvec_seek, int len)
{
struct cfi_private *cfi = map->fldrv_priv;
map_word status, write_cmd, datum;
unsigned long cmd_adr;
int ret, wbufsize, word_gap, words;
const struct kvec *vec;
unsigned long vec_seek;
unsigned long initial_adr;
int initial_len = len;
wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
adr += chip->start;
initial_adr = adr;
cmd_adr = adr & ~(wbufsize-1);
/* Sharp LH28F640BF chips need the first address for the
* Page Buffer Program command. See Table 5 of
* LH28F320BF, LH28F640BF, LH28F128BF Series (Appendix FUM00701) */
if (is_LH28F640BF(cfi))
cmd_adr = adr;
/* Let's determine this according to the interleave only once */
write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0xe8) : CMD(0xe9);
mutex_lock(&chip->mutex);
ret = get_chip(map, chip, cmd_adr, FL_WRITING);
if (ret) {
mutex_unlock(&chip->mutex);
return ret;
}
XIP_INVAL_CACHED_RANGE(map, initial_adr, initial_len);
ENABLE_VPP(map);
xip_disable(map, chip, cmd_adr);
/* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set
[...], the device will not accept any more Write to Buffer commands".
So we must check here and reset those bits if they're set. Otherwise
we're just pissing in the wind */
if (chip->state != FL_STATUS) {
map_write(map, CMD(0x70), cmd_adr);
chip->state = FL_STATUS;
}
status = map_read(map, cmd_adr);
if (map_word_bitsset(map, status, CMD(0x30))) {
xip_enable(map, chip, cmd_adr);
printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]);
xip_disable(map, chip, cmd_adr);
map_write(map, CMD(0x50), cmd_adr);
map_write(map, CMD(0x70), cmd_adr);
}
chip->state = FL_WRITING_TO_BUFFER;
map_write(map, write_cmd, cmd_adr);
ret = WAIT_TIMEOUT(map, chip, cmd_adr, 0, 0);
if (ret) {
/* Argh. Not ready for write to buffer */
map_word Xstatus = map_read(map, cmd_adr);
map_write(map, CMD(0x70), cmd_adr);
chip->state = FL_STATUS;
status = map_read(map, cmd_adr);
map_write(map, CMD(0x50), cmd_adr);
map_write(map, CMD(0x70), cmd_adr);
xip_enable(map, chip, cmd_adr);
printk(KERN_ERR "%s: Chip not ready for buffer write. Xstatus = %lx, status = %lx\n",
map->name, Xstatus.x[0], status.x[0]);
goto out;
}
/* Figure out the number of words to write */
word_gap = (-adr & (map_bankwidth(map)-1));
words = DIV_ROUND_UP(len - word_gap, map_bankwidth(map));
if (!word_gap) {
words--;
} else {
word_gap = map_bankwidth(map) - word_gap;
adr -= word_gap;
datum = map_word_ff(map);
}
/* Write length of data to come */
map_write(map, CMD(words), cmd_adr );
/* Write data */
vec = *pvec;
vec_seek = *pvec_seek;
do {
int n = map_bankwidth(map) - word_gap;
if (n > vec->iov_len - vec_seek)
n = vec->iov_len - vec_seek;
if (n > len)
n = len;
if (!word_gap && len < map_bankwidth(map))
datum = map_word_ff(map);
datum = map_word_load_partial(map, datum,
vec->iov_base + vec_seek,
word_gap, n);
len -= n;
word_gap += n;
if (!len || word_gap == map_bankwidth(map)) {
map_write(map, datum, adr);
adr += map_bankwidth(map);
word_gap = 0;
}
vec_seek += n;
if (vec_seek == vec->iov_len) {
vec++;
vec_seek = 0;
}
} while (len);
*pvec = vec;
*pvec_seek = vec_seek;
/* GO GO GO */
map_write(map, CMD(0xd0), cmd_adr);
chip->state = FL_WRITING;
ret = INVAL_CACHE_AND_WAIT(map, chip, cmd_adr,
initial_adr, initial_len,
chip->buffer_write_time,
chip->buffer_write_time_max);
if (ret) {
map_write(map, CMD(0x70), cmd_adr);
chip->state = FL_STATUS;
xip_enable(map, chip, cmd_adr);
printk(KERN_ERR "%s: buffer write error (status timeout)\n", map->name);
goto out;
}
/* check for errors */
status = map_read(map, cmd_adr);
if (map_word_bitsset(map, status, CMD(0x1a))) {
unsigned long chipstatus = MERGESTATUS(status);
/* reset status */
map_write(map, CMD(0x50), cmd_adr);
map_write(map, CMD(0x70), cmd_adr);
xip_enable(map, chip, cmd_adr);
if (chipstatus & 0x02) {
ret = -EROFS;
} else if (chipstatus & 0x08) {
printk(KERN_ERR "%s: buffer write error (bad VPP)\n", map->name);
ret = -EIO;
} else {
printk(KERN_ERR "%s: buffer write error (status 0x%lx)\n", map->name, chipstatus);
ret = -EINVAL;
}
goto out;
}
xip_enable(map, chip, cmd_adr);
out: DISABLE_VPP(map);
put_chip(map, chip, cmd_adr);
mutex_unlock(&chip->mutex);
return ret;
}