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