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