xref: /netbsd-src/sys/dev/nor/nor.c (revision c7fb772b85b2b5d4cfb282f868f454b4701534fd)
1*c7fb772bSthorpej /*	$NetBSD: nor.c,v 1.7 2021/08/07 16:19:13 thorpej Exp $	*/
2de9f8578Sahoka 
3de9f8578Sahoka /*-
4de9f8578Sahoka  * Copyright (c) 2011 Department of Software Engineering,
5de9f8578Sahoka  *		      University of Szeged, Hungary
6de9f8578Sahoka  * Copyright (c) 2011 Adam Hoka <ahoka@NetBSD.org>
7de9f8578Sahoka  * All rights reserved.
8de9f8578Sahoka  *
9de9f8578Sahoka  * This code is derived from software contributed to The NetBSD Foundation
10de9f8578Sahoka  * by the Department of Software Engineering, University of Szeged, Hungary
11de9f8578Sahoka  *
12de9f8578Sahoka  * Redistribution and use in source and binary forms, with or without
13de9f8578Sahoka  * modification, are permitted provided that the following conditions
14de9f8578Sahoka  * are met:
15de9f8578Sahoka  * 1. Redistributions of source code must retain the above copyright
16de9f8578Sahoka  *    notice, this list of conditions and the following disclaimer.
17de9f8578Sahoka  * 2. Redistributions in binary form must reproduce the above copyright
18de9f8578Sahoka  *    notice, this list of conditions and the following disclaimer in the
19de9f8578Sahoka  *    documentation and/or other materials provided with the distribution.
20de9f8578Sahoka  *
21de9f8578Sahoka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22de9f8578Sahoka  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23de9f8578Sahoka  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24de9f8578Sahoka  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25de9f8578Sahoka  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26de9f8578Sahoka  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27de9f8578Sahoka  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28de9f8578Sahoka  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29de9f8578Sahoka  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30de9f8578Sahoka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31de9f8578Sahoka  * SUCH DAMAGE.
32de9f8578Sahoka  */
33de9f8578Sahoka 
34de9f8578Sahoka /* Common driver for NOR chips implementing the ONFI CFI specification */
35de9f8578Sahoka 
36de9f8578Sahoka #include <sys/cdefs.h>
37*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: nor.c,v 1.7 2021/08/07 16:19:13 thorpej Exp $");
38de9f8578Sahoka 
39de9f8578Sahoka #include "locators.h"
40bf03074aScliff #include "opt_nor.h"
41de9f8578Sahoka 
42de9f8578Sahoka #include <sys/param.h>
43de9f8578Sahoka #include <sys/types.h>
44de9f8578Sahoka #include <sys/device.h>
45de9f8578Sahoka #include <sys/kmem.h>
46de9f8578Sahoka #include <sys/sysctl.h>
47de9f8578Sahoka #include <sys/atomic.h>
48de9f8578Sahoka 
49de9f8578Sahoka #include <dev/flash/flash.h>
50bf03074aScliff #include <dev/flash/flash_io.h>
51de9f8578Sahoka #include <dev/nor/nor.h>
52de9f8578Sahoka 
53de9f8578Sahoka 
54bf03074aScliff static int nor_match(device_t, cfdata_t, void *);
55bf03074aScliff static void nor_attach(device_t, device_t, void *);
56bf03074aScliff static int nor_detach(device_t, int);
57bf03074aScliff static bool nor_shutdown(device_t, int);
58bf03074aScliff static int nor_print(void *, const char *);
59de9f8578Sahoka static int nor_search(device_t, cfdata_t, const int *, void *);
60de9f8578Sahoka 
61bf03074aScliff /* flash interface implementation */
62bf03074aScliff static int nor_flash_isbad(device_t, flash_off_t, bool *);
63bf03074aScliff static int nor_flash_markbad(device_t, flash_off_t);
64bf03074aScliff static int nor_flash_write(device_t, flash_off_t, size_t, size_t *,
65bf03074aScliff 	const u_char *);
66bf03074aScliff static int nor_flash_read(device_t, flash_off_t, size_t, size_t *, uint8_t *);
67bf03074aScliff static int nor_flash_erase_all(device_t);
68bf03074aScliff static int nor_flash_erase(device_t, struct flash_erase_instruction *);
69bf03074aScliff static int nor_flash_submit(device_t, buf_t *);
70bf03074aScliff 
71bf03074aScliff /* default functions for driver development */
72bf03074aScliff static void nor_default_select(device_t, bool);
73bf03074aScliff static int  nor_default_read_page(device_t, flash_off_t, uint8_t *);
74bf03074aScliff static int  nor_default_program_page(device_t, flash_off_t, const uint8_t *);
75bf03074aScliff 
76bf03074aScliff static int nor_scan_media(device_t, struct nor_chip *);
77bf03074aScliff 
78de9f8578Sahoka CFATTACH_DECL_NEW(nor, sizeof(struct nor_softc),
79de9f8578Sahoka     nor_match, nor_attach, nor_detach, NULL);
80de9f8578Sahoka 
81de9f8578Sahoka #ifdef NOR_DEBUG
82de9f8578Sahoka int	nordebug = NOR_DEBUG;
83de9f8578Sahoka #endif
84de9f8578Sahoka 
85de9f8578Sahoka int nor_cachesync_timeout = 1;
86de9f8578Sahoka int nor_cachesync_nodenum;
87de9f8578Sahoka 
88bf03074aScliff struct flash_interface nor_flash_if = {
89bf03074aScliff 	.type = FLASH_TYPE_NOR,
90bf03074aScliff 
91bf03074aScliff 	.read = nor_flash_read,
92bf03074aScliff 	.write = nor_flash_write,
93bf03074aScliff 	.erase = nor_flash_erase,
94bf03074aScliff 	.block_isbad = nor_flash_isbad,
95bf03074aScliff 	.block_markbad = nor_flash_markbad,
96bf03074aScliff 
97bf03074aScliff 	.submit = nor_flash_submit
98bf03074aScliff };
99bf03074aScliff 
100de9f8578Sahoka #ifdef NOR_VERBOSE
1015df0d50bScliff const struct nor_manufacturer nor_mfrs[] = {
102de9f8578Sahoka 	{ NOR_MFR_AMD,		"AMD" },
103de9f8578Sahoka 	{ NOR_MFR_FUJITSU,	"Fujitsu" },
104de9f8578Sahoka 	{ NOR_MFR_RENESAS,	"Renesas" },
105de9f8578Sahoka 	{ NOR_MFR_STMICRO,	"ST Micro" },
106de9f8578Sahoka 	{ NOR_MFR_MICRON,	"Micron" },
107de9f8578Sahoka 	{ NOR_MFR_NATIONAL,	"National" },
108de9f8578Sahoka 	{ NOR_MFR_TOSHIBA,	"Toshiba" },
109de9f8578Sahoka 	{ NOR_MFR_HYNIX,	"Hynix" },
1101608a978Scliff 	{ NOR_MFGR_MACRONIX,	"Macronix" },
111de9f8578Sahoka 	{ NOR_MFR_SAMSUNG,	"Samsung" },
112de9f8578Sahoka 	{ NOR_MFR_UNKNOWN,	"Unknown" }
113de9f8578Sahoka };
114de9f8578Sahoka 
115de9f8578Sahoka static const char *
nor_midtoname(int id)116de9f8578Sahoka nor_midtoname(int id)
117de9f8578Sahoka {
118de9f8578Sahoka 	int i;
119de9f8578Sahoka 
120de9f8578Sahoka 	for (i = 0; nor_mfrs[i].id != 0; i++) {
121de9f8578Sahoka 		if (nor_mfrs[i].id == id)
122de9f8578Sahoka 			return nor_mfrs[i].name;
123de9f8578Sahoka 	}
124de9f8578Sahoka 
125de9f8578Sahoka 	KASSERT(nor_mfrs[i].id == 0);
126de9f8578Sahoka 
127de9f8578Sahoka 	return nor_mfrs[i].name;
128de9f8578Sahoka }
129de9f8578Sahoka #endif
130de9f8578Sahoka 
131de9f8578Sahoka /* ARGSUSED */
132bf03074aScliff static int
nor_match(device_t parent,cfdata_t match,void * aux)133de9f8578Sahoka nor_match(device_t parent, cfdata_t match, void *aux)
134de9f8578Sahoka {
135de9f8578Sahoka 	/* pseudo device, always attaches */
136de9f8578Sahoka 	return 1;
137de9f8578Sahoka }
138de9f8578Sahoka 
139bf03074aScliff static void
nor_attach(device_t parent,device_t self,void * aux)140de9f8578Sahoka nor_attach(device_t parent, device_t self, void *aux)
141de9f8578Sahoka {
142bf03074aScliff 	struct nor_softc * const sc = device_private(self);
143bf03074aScliff 	struct nor_attach_args * const naa = aux;
144bf03074aScliff 	struct nor_chip * const chip = &sc->sc_chip;
145de9f8578Sahoka 
146de9f8578Sahoka 	sc->sc_dev = self;
147bf03074aScliff 	sc->sc_controller_dev = parent;
148bf03074aScliff 	sc->sc_nor_if = naa->naa_nor_if;
149de9f8578Sahoka 
150de9f8578Sahoka 	aprint_naive("\n");
151bf03074aScliff 	aprint_normal("\n");
152bf03074aScliff 
153bf03074aScliff 	if (nor_scan_media(self, chip))
154bf03074aScliff 		return;
155bf03074aScliff 
156bf03074aScliff 	sc->sc_flash_if = nor_flash_if;
157bf03074aScliff 	sc->sc_flash_if.erasesize = chip->nc_block_size;
158bf03074aScliff 	sc->sc_flash_if.page_size = chip->nc_page_size;
159bf03074aScliff 	sc->sc_flash_if.writesize = chip->nc_page_size;
160de9f8578Sahoka 
161de9f8578Sahoka 	/* allocate cache */
162bf03074aScliff #ifdef NOTYET
163de9f8578Sahoka 	chip->nc_oob_cache = kmem_alloc(chip->nc_spare_size, KM_SLEEP);
164bf03074aScliff #endif
165de9f8578Sahoka 	chip->nc_page_cache = kmem_alloc(chip->nc_page_size, KM_SLEEP);
166de9f8578Sahoka 
167de9f8578Sahoka 	mutex_init(&sc->sc_device_lock, MUTEX_DEFAULT, IPL_NONE);
168de9f8578Sahoka 
169bf03074aScliff 	if (flash_sync_thread_init(&sc->sc_flash_io, self, &sc->sc_flash_if)) {
170de9f8578Sahoka 		goto error;
171de9f8578Sahoka 	}
172de9f8578Sahoka 
173de9f8578Sahoka 	if (!pmf_device_register1(sc->sc_dev, NULL, NULL, nor_shutdown))
174de9f8578Sahoka 		aprint_error_dev(sc->sc_dev,
175de9f8578Sahoka 		    "couldn't establish power handler\n");
176de9f8578Sahoka 
177de9f8578Sahoka #ifdef NOR_BBT
178de9f8578Sahoka 	nor_bbt_init(self);
179de9f8578Sahoka 	nor_bbt_scan(self);
180de9f8578Sahoka #endif
181de9f8578Sahoka 
182de9f8578Sahoka 	/*
183de9f8578Sahoka 	 * Attach all our devices
184de9f8578Sahoka 	 */
1852685996bSthorpej 	config_search(self, NULL,
186*c7fb772bSthorpej 	    CFARGS(.search = nor_search));
187de9f8578Sahoka 
188de9f8578Sahoka 	return;
189bf03074aScliff 
190de9f8578Sahoka error:
191bf03074aScliff #ifdef NOTET
192de9f8578Sahoka 	kmem_free(chip->nc_oob_cache, chip->nc_spare_size);
193bf03074aScliff #endif
194de9f8578Sahoka 	kmem_free(chip->nc_page_cache, chip->nc_page_size);
195de9f8578Sahoka 	mutex_destroy(&sc->sc_device_lock);
196de9f8578Sahoka }
197de9f8578Sahoka 
198de9f8578Sahoka static int
nor_search(device_t parent,cfdata_t cf,const int * ldesc,void * aux)199de9f8578Sahoka nor_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
200de9f8578Sahoka {
201bf03074aScliff 	struct nor_softc * const sc = device_private(parent);
202bf03074aScliff 	struct nor_chip * const chip = &sc->sc_chip;
203de9f8578Sahoka 	struct flash_attach_args faa;
204de9f8578Sahoka 
205bf03074aScliff 	faa.partinfo.part_offset = cf->cf_loc[FLASHBUSCF_OFFSET];
206de9f8578Sahoka 
207de9f8578Sahoka 	if (cf->cf_loc[FLASHBUSCF_SIZE] == 0) {
208bf03074aScliff 		faa.partinfo.part_size =
209bf03074aScliff 		    chip->nc_size - faa.partinfo.part_offset;
210de9f8578Sahoka 	} else {
211bf03074aScliff 		faa.partinfo.part_size = cf->cf_loc[FLASHBUSCF_SIZE];
212de9f8578Sahoka 	}
213de9f8578Sahoka 
214de9f8578Sahoka 	if (cf->cf_loc[FLASHBUSCF_READONLY])
215bf03074aScliff 		faa.partinfo.part_flags = FLASH_PART_READONLY;
216de9f8578Sahoka 	else
217bf03074aScliff 		faa.partinfo.part_flags = 0;
218de9f8578Sahoka 
219bf03074aScliff 	faa.flash_if = &sc->sc_flash_if;
220de9f8578Sahoka 
2212685996bSthorpej 	if (config_probe(parent, cf, &faa)) {
2222685996bSthorpej 		if (config_attach(parent, cf, &faa, nor_print,
223*c7fb772bSthorpej 				  CFARGS_NONE) != NULL) {
224de9f8578Sahoka 			return 0;
225de9f8578Sahoka 		} else {
226de9f8578Sahoka 			return 1;
227de9f8578Sahoka 		}
228de9f8578Sahoka 	}
229de9f8578Sahoka 
230de9f8578Sahoka 	return 1;
231de9f8578Sahoka }
232de9f8578Sahoka 
233bf03074aScliff static int
nor_detach(device_t self,int flags)234de9f8578Sahoka nor_detach(device_t self, int flags)
235de9f8578Sahoka {
236bf03074aScliff 	struct nor_softc * const sc = device_private(self);
237bf03074aScliff 	struct nor_chip * const chip = &sc->sc_chip;
238de9f8578Sahoka 	int error = 0;
239de9f8578Sahoka 
240de9f8578Sahoka 	error = config_detach_children(self, flags);
241de9f8578Sahoka 	if (error) {
242de9f8578Sahoka 		return error;
243de9f8578Sahoka 	}
244de9f8578Sahoka 
245bf03074aScliff 	flash_sync_thread_destroy(&sc->sc_flash_io);
246de9f8578Sahoka #ifdef NOR_BBT
247de9f8578Sahoka 	nor_bbt_detach(self);
248de9f8578Sahoka #endif
249bf03074aScliff #ifdef NOTET
250de9f8578Sahoka 	/* free oob cache */
251de9f8578Sahoka 	kmem_free(chip->nc_oob_cache, chip->nc_spare_size);
252bf03074aScliff #endif
253de9f8578Sahoka 	kmem_free(chip->nc_page_cache, chip->nc_page_size);
254de9f8578Sahoka 
255de9f8578Sahoka 	mutex_destroy(&sc->sc_device_lock);
256de9f8578Sahoka 
257de9f8578Sahoka 	pmf_device_deregister(sc->sc_dev);
258de9f8578Sahoka 
259de9f8578Sahoka 	return error;
260de9f8578Sahoka }
261de9f8578Sahoka 
262bf03074aScliff static int
nor_print(void * aux,const char * pnp)263de9f8578Sahoka nor_print(void *aux, const char *pnp)
264de9f8578Sahoka {
265de9f8578Sahoka 	if (pnp != NULL)
266de9f8578Sahoka 		aprint_normal("nor at %s\n", pnp);
267de9f8578Sahoka 
268de9f8578Sahoka 	return UNCONF;
269de9f8578Sahoka }
270de9f8578Sahoka 
271de9f8578Sahoka /* ask for a nor driver to attach to the controller */
272de9f8578Sahoka device_t
nor_attach_mi(struct nor_interface * const nor_if,device_t parent)273bf03074aScliff nor_attach_mi(struct nor_interface * const nor_if, device_t parent)
274de9f8578Sahoka {
275de9f8578Sahoka 	struct nor_attach_args arg;
276de9f8578Sahoka 
277de9f8578Sahoka 	KASSERT(nor_if != NULL);
278de9f8578Sahoka 
279bf03074aScliff 	if (nor_if->select == NULL)
280bf03074aScliff 		nor_if->select = &nor_default_select;
281bf03074aScliff 	if (nor_if->read_page == NULL)
282bf03074aScliff 		nor_if->read_page = &nor_default_read_page;
283bf03074aScliff 	if (nor_if->program_page == NULL)
284bf03074aScliff 		nor_if->program_page = &nor_default_program_page;
285bf03074aScliff 
286de9f8578Sahoka 	arg.naa_nor_if = nor_if;
287bf03074aScliff 
2882685996bSthorpej 	device_t dev = config_found(parent, &arg, nor_print,
289*c7fb772bSthorpej 	    CFARGS(.iattr = "norbus"));
290bf03074aScliff 
291bf03074aScliff 	return dev;
292de9f8578Sahoka }
293de9f8578Sahoka 
294bf03074aScliff static void
nor_default_select(device_t self,bool n)295bf03074aScliff nor_default_select(device_t self, bool n)
296bf03074aScliff {
297bf03074aScliff 	/* do nothing */
298bf03074aScliff 	return;
299bf03074aScliff }
300bf03074aScliff 
301bf03074aScliff static int
nor_flash_submit(device_t self,buf_t * const bp)302bf03074aScliff nor_flash_submit(device_t self, buf_t * const bp)
303bf03074aScliff {
304bf03074aScliff 	struct nor_softc * const sc = device_private(self);
305bf03074aScliff 
306bf03074aScliff 	return flash_io_submit(&sc->sc_flash_io, bp);
307bf03074aScliff }
308bf03074aScliff 
309bf03074aScliff 
310de9f8578Sahoka /* default everything to reasonable values, to ease future api changes */
311de9f8578Sahoka void
nor_init_interface(struct nor_interface * const nor_if)312bf03074aScliff nor_init_interface(struct nor_interface * const nor_if)
313de9f8578Sahoka {
314bf03074aScliff 	nor_if->select = &nor_default_select;
315bf03074aScliff 	nor_if->read_1 = NULL;
316bf03074aScliff 	nor_if->read_2 = NULL;
317bf03074aScliff 	nor_if->read_4 = NULL;
318bf03074aScliff 	nor_if->read_buf_1 = NULL;
319bf03074aScliff 	nor_if->read_buf_2 = NULL;
320bf03074aScliff 	nor_if->read_buf_4 = NULL;
321bf03074aScliff 	nor_if->write_1 = NULL;
322bf03074aScliff 	nor_if->write_2 = NULL;
323bf03074aScliff 	nor_if->write_4 = NULL;
324bf03074aScliff 	nor_if->write_buf_1 = NULL;
325bf03074aScliff 	nor_if->write_buf_2 = NULL;
326bf03074aScliff 	nor_if->write_buf_4 = NULL;
327bf03074aScliff 	nor_if->busy = NULL;
328de9f8578Sahoka }
329de9f8578Sahoka 
330bf03074aScliff #ifdef NOTYET
331de9f8578Sahoka /* handle quirks here */
332de9f8578Sahoka static void
nor_quirks(device_t self,struct nor_chip * const chip)333bf03074aScliff nor_quirks(device_t self, struct nor_chip * const chip)
334de9f8578Sahoka {
335de9f8578Sahoka 	/* this is an example only! */
336de9f8578Sahoka 	switch (chip->nc_manf_id) {
337de9f8578Sahoka 	case NOR_MFR_SAMSUNG:
338de9f8578Sahoka 		if (chip->nc_dev_id == 0x00) {
339de9f8578Sahoka 			/* do something only samsung chips need */
340de9f8578Sahoka 			/* or */
341de9f8578Sahoka 			/* chip->nc_quirks |= NC_QUIRK_NO_READ_START */
342de9f8578Sahoka 		}
343de9f8578Sahoka 	}
344de9f8578Sahoka 
345de9f8578Sahoka 	return;
346de9f8578Sahoka }
347de9f8578Sahoka #endif
348de9f8578Sahoka 
349de9f8578Sahoka /**
350de9f8578Sahoka  * scan media to determine the chip's properties
351de9f8578Sahoka  * this function resets the device
352de9f8578Sahoka  */
353de9f8578Sahoka static int
nor_scan_media(device_t self,struct nor_chip * const chip)354bf03074aScliff nor_scan_media(device_t self, struct nor_chip * const chip)
355de9f8578Sahoka {
356bf03074aScliff 	struct nor_softc * const sc = device_private(self);
357bf03074aScliff 	char pbuf[3][sizeof("XXXX MB")];
358de9f8578Sahoka 
359bf03074aScliff 	KASSERT(sc->sc_nor_if != NULL);
360bf03074aScliff 	KASSERT(sc->sc_nor_if->scan_media != NULL);
361bf03074aScliff 	int error = sc->sc_nor_if->scan_media(self, chip);
362bf03074aScliff 	if (error != 0)
363bf03074aScliff 		return error;
364de9f8578Sahoka 
365de9f8578Sahoka #ifdef NOR_VERBOSE
366de9f8578Sahoka 	aprint_normal_dev(self,
367bf03074aScliff 	    "manufacturer id: 0x%.4x (%s), device id: 0x%.4x\n",
368de9f8578Sahoka 	    chip->nc_manf_id,
369de9f8578Sahoka 	    nor_midtoname(chip->nc_manf_id),
370de9f8578Sahoka 	    chip->nc_dev_id);
371de9f8578Sahoka #endif
372de9f8578Sahoka 
373bf03074aScliff 	format_bytes(pbuf[0], sizeof(pbuf[0]), chip->nc_page_size);
374bf03074aScliff 	format_bytes(pbuf[1], sizeof(pbuf[1]), chip->nc_spare_size);
375bf03074aScliff 	format_bytes(pbuf[2], sizeof(pbuf[2]), chip->nc_block_size);
376de9f8578Sahoka 	aprint_normal_dev(self,
377bf03074aScliff 	    "page size: %s, spare size: %s, block size: %s\n",
378bf03074aScliff 	    pbuf[0], pbuf[1], pbuf[2]);
379de9f8578Sahoka 
380bf03074aScliff 	format_bytes(pbuf[0], sizeof(pbuf[0]), chip->nc_size);
381de9f8578Sahoka 	aprint_normal_dev(self,
382de9f8578Sahoka 	    "LUN size: %" PRIu32 " blocks, LUNs: %" PRIu8
383bf03074aScliff 	    ", total storage size: %s\n",
384bf03074aScliff 	    chip->nc_lun_blocks, chip->nc_num_luns, pbuf[0]);
385de9f8578Sahoka 
386bf03074aScliff #ifdef NOTYET
387de9f8578Sahoka 	/* XXX does this apply to nor? */
388de9f8578Sahoka 	/*
389de9f8578Sahoka 	 * calculate badblock marker offset in oob
390de9f8578Sahoka 	 * we try to be compatible with linux here
391de9f8578Sahoka 	 */
392de9f8578Sahoka 	if (chip->nc_page_size > 512)
393de9f8578Sahoka 		chip->nc_badmarker_offs = 0;
394de9f8578Sahoka 	else
395de9f8578Sahoka 		chip->nc_badmarker_offs = 5;
396bf03074aScliff #endif
397de9f8578Sahoka 
398de9f8578Sahoka 	/* Calculate page shift and mask */
399de9f8578Sahoka 	chip->nc_page_shift = ffs(chip->nc_page_size) - 1;
400de9f8578Sahoka 	chip->nc_page_mask = ~(chip->nc_page_size - 1);
401de9f8578Sahoka 	/* same for block */
402de9f8578Sahoka 	chip->nc_block_shift = ffs(chip->nc_block_size) - 1;
403de9f8578Sahoka 	chip->nc_block_mask = ~(chip->nc_block_size - 1);
404de9f8578Sahoka 
405bf03074aScliff #ifdef NOTYET
406de9f8578Sahoka 	/* look for quirks here if needed in future */
407bf03074aScliff 	nor_quirks(self, chip);
408bf03074aScliff #endif
409de9f8578Sahoka 
410de9f8578Sahoka 	return 0;
411de9f8578Sahoka }
412de9f8578Sahoka 
413de9f8578Sahoka /* ARGSUSED */
414bf03074aScliff static bool
nor_shutdown(device_t self,int howto)415de9f8578Sahoka nor_shutdown(device_t self, int howto)
416de9f8578Sahoka {
417de9f8578Sahoka 	return true;
418de9f8578Sahoka }
419de9f8578Sahoka 
420de9f8578Sahoka /* implementation of the block device API */
421de9f8578Sahoka 
422bf03074aScliff /* read a page, default implementation */
423bf03074aScliff static int
nor_default_read_page(device_t self,flash_off_t offset,uint8_t * const data)424bf03074aScliff nor_default_read_page(device_t self, flash_off_t offset, uint8_t * const data)
425de9f8578Sahoka {
426bf03074aScliff 	struct nor_softc * const sc = device_private(self);
427bf03074aScliff 	struct nor_chip * const chip = &sc->sc_chip;
428bf03074aScliff 
429bf03074aScliff 	/*
430bf03074aScliff 	 * access by specified access_width
431bf03074aScliff 	 * note: #bits == 1 << width
432bf03074aScliff 	 */
433bf03074aScliff 	switch(sc->sc_nor_if->access_width) {
434bf03074aScliff 	case 0:
435bf03074aScliff 		nor_read_buf_1(self, offset, data, chip->nc_page_size);
436bf03074aScliff 		break;
437bf03074aScliff 	case 1:
438bf03074aScliff 		nor_read_buf_2(self, offset, data, chip->nc_page_size);
439bf03074aScliff 		break;
440bf03074aScliff 	case 2:
441bf03074aScliff 		nor_read_buf_4(self, offset, data, chip->nc_page_size);
442bf03074aScliff 		break;
443bf03074aScliff #ifdef NOTYET
444bf03074aScliff 	case 3:
445bf03074aScliff 		nor_read_buf_8(self, offset, data, chip->nc_page_size);
446bf03074aScliff 		break;
447bf03074aScliff #endif
448bf03074aScliff 	default:
449bf03074aScliff 		panic("%s: bad width %d\n", __func__, sc->sc_nor_if->access_width);
450bf03074aScliff 	}
451bf03074aScliff 
452bf03074aScliff #if 0
453bf03074aScliff 	/* for debugging new drivers */
454bf03074aScliff 	nor_dump_data("page", data, chip->nc_page_size);
455bf03074aScliff #endif
456bf03074aScliff 
457de9f8578Sahoka 	return 0;
458de9f8578Sahoka }
459de9f8578Sahoka 
460bf03074aScliff /* write a page, default implementation */
461bf03074aScliff static int
nor_default_program_page(device_t self,flash_off_t offset,const uint8_t * const data)462bf03074aScliff nor_default_program_page(device_t self, flash_off_t offset,
463bf03074aScliff     const uint8_t * const data)
464de9f8578Sahoka {
465bf03074aScliff 	struct nor_softc * const sc = device_private(self);
466bf03074aScliff 	struct nor_chip * const chip = &sc->sc_chip;
467bf03074aScliff 
468bf03074aScliff 	/*
469bf03074aScliff 	 * access by specified width
470bf03074aScliff 	 * #bits == 1 << access_width
471bf03074aScliff 	 */
472bf03074aScliff 	switch(sc->sc_nor_if->access_width) {
473bf03074aScliff 	case 0:
474bf03074aScliff 		nor_write_buf_1(self, offset, data, chip->nc_page_size);
475bf03074aScliff 		break;
476bf03074aScliff 	case 1:
477bf03074aScliff 		nor_write_buf_2(self, offset, data, chip->nc_page_size);
478bf03074aScliff 		break;
479bf03074aScliff 	case 2:
480bf03074aScliff 		nor_write_buf_4(self, offset, data, chip->nc_page_size);
481bf03074aScliff 		break;
482bf03074aScliff #ifdef NOTYET
483bf03074aScliff 	case 3:
484bf03074aScliff 		nor_write_buf_8(self, offset, data, chip->nc_page_size);
485bf03074aScliff 		break;
486bf03074aScliff #endif
487bf03074aScliff 	default:
488bf03074aScliff 		panic("%s: bad width %d\n", __func__,
489bf03074aScliff 			sc->sc_nor_if->access_width);
490bf03074aScliff 	}
491bf03074aScliff 
492bf03074aScliff #if 0
493bf03074aScliff 	/* for debugging new drivers */
494bf03074aScliff 	nor_dump_data("page", data, chip->nc_page_size);
495bf03074aScliff #endif
496bf03074aScliff 
497de9f8578Sahoka 	return 0;
498de9f8578Sahoka }
499de9f8578Sahoka 
500bf03074aScliff /*
501bf03074aScliff  * nor_flash_erase_all - erase the entire chip
502bf03074aScliff  *
503bf03074aScliff  * XXX a good way to brick your system
504bf03074aScliff  */
505bf03074aScliff static int
nor_flash_erase_all(device_t self)506bf03074aScliff nor_flash_erase_all(device_t self)
507de9f8578Sahoka {
508bf03074aScliff 	struct nor_softc * const sc = device_private(self);
509bf03074aScliff 	int error;
510bf03074aScliff 
511bf03074aScliff 	mutex_enter(&sc->sc_device_lock);
512bf03074aScliff 	error = nor_erase_all(self);
513bf03074aScliff 	mutex_exit(&sc->sc_device_lock);
514bf03074aScliff 
515bf03074aScliff 	return error;
516bf03074aScliff }
517bf03074aScliff 
518bf03074aScliff static int
nor_flash_erase(device_t self,struct flash_erase_instruction * const ei)519bf03074aScliff nor_flash_erase(device_t self, struct flash_erase_instruction * const ei)
520bf03074aScliff {
521bf03074aScliff 	struct nor_softc * const sc = device_private(self);
522bf03074aScliff 	struct nor_chip * const chip = &sc->sc_chip;
523bf03074aScliff 	flash_off_t addr;
524bf03074aScliff 	int error = 0;
525bf03074aScliff 
526bf03074aScliff 	if (ei->ei_addr < 0 || ei->ei_len < chip->nc_block_size)
527bf03074aScliff 		return EINVAL;
528bf03074aScliff 
529bf03074aScliff 	if (ei->ei_addr + ei->ei_len > chip->nc_size) {
530bf03074aScliff 		DPRINTF(("%s: erase address is past the end"
531bf03074aScliff 			" of the device\n", __func__));
532bf03074aScliff 		return EINVAL;
533bf03074aScliff 	}
534bf03074aScliff 
535bf03074aScliff 	if ((ei->ei_addr == 0) && (ei->ei_len == chip->nc_size)
536bf03074aScliff 	&&  (sc->sc_nor_if->erase_all != NULL)) {
537bf03074aScliff 		return nor_flash_erase_all(self);
538bf03074aScliff 	}
539bf03074aScliff 
540bf03074aScliff 	if (ei->ei_addr % chip->nc_block_size != 0) {
541bf03074aScliff 		aprint_error_dev(self,
542bf03074aScliff 		    "nor_flash_erase: ei_addr (%ju) is not"
543bf03074aScliff 		    " a multiple of block size (%ju)\n",
544bf03074aScliff 		    (uintmax_t)ei->ei_addr,
545bf03074aScliff 		    (uintmax_t)chip->nc_block_size);
546bf03074aScliff 		return EINVAL;
547bf03074aScliff 	}
548bf03074aScliff 
549bf03074aScliff 	if (ei->ei_len % chip->nc_block_size != 0) {
550bf03074aScliff 		aprint_error_dev(self,
551bf03074aScliff 		    "nor_flash_erase: ei_len (%ju) is not"
552bf03074aScliff 		    " a multiple of block size (%ju)",
553bf03074aScliff 		    (uintmax_t)ei->ei_len,
554bf03074aScliff 		    (uintmax_t)chip->nc_block_size);
555bf03074aScliff 		return EINVAL;
556bf03074aScliff 	}
557bf03074aScliff 
558bf03074aScliff 	mutex_enter(&sc->sc_device_lock);
559bf03074aScliff 	addr = ei->ei_addr;
560bf03074aScliff 	while (addr < ei->ei_addr + ei->ei_len) {
561bf03074aScliff #ifdef NOTYET
562bf03074aScliff 		if (nor_isbad(self, addr)) {
563bf03074aScliff 			aprint_error_dev(self, "bad block encountered\n");
564bf03074aScliff 			ei->ei_state = FLASH_ERASE_FAILED;
565bf03074aScliff 			error = EIO;
566bf03074aScliff 			goto out;
567bf03074aScliff 		}
568bf03074aScliff #endif
569bf03074aScliff 
570bf03074aScliff 		error = nor_erase_block(self, addr);
571bf03074aScliff 		if (error) {
572bf03074aScliff 			ei->ei_state = FLASH_ERASE_FAILED;
573bf03074aScliff 			goto out;
574bf03074aScliff 		}
575bf03074aScliff 
576bf03074aScliff 		addr += chip->nc_block_size;
577bf03074aScliff 	}
578bf03074aScliff 	mutex_exit(&sc->sc_device_lock);
579bf03074aScliff 
580bf03074aScliff 	ei->ei_state = FLASH_ERASE_DONE;
581bf03074aScliff 	if (ei->ei_callback != NULL) {
582bf03074aScliff 		ei->ei_callback(ei);
583bf03074aScliff 	}
584bf03074aScliff 
585bf03074aScliff 	return 0;
586bf03074aScliff out:
587bf03074aScliff 	mutex_exit(&sc->sc_device_lock);
588bf03074aScliff 
589bf03074aScliff 	return error;
590bf03074aScliff }
591bf03074aScliff 
592bf03074aScliff /*
593bf03074aScliff  * handle (page) unaligned write to nor
594bf03074aScliff  */
595bf03074aScliff static int
nor_flash_write_unaligned(device_t self,flash_off_t offset,size_t len,size_t * const retlen,const uint8_t * const buf)596bf03074aScliff nor_flash_write_unaligned(device_t self, flash_off_t offset, size_t len,
597bf03074aScliff     size_t * const retlen, const uint8_t * const buf)
598bf03074aScliff {
599bf03074aScliff 	struct nor_softc * const sc = device_private(self);
600bf03074aScliff 	struct nor_chip * const chip = &sc->sc_chip;
601bf03074aScliff 	flash_off_t first, last, firstoff;
602bf03074aScliff 	const uint8_t *bufp;
603bf03074aScliff 	flash_off_t addr;
604bf03074aScliff 	size_t left, count;
605bf03074aScliff 	int error = 0, i;
606bf03074aScliff 
607bf03074aScliff 	first = offset & chip->nc_page_mask;
608bf03074aScliff 	firstoff = offset & ~chip->nc_page_mask;
609bf03074aScliff 	/* XXX check if this should be len - 1 */
610bf03074aScliff 	last = (offset + len) & chip->nc_page_mask;
611bf03074aScliff 	count = last - first + 1;
612bf03074aScliff 
613bf03074aScliff 	addr = first;
614bf03074aScliff 	*retlen = 0;
615bf03074aScliff 
616bf03074aScliff 	mutex_enter(&sc->sc_device_lock);
617bf03074aScliff 	if (count == 1) {
618bf03074aScliff #ifdef NOTYET
619bf03074aScliff 		if (nor_isbad(self, addr)) {
620bf03074aScliff 			aprint_error_dev(self,
621bf03074aScliff 			    "nor_flash_write_unaligned: "
622bf03074aScliff 			    "bad block encountered\n");
623bf03074aScliff 			error = EIO;
624bf03074aScliff 			goto out;
625bf03074aScliff 		}
626bf03074aScliff #endif
627bf03074aScliff 
628bf03074aScliff 		error = nor_read_page(self, addr, chip->nc_page_cache);
629bf03074aScliff 		if (error) {
630bf03074aScliff 			goto out;
631bf03074aScliff 		}
632bf03074aScliff 
633bf03074aScliff 		memcpy(chip->nc_page_cache + firstoff, buf, len);
634bf03074aScliff 
635bf03074aScliff 		error = nor_program_page(self, addr, chip->nc_page_cache);
636bf03074aScliff 		if (error) {
637bf03074aScliff 			goto out;
638bf03074aScliff 		}
639bf03074aScliff 
640bf03074aScliff 		*retlen = len;
641bf03074aScliff 		goto out;
642bf03074aScliff 	}
643bf03074aScliff 
644bf03074aScliff 	bufp = buf;
645bf03074aScliff 	left = len;
646bf03074aScliff 
647bf03074aScliff 	for (i = 0; i < count && left != 0; i++) {
648bf03074aScliff #ifdef NOTYET
649bf03074aScliff 		if (nor_isbad(self, addr)) {
650bf03074aScliff 			aprint_error_dev(self,
651bf03074aScliff 			    "nor_flash_write_unaligned: "
652bf03074aScliff 			    "bad block encountered\n");
653bf03074aScliff 			error = EIO;
654bf03074aScliff 			goto out;
655bf03074aScliff 		}
656bf03074aScliff #endif
657bf03074aScliff 
658bf03074aScliff 		if (i == 0) {
659bf03074aScliff 			error = nor_read_page(self, addr, chip->nc_page_cache);
660bf03074aScliff 			if (error) {
661bf03074aScliff 				goto out;
662bf03074aScliff 			}
663bf03074aScliff 
664bf03074aScliff 			memcpy(chip->nc_page_cache + firstoff,
665bf03074aScliff 			    bufp, chip->nc_page_size - firstoff);
666bf03074aScliff 
667bf03074aScliff 			printf("write page: %s: %d\n", __FILE__, __LINE__);
668bf03074aScliff 			error = nor_program_page(self, addr,
669bf03074aScliff 				chip->nc_page_cache);
670bf03074aScliff 			if (error) {
671bf03074aScliff 				goto out;
672bf03074aScliff 			}
673bf03074aScliff 
674bf03074aScliff 			bufp += chip->nc_page_size - firstoff;
675bf03074aScliff 			left -= chip->nc_page_size - firstoff;
676bf03074aScliff 			*retlen += chip->nc_page_size - firstoff;
677bf03074aScliff 
678bf03074aScliff 		} else if (i == count - 1) {
679bf03074aScliff 			error = nor_read_page(self, addr, chip->nc_page_cache);
680bf03074aScliff 			if (error) {
681bf03074aScliff 				goto out;
682bf03074aScliff 			}
683bf03074aScliff 
684bf03074aScliff 			memcpy(chip->nc_page_cache, bufp, left);
685bf03074aScliff 
686bf03074aScliff 			error = nor_program_page(self, addr,
687bf03074aScliff 				chip->nc_page_cache);
688bf03074aScliff 			if (error) {
689bf03074aScliff 				goto out;
690bf03074aScliff 			}
691bf03074aScliff 
692bf03074aScliff 			*retlen += left;
693bf03074aScliff 			KASSERT(left < chip->nc_page_size);
694bf03074aScliff 
695bf03074aScliff 		} else {
696bf03074aScliff 			/* XXX debug */
697bf03074aScliff 			if (left > chip->nc_page_size) {
698bf03074aScliff 				printf("left: %zu, i: %d, count: %zu\n",
699bf03074aScliff 				    (size_t )left, i, count);
700bf03074aScliff 			}
701bf03074aScliff 			KASSERT(left > chip->nc_page_size);
702bf03074aScliff 
703bf03074aScliff 			error = nor_program_page(self, addr, bufp);
704bf03074aScliff 			if (error) {
705bf03074aScliff 				goto out;
706bf03074aScliff 			}
707bf03074aScliff 
708bf03074aScliff 			bufp += chip->nc_page_size;
709bf03074aScliff 			left -= chip->nc_page_size;
710bf03074aScliff 			*retlen += chip->nc_page_size;
711bf03074aScliff 		}
712bf03074aScliff 
713bf03074aScliff 		addr += chip->nc_page_size;
714bf03074aScliff 	}
715bf03074aScliff 
716bf03074aScliff 	KASSERT(*retlen == len);
717bf03074aScliff out:
718bf03074aScliff 	mutex_exit(&sc->sc_device_lock);
719bf03074aScliff 
720bf03074aScliff 	return error;
721bf03074aScliff }
722bf03074aScliff 
723bf03074aScliff static int
nor_flash_write(device_t self,flash_off_t offset,size_t len,size_t * const retlen,const uint8_t * const buf)724bf03074aScliff nor_flash_write(device_t self, flash_off_t offset, size_t len,
725bf03074aScliff     size_t * const retlen, const uint8_t * const buf)
726bf03074aScliff {
727bf03074aScliff 	struct nor_softc * const sc = device_private(self);
728bf03074aScliff 	struct nor_chip * const chip = &sc->sc_chip;
729bf03074aScliff 	const uint8_t *bufp;
730bf03074aScliff 	size_t pages, page;
731bf03074aScliff 	daddr_t addr;
732bf03074aScliff 	int error = 0;
733bf03074aScliff 
734bf03074aScliff 	if ((offset + len) > chip->nc_size) {
735bf03074aScliff 		DPRINTF(("%s: write (off: 0x%jx, len: %ju),"
736bf03074aScliff 			" exceeds device size (0x%jx)\n", __func__,
737bf03074aScliff 			(uintmax_t)offset, (uintmax_t)len,
738bf03074aScliff 			(uintmax_t)chip->nc_size));
739bf03074aScliff 		return EINVAL;
740bf03074aScliff 	}
741bf03074aScliff 
742bf03074aScliff 	if (len % chip->nc_page_size != 0 ||
743bf03074aScliff 	    offset % chip->nc_page_size != 0) {
744bf03074aScliff 		return nor_flash_write_unaligned(self,
745bf03074aScliff 		    offset, len, retlen, buf);
746bf03074aScliff 	}
747bf03074aScliff 
748bf03074aScliff 	pages = len / chip->nc_page_size;
749bf03074aScliff 	KASSERT(pages != 0);
750bf03074aScliff 	*retlen = 0;
751bf03074aScliff 
752bf03074aScliff 	addr = offset;
753bf03074aScliff 	bufp = buf;
754bf03074aScliff 
755bf03074aScliff 	mutex_enter(&sc->sc_device_lock);
756bf03074aScliff 	for (page = 0; page < pages; page++) {
757bf03074aScliff #ifdef NOTYET
758bf03074aScliff 		/* do we need this check here? */
759bf03074aScliff 		if (nor_isbad(self, addr)) {
760bf03074aScliff 			aprint_error_dev(self,
761bf03074aScliff 			    "nor_flash_write: bad block encountered\n");
762bf03074aScliff 
763bf03074aScliff 			error = EIO;
764bf03074aScliff 			goto out;
765bf03074aScliff 		}
766bf03074aScliff #endif
767bf03074aScliff 
768bf03074aScliff 		error = nor_program_page(self, addr, bufp);
769bf03074aScliff 		if (error) {
770bf03074aScliff 			goto out;
771bf03074aScliff 		}
772bf03074aScliff 
773bf03074aScliff 		addr += chip->nc_page_size;
774bf03074aScliff 		bufp += chip->nc_page_size;
775bf03074aScliff 		*retlen += chip->nc_page_size;
776bf03074aScliff 	}
777bf03074aScliff out:
778bf03074aScliff 	mutex_exit(&sc->sc_device_lock);
779bf03074aScliff 	DPRINTF(("%s: retlen: %zu, len: %zu\n", __func__, *retlen, len));
780bf03074aScliff 
781bf03074aScliff 	return error;
782bf03074aScliff }
783bf03074aScliff 
784bf03074aScliff /*
785bf03074aScliff  * handle (page) unaligned read from nor
786bf03074aScliff  */
787bf03074aScliff static int
nor_flash_read_unaligned(device_t self,flash_off_t offset,size_t len,size_t * const retlen,uint8_t * const buf)788bf03074aScliff nor_flash_read_unaligned(device_t self, flash_off_t offset, size_t len,
789bf03074aScliff     size_t * const retlen, uint8_t * const buf)
790bf03074aScliff {
791bf03074aScliff 	struct nor_softc * const sc = device_private(self);
792bf03074aScliff 	struct nor_chip * const chip = &sc->sc_chip;
793bf03074aScliff 	daddr_t first, last, count, firstoff;
794bf03074aScliff 	uint8_t *bufp;
795bf03074aScliff 	daddr_t addr;
796bf03074aScliff 	size_t left;
797bf03074aScliff 	int error = 0, i;
798bf03074aScliff 
799bf03074aScliff 	first = offset & chip->nc_page_mask;
800bf03074aScliff 	firstoff = offset & ~chip->nc_page_mask;
801bf03074aScliff 	last = (offset + len) & chip->nc_page_mask;
802bf03074aScliff 	count = (last - first) / chip->nc_page_size + 1;
803bf03074aScliff 
804bf03074aScliff 	addr = first;
805bf03074aScliff 	bufp = buf;
806bf03074aScliff 	left = len;
807bf03074aScliff 	*retlen = 0;
808bf03074aScliff 
809bf03074aScliff 	mutex_enter(&sc->sc_device_lock);
810bf03074aScliff 	if (count == 1) {
811bf03074aScliff 		error = nor_read_page(self, addr, chip->nc_page_cache);
812bf03074aScliff 		if (error) {
813bf03074aScliff 			goto out;
814bf03074aScliff 		}
815bf03074aScliff 
816bf03074aScliff 		memcpy(bufp, chip->nc_page_cache + firstoff, len);
817bf03074aScliff 
818bf03074aScliff 		*retlen = len;
819bf03074aScliff 		goto out;
820bf03074aScliff 	}
821bf03074aScliff 
822bf03074aScliff 	for (i = 0; i < count && left != 0; i++) {
823bf03074aScliff 		/* XXX Why use the page cache here ? */
824bf03074aScliff 		error = nor_read_page(self, addr, chip->nc_page_cache);
825bf03074aScliff 		if (error) {
826bf03074aScliff 			goto out;
827bf03074aScliff 		}
828bf03074aScliff 
829bf03074aScliff 		if (i == 0) {
830bf03074aScliff 			memcpy(bufp, chip->nc_page_cache + firstoff,
831bf03074aScliff 			    chip->nc_page_size - firstoff);
832bf03074aScliff 
833bf03074aScliff 			bufp += chip->nc_page_size - firstoff;
834bf03074aScliff 			left -= chip->nc_page_size - firstoff;
835bf03074aScliff 			*retlen += chip->nc_page_size - firstoff;
836bf03074aScliff 
837bf03074aScliff 		} else if (i == count - 1) {
838bf03074aScliff 			memcpy(bufp, chip->nc_page_cache, left);
839bf03074aScliff 			*retlen += left;
840bf03074aScliff 			KASSERT(left < chip->nc_page_size);
841bf03074aScliff 
842bf03074aScliff 		} else {
843bf03074aScliff 			memcpy(bufp, chip->nc_page_cache, chip->nc_page_size);
844bf03074aScliff 
845bf03074aScliff 			bufp += chip->nc_page_size;
846bf03074aScliff 			left -= chip->nc_page_size;
847bf03074aScliff 			*retlen += chip->nc_page_size;
848bf03074aScliff 		}
849bf03074aScliff 
850bf03074aScliff 		addr += chip->nc_page_size;
851bf03074aScliff 	}
852bf03074aScliff 	KASSERT(*retlen == len);
853bf03074aScliff out:
854bf03074aScliff 	mutex_exit(&sc->sc_device_lock);
855bf03074aScliff 
856bf03074aScliff 	return error;
857bf03074aScliff }
858bf03074aScliff 
859bf03074aScliff static int
nor_flash_read(device_t self,flash_off_t offset,size_t len,size_t * const retlen,uint8_t * const buf)860bf03074aScliff nor_flash_read(device_t self, flash_off_t offset, size_t len,
861bf03074aScliff     size_t * const retlen, uint8_t * const buf)
862bf03074aScliff {
863bf03074aScliff 	struct nor_softc * const sc = device_private(self);
864bf03074aScliff 	struct nor_chip * const chip = &sc->sc_chip;
865bf03074aScliff 	uint8_t *bufp;
866bf03074aScliff 	size_t addr;
867bf03074aScliff 	size_t i, pages;
868bf03074aScliff 	int error = 0;
869bf03074aScliff 
870bf03074aScliff 	*retlen = 0;
871bf03074aScliff 
872bf03074aScliff 	DPRINTF(("%s: off: 0x%jx, len: %zu\n",
873bf03074aScliff 		__func__, (uintmax_t)offset, len));
874bf03074aScliff 
875bf03074aScliff 	if (__predict_false((offset + len) > chip->nc_size)) {
876bf03074aScliff 		DPRINTF(("%s: read (off: 0x%jx, len: %zu),"
877bf03074aScliff 			" exceeds device size (%ju)\n", __func__,
878bf03074aScliff 			(uintmax_t)offset, len, (uintmax_t)chip->nc_size));
879bf03074aScliff 		return EINVAL;
880bf03074aScliff 	}
881bf03074aScliff 
882bf03074aScliff 	/* Handle unaligned access, shouldnt be needed when using the
883bf03074aScliff 	 * block device, as strategy handles it, so only low level
884bf03074aScliff 	 * accesses will use this path
885bf03074aScliff 	 */
886bf03074aScliff 	/* XXX^2 */
887bf03074aScliff #if 0
888bf03074aScliff 	if (len < chip->nc_page_size)
889bf03074aScliff 		panic("TODO page size is larger than read size");
890bf03074aScliff #endif
891bf03074aScliff 
892bf03074aScliff 	if (len % chip->nc_page_size != 0 ||
893bf03074aScliff 	    offset % chip->nc_page_size != 0) {
894bf03074aScliff 		return nor_flash_read_unaligned(self,
895bf03074aScliff 		    offset, len, retlen, buf);
896bf03074aScliff 	}
897bf03074aScliff 
898bf03074aScliff 	bufp = buf;
899bf03074aScliff 	addr = offset;
900bf03074aScliff 	pages = len / chip->nc_page_size;
901bf03074aScliff 
902bf03074aScliff 	mutex_enter(&sc->sc_device_lock);
903bf03074aScliff 	for (i = 0; i < pages; i++) {
904bf03074aScliff #ifdef NOTYET
905bf03074aScliff 		/* XXX do we need this check here? */
906bf03074aScliff 		if (nor_isbad(self, addr)) {
907bf03074aScliff 			aprint_error_dev(self, "bad block encountered\n");
908bf03074aScliff 			error = EIO;
909bf03074aScliff 			goto out;
910bf03074aScliff 		}
911bf03074aScliff #endif
912bf03074aScliff 		error = nor_read_page(self, addr, bufp);
913bf03074aScliff 		if (error)
914bf03074aScliff 			goto out;
915bf03074aScliff 
916bf03074aScliff 		bufp += chip->nc_page_size;
917bf03074aScliff 		addr += chip->nc_page_size;
918bf03074aScliff 		*retlen += chip->nc_page_size;
919bf03074aScliff 	}
920bf03074aScliff out:
921bf03074aScliff 	mutex_exit(&sc->sc_device_lock);
922bf03074aScliff 
923bf03074aScliff 	return error;
924bf03074aScliff }
925bf03074aScliff 
926bf03074aScliff static int
nor_flash_isbad(device_t self,flash_off_t ofs,bool * const isbad)927bf03074aScliff nor_flash_isbad(device_t self, flash_off_t ofs, bool * const isbad)
928bf03074aScliff {
929bf03074aScliff 	struct nor_softc * const sc = device_private(self);
930bf03074aScliff 	struct nor_chip * const chip = &sc->sc_chip;
931bf03074aScliff #ifdef NOTYET
932bf03074aScliff 	bool result;
933bf03074aScliff #endif
934de9f8578Sahoka 
935de9f8578Sahoka 	if (ofs > chip->nc_size) {
936bf03074aScliff 		DPRINTF(("%s: offset 0x%jx is larger than"
937bf03074aScliff 			" device size (0x%jx)\n", __func__,
938bf03074aScliff 			(uintmax_t)ofs, (uintmax_t)chip->nc_size));
939de9f8578Sahoka 		return EINVAL;
940de9f8578Sahoka 	}
941de9f8578Sahoka 
942de9f8578Sahoka 	if (ofs % chip->nc_block_size != 0) {
943de9f8578Sahoka 		DPRINTF(("offset (0x%jx) is not the multiple of block size "
944de9f8578Sahoka 			"(%ju)",
945de9f8578Sahoka 			(uintmax_t)ofs, (uintmax_t)chip->nc_block_size));
946de9f8578Sahoka 		return EINVAL;
947de9f8578Sahoka 	}
948de9f8578Sahoka 
949bf03074aScliff #ifdef NOTYET
950de9f8578Sahoka 	mutex_enter(&sc->sc_device_lock);
951bf03074aScliff 	result = nor_isbad(self, ofs);
952de9f8578Sahoka 	mutex_exit(&sc->sc_device_lock);
953de9f8578Sahoka 
954bf03074aScliff 	*isbad = result;
955bf03074aScliff #else
956de9f8578Sahoka 	*isbad = false;
957bf03074aScliff #endif
958de9f8578Sahoka 
959de9f8578Sahoka 	return 0;
960de9f8578Sahoka }
961de9f8578Sahoka 
962bf03074aScliff static int
nor_flash_markbad(device_t self,flash_off_t ofs)963de9f8578Sahoka nor_flash_markbad(device_t self, flash_off_t ofs)
964de9f8578Sahoka {
965bf03074aScliff 	struct nor_softc * const sc = device_private(self);
966bf03074aScliff 	struct nor_chip * const chip = &sc->sc_chip;
967de9f8578Sahoka 
968de9f8578Sahoka 	if (ofs > chip->nc_size) {
969bf03074aScliff 		DPRINTF(("%s: offset 0x%jx is larger than"
970bf03074aScliff 			" device size (0x%jx)\n", __func__,
971bf03074aScliff 			ofs, (uintmax_t)chip->nc_size));
972de9f8578Sahoka 		return EINVAL;
973de9f8578Sahoka 	}
974de9f8578Sahoka 
975de9f8578Sahoka 	if (ofs % chip->nc_block_size != 0) {
976de9f8578Sahoka 		panic("offset (%ju) is not the multiple of block size (%ju)",
977de9f8578Sahoka 		    (uintmax_t)ofs, (uintmax_t)chip->nc_block_size);
978de9f8578Sahoka 	}
979de9f8578Sahoka 
980de9f8578Sahoka 	/* TODO: implement this */
981de9f8578Sahoka 
982de9f8578Sahoka 	return 0;
983de9f8578Sahoka }
984de9f8578Sahoka 
985de9f8578Sahoka static int
sysctl_nor_verify(SYSCTLFN_ARGS)986de9f8578Sahoka sysctl_nor_verify(SYSCTLFN_ARGS)
987de9f8578Sahoka {
988de9f8578Sahoka 	int error, t;
989de9f8578Sahoka 	struct sysctlnode node;
990de9f8578Sahoka 
991de9f8578Sahoka 	node = *rnode;
992de9f8578Sahoka 	t = *(int *)rnode->sysctl_data;
993de9f8578Sahoka 	node.sysctl_data = &t;
994de9f8578Sahoka 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
995de9f8578Sahoka 	if (error || newp == NULL)
996de9f8578Sahoka 		return error;
997de9f8578Sahoka 
998de9f8578Sahoka 	if (node.sysctl_num == nor_cachesync_nodenum) {
999de9f8578Sahoka 		if (t <= 0 || t > 60)
1000de9f8578Sahoka 			return EINVAL;
1001de9f8578Sahoka 	} else {
1002de9f8578Sahoka 		return EINVAL;
1003de9f8578Sahoka 	}
1004de9f8578Sahoka 
1005de9f8578Sahoka 	*(int *)rnode->sysctl_data = t;
1006de9f8578Sahoka 
1007de9f8578Sahoka 	return 0;
1008de9f8578Sahoka }
1009de9f8578Sahoka 
1010de9f8578Sahoka SYSCTL_SETUP(sysctl_nor, "sysctl nor subtree setup")
1011de9f8578Sahoka {
1012de9f8578Sahoka 	int rc, nor_root_num;
1013de9f8578Sahoka 	const struct sysctlnode *node;
1014de9f8578Sahoka 
1015de9f8578Sahoka 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
1016de9f8578Sahoka 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "nor",
1017de9f8578Sahoka 	    SYSCTL_DESCR("NOR driver controls"),
1018de9f8578Sahoka 	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) {
1019de9f8578Sahoka 		goto error;
1020de9f8578Sahoka 	}
1021de9f8578Sahoka 
1022de9f8578Sahoka 	nor_root_num = node->sysctl_num;
1023de9f8578Sahoka 
1024de9f8578Sahoka 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
1025de9f8578Sahoka 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
1026de9f8578Sahoka 	    CTLTYPE_INT, "cache_sync_timeout",
1027de9f8578Sahoka 	    SYSCTL_DESCR("NOR write cache sync timeout in seconds"),
1028de9f8578Sahoka 	    sysctl_nor_verify, 0, &nor_cachesync_timeout,
1029de9f8578Sahoka 	    0, CTL_HW, nor_root_num, CTL_CREATE,
1030de9f8578Sahoka 	    CTL_EOL)) != 0) {
1031de9f8578Sahoka 		goto error;
1032de9f8578Sahoka 	}
1033de9f8578Sahoka 
1034de9f8578Sahoka 	nor_cachesync_nodenum = node->sysctl_num;
1035de9f8578Sahoka 
1036de9f8578Sahoka 	return;
1037de9f8578Sahoka 
1038de9f8578Sahoka error:
1039de9f8578Sahoka 	aprint_error("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
1040de9f8578Sahoka }
1041de9f8578Sahoka 
1042de9f8578Sahoka MODULE(MODULE_CLASS_DRIVER, nor, "flash");
1043de9f8578Sahoka 
1044de9f8578Sahoka #ifdef _MODULE
1045de9f8578Sahoka #include "ioconf.c"
1046de9f8578Sahoka #endif
1047de9f8578Sahoka 
1048de9f8578Sahoka static int
nor_modcmd(modcmd_t cmd,void * opaque)1049de9f8578Sahoka nor_modcmd(modcmd_t cmd, void *opaque)
1050de9f8578Sahoka {
1051de9f8578Sahoka 	switch (cmd) {
1052de9f8578Sahoka 	case MODULE_CMD_INIT:
1053de9f8578Sahoka #ifdef _MODULE
1054de9f8578Sahoka 		return config_init_component(cfdriver_ioconf_nor,
1055de9f8578Sahoka 		    cfattach_ioconf_nor, cfdata_ioconf_nor);
1056de9f8578Sahoka #else
1057de9f8578Sahoka 		return 0;
1058de9f8578Sahoka #endif
1059de9f8578Sahoka 	case MODULE_CMD_FINI:
1060de9f8578Sahoka #ifdef _MODULE
1061de9f8578Sahoka 		return config_fini_component(cfdriver_ioconf_nor,
1062de9f8578Sahoka 		    cfattach_ioconf_nor, cfdata_ioconf_nor);
1063de9f8578Sahoka #else
1064de9f8578Sahoka 		return 0;
1065de9f8578Sahoka #endif
1066de9f8578Sahoka 	default:
1067de9f8578Sahoka 		return ENOTTY;
1068de9f8578Sahoka 	}
1069de9f8578Sahoka }
1070