1/* $NetBSD: flash_vrip.c,v 1.6 2008/04/28 20:23:22 martin Exp $ */
2
3/*
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Naoto Shimazaki of YOKOGAWA Electric Corporation.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * Flash Memory Driver
34 */
35
36#include <sys/cdefs.h>
37__KERNEL_RCSID(0, "$NetBSD: flash_vrip.c,v 1.6 2008/04/28 20:23:22 martin Exp $");
38
39#include <sys/param.h>
40#include <sys/conf.h>
41#include <sys/device.h>
42#include <sys/kernel.h>
43#include <sys/malloc.h>
44#include <sys/proc.h>
45#include <sys/systm.h>
46
47#include <machine/bus.h>
48
49#include <hpcmips/vr/vripif.h>
50#include <hpcmips/vr/cfireg.h>
51#include <hpcmips/vr/flashreg.h>
52#include <hpcmips/vr/flashvar.h>
53
54#ifdef FLASH_DEBUG
55int	flash_debug = 0;
56#define DPRINTF(x)	if (flash_debug) printf x
57#else
58#define DPRINTF(x)
59#endif
60
61static int flash_probe(struct device *, struct cfdata *, void *);
62static void flash_attach(struct device *, struct device *, void *);
63
64const static struct flashops * find_command_set(u_int8_t cmdset0,
65						u_int8_t cmdset1);
66static int i28f128_probe(bus_space_tag_t, bus_space_handle_t);
67static int mbm29160_probe(bus_space_tag_t, bus_space_handle_t);
68static int is_block_same(struct flash_softc *, bus_size_t, const void *);
69static int probe_cfi(bus_space_tag_t iot, bus_space_handle_t ioh);
70
71static int intel_erase(struct flash_softc *, bus_size_t);
72static int intel_write(struct flash_softc *, bus_size_t);
73static int amd_erase(struct flash_softc *, bus_size_t);
74static int amd_write(struct flash_softc *, bus_size_t);
75
76extern struct cfdriver flash_cd;
77
78CFATTACH_DECL(flash_vrip, sizeof(struct flash_softc),
79	      flash_probe, flash_attach, NULL, NULL);
80
81dev_type_open(flashopen);
82dev_type_close(flashclose);
83dev_type_read(flashread);
84dev_type_write(flashwrite);
85
86const struct cdevsw flash_cdevsw = {
87	flashopen, flashclose, flashread, flashwrite, noioctl,
88	nostop, notty, nopoll, nommap, nokqfilter,
89};
90
91static const struct flash_command_set {
92	u_int8_t	fc_set0;
93	u_int8_t	fc_set1;
94	struct flashops	fc_ops;
95} flash_cmd[] = {
96	{
97		.fc_set0	= CFI_COMMSET_INTEL0,
98		.fc_set1	= CFI_COMMSET_INTEL1,
99		.fc_ops		= {
100			.fo_name	= "Intel",
101			.fo_erase	= intel_erase,
102			.fo_write	= intel_write,
103		}
104	},
105	{
106		.fc_set0	= CFI_COMMSET_AMDFJITU0,
107		.fc_set1	= CFI_COMMSET_AMDFJITU1,
108		.fc_ops		= {
109			.fo_name	= "AMD/Fujitsu",
110			.fo_erase	= amd_erase,
111			.fo_write	= amd_write,
112		}
113	},
114	{
115		.fc_set0	= 0,
116		.fc_set1	= 0,
117		.fc_ops		= {
118			.fo_name	= NULL,
119			.fo_erase	= NULL,
120			.fo_write	= NULL,
121		}
122	}
123};
124
125
126const static struct flashops *
127find_command_set(u_int8_t cmdset0, u_int8_t cmdset1)
128{
129	const struct flash_command_set	*fc;
130
131	for (fc = flash_cmd; fc->fc_ops.fo_name; fc++) {
132		if (cmdset0 == fc->fc_set0 && cmdset1 == fc->fc_set1)
133			return &fc->fc_ops;
134	}
135	return NULL;
136}
137
138static int
139probe_cfi(bus_space_tag_t iot, bus_space_handle_t ioh)
140{
141	const u_int8_t	*idstr = CFI_QUERY_ID_STR;
142	int		i;
143	u_int8_t	cmdset0;
144	u_int8_t	cmdset1;
145
146	/* start Common Flash Interface Query */
147	bus_space_write_2(iot, ioh, CFI_QUERY_OFFSET, CFI_READ_CFI_QUERY);
148
149	/* read CFI Query ID string */
150	i = CFI_QUERY_ID_STR_REG << 1;
151	do {
152		if (bus_space_read_2(iot, ioh, i) != *idstr) {
153			bus_space_write_2(iot, ioh, 0, FLASH_RESET);
154			return 1;
155		}
156		i += 2;
157		idstr++;
158	} while (*idstr);
159
160	cmdset0 = bus_space_read_2(iot, ioh, CFI_PRIM_COMM_REG0 << 1);
161	cmdset1 = bus_space_read_2(iot, ioh, CFI_PRIM_COMM_REG1 << 1);
162
163	/* switch flash to read mode */
164	bus_space_write_2(iot, ioh, 0, FLASH_RESET);
165
166	if (!find_command_set(cmdset0, cmdset1))
167		return 1;
168
169	return 0;
170}
171
172static int
173flash_probe(struct device *parent, struct cfdata *match, void *aux)
174{
175	struct vrip_attach_args	*va = aux;
176	bus_space_handle_t	ioh;
177
178	if (bus_space_map(va->va_iot, va->va_addr, va->va_size, 0, &ioh))
179		return 0;
180	if (!probe_cfi(va->va_iot, ioh)) {
181		DPRINTF("CFI ID str and command set recognized\n");
182		goto detect;
183	}
184	if (!i28f128_probe(va->va_iot, ioh)) {
185		DPRINTF("28F128 detected\n");
186		goto detect;
187	}
188	if (!mbm29160_probe(va->va_iot, ioh)) {
189		DPRINTF("29LV160 detected\n");
190		goto detect;
191	}
192	return 0;
193
194detect:
195	bus_space_unmap(va->va_iot, ioh, va->va_size);
196	return 1;
197}
198
199static void
200flash_attach(struct device *parent, struct device *self, void *aux)
201{
202	struct flash_softc	*sc = (void *) self;
203	struct vrip_attach_args	*va = aux;
204	int			i;
205	int			fence;
206	bus_space_tag_t		iot = va->va_iot;
207	bus_space_handle_t	ioh;
208	size_t			block_size;
209
210	if (bus_space_map(iot, va->va_addr, va->va_size, 0, &ioh)) {
211		printf(": can't map i/o space\n");
212                return;
213  	}
214
215	sc->sc_iot = iot;
216	sc->sc_ioh = ioh;
217	sc->sc_size = va->va_size;
218	sc->sc_status = 0;
219
220	/*
221	 * Read entire CFI structure
222	 */
223	bus_space_write_2(iot, ioh, CFI_QUERY_OFFSET, CFI_READ_CFI_QUERY);
224	for (i = 0; i < CFI_TOTAL_SIZE; i++) {
225		sc->sc_cfi_raw[i] = bus_space_read_2(iot, ioh, i << 1);
226	}
227	bus_space_write_2(iot, ioh, 0, FLASH_RESET);
228
229	sc->sc_ops = find_command_set(sc->sc_cfi_raw[CFI_PRIM_COMM_REG0],
230				      sc->sc_cfi_raw[CFI_PRIM_COMM_REG1]);
231	if (sc->sc_ops) {
232		printf(": using %s command set", sc->sc_ops->fo_name);
233	} else {
234		printf("opps sc->sc_ops is NULL\n");
235	}
236
237	/*
238	 * determine size of the largest block
239	 */
240	sc->sc_block_size = 0;
241	i = CFI_EBLK1_INFO_REG;
242	fence = sc->sc_cfi_raw[CFI_NUM_ERASE_BLK_REG] * CFI_EBLK_INFO_SIZE
243		+ i;
244	for (; i < fence; i += CFI_EBLK_INFO_SIZE) {
245		if (sc->sc_cfi_raw[i + CFI_EBLK_INFO_NSECT0] == 0
246		    && sc->sc_cfi_raw[i + CFI_EBLK_INFO_NSECT1] == 0)
247			continue;
248		block_size
249			= (sc->sc_cfi_raw[i + CFI_EBLK_INFO_SECSIZE0] << 8)
250			+ (sc->sc_cfi_raw[i + CFI_EBLK_INFO_SECSIZE1] << 16);
251		if (sc->sc_block_size < block_size)
252			sc->sc_block_size = block_size;
253	}
254
255	if ((sc->sc_buf = malloc(sc->sc_block_size, M_DEVBUF, M_NOWAIT))
256	    == NULL) {
257		printf(": can't alloc buffer space\n");
258		return;
259	}
260
261	sc->sc_write_buffer_size
262		= 1 << (sc->sc_cfi_raw[CFI_MAX_WBUF_SIZE_REG0]
263			+ (sc->sc_cfi_raw[CFI_MAX_WBUF_SIZE_REG1] << 8));
264	sc->sc_typ_word_prog_timo
265		= 1 << sc->sc_cfi_raw[CFI_TYP_WORD_PROG_REG];
266	sc->sc_max_word_prog_timo
267		= 1 << sc->sc_cfi_raw[CFI_MAX_WORD_PROG_REG];
268	sc->sc_typ_buffer_write_timo
269		= 1 << sc->sc_cfi_raw[CFI_TYP_BUF_WRITE_REG];
270	sc->sc_max_buffer_write_timo
271		= 1 << sc->sc_cfi_raw[CFI_MAX_BUF_WRITE_REG];
272	sc->sc_typ_block_erase_timo
273		= 1 << sc->sc_cfi_raw[CFI_TYP_BLOCK_ERASE_REG];
274	sc->sc_max_block_erase_timo
275		= 1 << sc->sc_cfi_raw[CFI_MAX_BLOCK_ERASE_REG];
276
277	printf("\n");
278
279#ifdef FLASH_DEBUG
280	printf("read_cfi: extract cfi\n");
281	printf("max block size: %dbyte\n", sc->sc_block_size);
282	printf("write buffer size: %dbyte\n", sc->sc_write_buffer_size);
283	printf("typical word program timeout: %dusec\n",
284	       sc->sc_typ_word_prog_timo);
285	printf("maximam word program timeout: %dusec (%d time of typ)\n",
286	       sc->sc_typ_word_prog_timo * sc->sc_max_word_prog_timo,
287	       sc->sc_max_word_prog_timo);
288	printf("typical buffer write timeout: %dusec\n",
289	       sc->sc_typ_buffer_write_timo);
290	printf("maximam buffer write timeout: %dusec (%d time of typ)\n",
291	       sc->sc_typ_buffer_write_timo * sc->sc_max_buffer_write_timo,
292	       sc->sc_max_buffer_write_timo);
293	printf("typical block erase timeout: %dmsec\n",
294	       sc->sc_typ_block_erase_timo);
295	printf("maximam block erase timeout: %dmsec (%d time of typ)\n",
296	       sc->sc_typ_block_erase_timo * sc->sc_max_block_erase_timo,
297	       sc->sc_max_block_erase_timo);
298
299	printf("read_cfi: dump cfi\n");
300	for (i = 0; i < CFI_TOTAL_SIZE;) {
301		int	j;
302		for (j = 0; j < 16; j++) {
303			printf("%02x ", sc->sc_cfi_raw[i++]);
304		}
305		printf("\n");
306	}
307#endif
308}
309
310int
311flashopen(dev_t dev, int flag, int mode, struct lwp *l)
312{
313	struct flash_softc	*sc;
314
315	sc = device_lookup_private(&flash_cd, minor(dev));
316	if (sc == NULL)
317		return ENXIO;
318	if (sc->sc_status & FLASH_ST_BUSY)
319		return EBUSY;
320	sc->sc_status |= FLASH_ST_BUSY;
321	return 0;
322}
323
324int
325flashclose(dev_t dev, int flag, int mode, struct lwp *l)
326{
327	struct flash_softc	*sc;
328
329	sc = device_lookup_private(&flash_cd, minor(dev));
330	sc->sc_status &= ~FLASH_ST_BUSY;
331	return 0;
332}
333
334int
335flashread(dev_t dev, struct uio *uio, int flag)
336{
337	struct flash_softc	*sc;
338	bus_space_tag_t		iot;
339	bus_space_handle_t	ioh;
340	bus_size_t		off;
341	int			total;
342	int			count;
343	int			error;
344
345	sc = device_lookup_private(&flash_cd, minor(dev));
346	iot = sc->sc_iot;
347	ioh = sc->sc_ioh;
348
349	off = uio->uio_offset;
350	total = min(sc->sc_size - off, uio->uio_resid);
351
352	while (total > 0) {
353		count = min(sc->sc_block_size, uio->uio_resid);
354		bus_space_read_region_1(iot, ioh, off, sc->sc_buf, count);
355		if ((error = uiomove(sc->sc_buf, count, uio)) != 0)
356			return error;
357		off += count;
358		total -= count;
359	}
360	return 0;
361}
362
363
364int
365flashwrite(dev_t dev, struct uio *uio, int flag)
366{
367	struct flash_softc	*sc;
368	bus_space_tag_t		iot;
369	bus_space_handle_t	ioh;
370	bus_size_t		off;
371	int			stat;
372	int			error;
373
374	sc = device_lookup_private(&flash_cd, minor(dev));
375
376	if (sc->sc_size < uio->uio_offset + uio->uio_resid)
377		return ENOSPC;
378	if (uio->uio_offset % sc->sc_block_size)
379		return EINVAL;
380	if (uio->uio_resid % sc->sc_block_size)
381		return EINVAL;
382
383	iot = sc->sc_iot;
384	ioh = sc->sc_ioh;
385
386	for (off = uio->uio_offset;
387	     uio->uio_resid > 0;
388	     off += sc->sc_block_size) {
389		if ((error = uiomove(sc->sc_buf, sc->sc_block_size, uio)) != 0)
390			return error;
391		if (is_block_same(sc, off, sc->sc_buf))
392			continue;
393		if ((stat = flash_block_erase(sc, off)) != 0) {
394			printf("block erase failed status = 0x%x\n", stat);
395			return EIO;
396		}
397		if ((stat = flash_block_write(sc, off)) != 0) {
398			printf("block write failed status = 0x%x\n", stat);
399			return EIO;
400		}
401	}
402	return 0;
403}
404
405/*
406 * XXX
407 * this function is too much specific for the device.
408 */
409static int
410i28f128_probe(bus_space_tag_t iot, bus_space_handle_t ioh)
411{
412	static const u_int8_t	vendor_code[] = {
413		0x89,	/* manufacturer code: 	intel */
414		0x18,	/* device code:		28F128 */
415	};
416
417	static const u_int8_t	idstr[] = {
418		'Q', 'R', 'Y',
419		0x01, 0x00,
420		0x31, 0x00,
421		0xff
422	};
423
424	int	i;
425
426	/* start Common Flash Interface Query */
427	bus_space_write_2(iot, ioh, 0, CFI_READ_CFI_QUERY);
428	/* read CFI Query ID string */
429	for (i = 0; idstr[i] != 0xff; i++) {
430		if (bus_space_read_2(iot, ioh, (0x10 + i) << 1) != idstr[i])
431			return 1;
432	}
433
434	/* read manufacturer code and device code */
435	if (bus_space_read_2(iot, ioh, 0x00) != vendor_code[0])
436		return 1;
437	if (bus_space_read_2(iot, ioh, 0x02) != vendor_code[1])
438		return 1;
439
440	bus_space_write_2(iot, ioh, 0, I28F128_RESET);
441	return 0;
442}
443
444/*
445 * XXX
446 * this function is too much specific for the device.
447 */
448static int
449mbm29160_probe(bus_space_tag_t iot, bus_space_handle_t ioh)
450{
451	static const u_int16_t	vendor_code[] = {
452		0x0004,	/* manufacturer code: 	intel */
453		0x2249,	/* device code:		29LV160BE */
454	};
455
456	static const u_int8_t	idstr[] = {
457		'Q', 'R', 'Y',
458		0x02, 0x00,
459		0x40, 0x00,
460		0xff
461	};
462
463	int	i;
464
465	/* start Common Flash Interface Query */
466	bus_space_write_2(iot, ioh, 0xaa, CFI_READ_CFI_QUERY);
467	/* read CFI Query ID string */
468	for (i = 0; idstr[i] != 0xff; i++) {
469		if (bus_space_read_2(iot, ioh, (0x10 + i) << 1) != idstr[i])
470			return 1;
471	}
472
473	bus_space_write_2(iot, ioh, 0, 0xff);
474
475	/* read manufacturer code and device code */
476	bus_space_write_2(iot, ioh, 0x555 << 1, 0xaa);
477	bus_space_write_2(iot, ioh, 0x2aa << 1, 0x55);
478	bus_space_write_2(iot, ioh, 0x555 << 1, 0x90);
479	if (bus_space_read_2(iot, ioh, 0x00) != vendor_code[0])
480		return 1;
481	if (bus_space_read_2(iot, ioh, 0x02) != vendor_code[1])
482		return 1;
483
484	bus_space_write_2(iot, ioh, 0, 0xff);
485	return 0;
486}
487
488static int
489is_block_same(struct flash_softc *sc, bus_size_t offset, const void *bufp)
490{
491	bus_space_tag_t		iot = sc->sc_iot;
492	bus_space_handle_t	ioh = sc->sc_ioh;
493	const u_int8_t		*p = bufp;
494	int			count = sc->sc_block_size;
495
496	while (count-- > 0) {
497		if (bus_space_read_1(iot, ioh, offset++) != *p++)
498			return 0;
499	}
500	return 1;
501}
502
503static int
504intel_erase(struct flash_softc *sc, bus_size_t offset)
505{
506	bus_space_tag_t		iot = sc->sc_iot;
507	bus_space_handle_t	ioh = sc->sc_ioh;
508	int			status;
509	int			i;
510
511	bus_space_write_2(iot, ioh, offset, I28F128_BLK_ERASE_1ST);
512	bus_space_write_2(iot, ioh, offset, I28F128_BLK_ERASE_2ND);
513
514	status = 0;
515	for (i = sc->sc_max_block_erase_timo; i > 0; i--) {
516		tsleep(sc, PRIBIO, "blockerase",
517		       1 + (sc->sc_typ_block_erase_timo * hz) / 1000);
518		if ((status = bus_space_read_2(iot, ioh, offset))
519		    & I28F128_S_READY)
520			break;
521	}
522	if (i == 0)
523		status |= FLASH_TIMEOUT;
524
525	bus_space_write_2(iot, ioh, offset, I28F128_CLEAR_STATUS);
526	bus_space_write_2(iot, ioh, offset, I28F128_RESET);
527
528	return status & (FLASH_TIMEOUT
529			 | I28F128_S_ERASE_SUSPEND
530			 | I28F128_S_COMSEQ_ERROR
531			 | I28F128_S_ERASE_ERROR
532			 | I28F128_S_BLOCK_LOCKED);
533}
534
535static int
536intel_write(struct flash_softc *sc, bus_size_t offset)
537{
538	bus_space_tag_t		iot = sc->sc_iot;
539	bus_space_handle_t	ioh = sc->sc_ioh;
540	int			wbuf_size;
541	int			timo;
542	int			status;
543	bus_size_t		fence;
544	int			i;
545	const u_int16_t		*p;
546
547	/* wbuf_size = size in u_int16_t */
548	wbuf_size = sc->sc_write_buffer_size >> 1;
549
550	p = (u_int16_t *) sc->sc_buf;
551	fence = offset + sc->sc_block_size;
552	do {
553		status = 0;
554		for (timo = sc->sc_max_buffer_write_timo; timo > 0; timo--) {
555			bus_space_write_2(iot, ioh, offset,
556					  I28F128_WRITE_BUFFER);
557			status = bus_space_read_2(iot, ioh, offset);
558			if (status & I28F128_XS_BUF_AVAIL)
559				break;
560			DELAY(sc->sc_typ_buffer_write_timo);
561		}
562		if (timo == 0) {
563			status |= FLASH_TIMEOUT;
564			goto errout;
565		}
566
567		bus_space_write_2(iot, ioh, offset, wbuf_size - 1);
568
569		for (i = wbuf_size; i > 0; i--, p++, offset += 2)
570			bus_space_write_2(iot, ioh, offset, *p);
571
572		bus_space_write_2(iot, ioh, offset, I28F128_WBUF_CONFIRM);
573
574		do {
575			bus_space_write_2(iot, ioh, offset,
576					  I28F128_READ_STATUS);
577			status = bus_space_read_2(iot, ioh, offset);
578		} while (!(status & I28F128_S_READY));
579
580	} while (offset < fence);
581
582	bus_space_write_2(iot, ioh, offset, I28F128_CLEAR_STATUS);
583	bus_space_write_2(iot, ioh, offset, I28F128_RESET);
584
585	return 0;
586
587errout:
588	bus_space_write_2(iot, ioh, offset, I28F128_CLEAR_STATUS);
589	bus_space_write_2(iot, ioh, offset, I28F128_RESET);
590
591	status &= (FLASH_TIMEOUT
592		   | I28F128_S_PROG_ERROR
593		   | I28F128_S_COMSEQ_ERROR
594		   | I28F128_S_LOW_VOLTAGE
595		   | I28F128_S_PROG_SUSPEND
596		   | I28F128_S_BLOCK_LOCKED);
597	return status;
598}
599
600static int
601amd_erase_sector(struct flash_softc *sc, bus_size_t offset)
602{
603	bus_space_tag_t		iot = sc->sc_iot;
604	bus_space_handle_t	ioh = sc->sc_ioh;
605	int			i;
606
607	DPRINTF(("amd_erase_sector offset = %08lx\n", offset));
608
609	bus_space_write_2(iot, ioh,
610			  MBM29LV160_COMM_ADDR0, MBM29LV160_COMM_CMD0);
611	bus_space_write_2(iot, ioh,
612			  MBM29LV160_COMM_ADDR1, MBM29LV160_COMM_CMD1);
613	bus_space_write_2(iot, ioh,
614			  MBM29LV160_COMM_ADDR2, MBM29LV160_ESECT_CMD2);
615	bus_space_write_2(iot, ioh,
616			  MBM29LV160_COMM_ADDR3, MBM29LV160_ESECT_CMD3);
617	bus_space_write_2(iot, ioh,
618			  MBM29LV160_COMM_ADDR4, MBM29LV160_ESECT_CMD4);
619	bus_space_write_2(iot, ioh, offset, MBM29LV160_ESECT_CMD5);
620
621	for (i = sc->sc_max_block_erase_timo; i > 0; i--) {
622		tsleep(sc, PRIBIO, "blockerase",
623		       1 + (sc->sc_typ_block_erase_timo * hz) / 1000);
624		if (bus_space_read_2(iot, ioh, offset) == 0xffff)
625			return 0;
626	}
627
628	return FLASH_TIMEOUT;
629}
630
631static int
632amd_erase(struct flash_softc *sc, bus_size_t offset)
633{
634	static const struct mbm29lv_subsect {
635		u_int16_t	devcode;
636		u_int32_t	subsect_mask;
637		u_int32_t	subsect_addr;
638	} subsect[] = {
639		{
640			MBM29LV160TE_DEVCODE,
641			MBM29LV160_SUBSECT_MASK,
642			MBM29LV160TE_SUBSECT_ADDR
643		},
644		{
645			MBM29LV160BE_DEVCODE,
646			MBM29LV160_SUBSECT_MASK,
647			MBM29LV160BE_SUBSECT_ADDR
648		},
649		{ 0, 0, 0 }
650	};
651
652	bus_space_tag_t			iot = sc->sc_iot;
653	bus_space_handle_t		ioh = sc->sc_ioh;
654	u_int16_t			devcode;
655	const struct mbm29lv_subsect	*ss;
656	bus_size_t			fence;
657	int				step;
658	int				status;
659
660	bus_space_write_2(iot, ioh,
661			  MBM29LV160_COMM_ADDR0, MBM29LV160_COMM_CMD0);
662	bus_space_write_2(iot, ioh,
663			  MBM29LV160_COMM_ADDR1, MBM29LV160_COMM_CMD1);
664	bus_space_write_2(iot, ioh,
665			  MBM29LV160_COMM_ADDR2, MBM29LV160_SIGN_CMD2);
666	devcode = bus_space_read_2(iot, ioh, MBM29LV160_DEVCODE_REG);
667
668	for (ss = subsect; ss->devcode; ss++) {
669		if (ss->devcode == devcode)
670			break;
671	}
672	if (ss->devcode == 0) {
673		printf("flash: amd_erase(): unknown device code %04x\n",
674		       devcode);
675		return -1;
676	}
677
678	DPRINTF(("flash: amd_erase(): devcode = %04x subsect = %08x\n",
679		 devcode, ss->subsect_addr));
680
681	fence = offset + sc->sc_block_size;
682	step = (offset & ss->subsect_mask) == ss->subsect_addr
683		? MBM29LV160_SUBSECT_SIZE : MBM29LV160_SECT_SIZE;
684	do {
685		if ((status = amd_erase_sector(sc, offset)) != 0)
686			return status;
687		offset += step;
688	} while (offset < fence);
689
690	return 0;
691}
692
693static int
694amd_write(struct flash_softc *sc, bus_size_t offset)
695{
696	bus_space_tag_t		iot = sc->sc_iot;
697	bus_space_handle_t	ioh = sc->sc_ioh;
698	int			timo;
699	bus_size_t		fence;
700	const u_int16_t		*p;
701
702	p = (u_int16_t *) sc->sc_buf;
703	fence = offset + sc->sc_block_size;
704	do {
705		bus_space_write_2(iot, ioh,
706				  MBM29LV160_COMM_ADDR0,
707				  MBM29LV160_COMM_CMD0);
708		bus_space_write_2(iot, ioh,
709				  MBM29LV160_COMM_ADDR1,
710				  MBM29LV160_COMM_CMD1);
711		bus_space_write_2(iot, ioh,
712				  MBM29LV160_COMM_ADDR2,
713				  MBM29LV160_PROG_CMD2);
714		bus_space_write_2(iot, ioh, offset, *p);
715
716		for (timo = sc->sc_max_word_prog_timo; timo > 0; timo--) {
717			if (bus_space_read_2(iot, ioh, offset) == *p)
718				break;
719			DELAY(sc->sc_typ_word_prog_timo);
720		}
721		if (timo == 0)
722			return FLASH_TIMEOUT;
723
724		p++;
725		offset += 2;
726	} while (offset < fence);
727
728	return 0;
729}
730