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