xref: /netbsd-src/sys/dev/flash/flash.c (revision 1a144108bdbd63f081a4a11bc57f5ae957f91f15)
1*1a144108Sthorpej /*	$NetBSD: flash.c,v 1.19 2022/09/25 21:56:12 thorpej Exp $	*/
22b6ee221Sahoka 
32b6ee221Sahoka /*-
42b6ee221Sahoka  * Copyright (c) 2011 Department of Software Engineering,
52b6ee221Sahoka  *		      University of Szeged, Hungary
62b6ee221Sahoka  * Copyright (c) 2011 Adam Hoka <ahoka@NetBSD.org>
72b6ee221Sahoka  * Copyright (c) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
82b6ee221Sahoka  * All rights reserved.
92b6ee221Sahoka  *
102b6ee221Sahoka  * This code is derived from software contributed to The NetBSD Foundation
112b6ee221Sahoka  * by the Department of Software Engineering, University of Szeged, Hungary
122b6ee221Sahoka  *
132b6ee221Sahoka  * Redistribution and use in source and binary forms, with or without
142b6ee221Sahoka  * modification, are permitted provided that the following conditions
152b6ee221Sahoka  * are met:
162b6ee221Sahoka  * 1. Redistributions of source code must retain the above copyright
172b6ee221Sahoka  *    notice, this list of conditions and the following disclaimer.
182b6ee221Sahoka  * 2. Redistributions in binary form must reproduce the above copyright
192b6ee221Sahoka  *    notice, this list of conditions and the following disclaimer in the
202b6ee221Sahoka  *    documentation and/or other materials provided with the distribution.
212b6ee221Sahoka  *
222b6ee221Sahoka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
232b6ee221Sahoka  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
242b6ee221Sahoka  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
252b6ee221Sahoka  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
262b6ee221Sahoka  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
272b6ee221Sahoka  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
282b6ee221Sahoka  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
292b6ee221Sahoka  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
302b6ee221Sahoka  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
312b6ee221Sahoka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
322b6ee221Sahoka  * SUCH DAMAGE.
332b6ee221Sahoka  */
342b6ee221Sahoka 
352b6ee221Sahoka /*-
362b6ee221Sahoka  * Framework for storage devices based on Flash technology
372b6ee221Sahoka  */
382b6ee221Sahoka 
392b6ee221Sahoka #include <sys/cdefs.h>
40*1a144108Sthorpej __KERNEL_RCSID(0, "$NetBSD: flash.c,v 1.19 2022/09/25 21:56:12 thorpej Exp $");
412b6ee221Sahoka 
422b6ee221Sahoka #include <sys/param.h>
432b6ee221Sahoka #include <sys/types.h>
442b6ee221Sahoka #include <sys/proc.h>
452b6ee221Sahoka #include <sys/errno.h>
462b6ee221Sahoka #include <sys/ioctl.h>
472b6ee221Sahoka #include <sys/device.h>
482b6ee221Sahoka #include <sys/conf.h>
492b6ee221Sahoka #include <sys/kmem.h>
502b6ee221Sahoka #include <sys/uio.h>
512b6ee221Sahoka #include <sys/kernel.h>
522b6ee221Sahoka 
532b6ee221Sahoka #include <sys/atomic.h>
542b6ee221Sahoka #include <sys/buf.h>
552b6ee221Sahoka #include <sys/bufq.h>
562b6ee221Sahoka #include <sys/disk.h>
572b6ee221Sahoka #include <sys/disklabel.h>
582b6ee221Sahoka #include <sys/reboot.h>
592b6ee221Sahoka 
6082b8cabaSriastradh #include "ioconf.h"
6182b8cabaSriastradh 
622b6ee221Sahoka #include <sys/flashio.h>
632b6ee221Sahoka #include "flash.h"
642b6ee221Sahoka 
652b6ee221Sahoka #ifdef FLASH_DEBUG
662b6ee221Sahoka int flashdebug = FLASH_DEBUG;
672b6ee221Sahoka #endif
682b6ee221Sahoka 
692b6ee221Sahoka dev_type_open(flashopen);
702b6ee221Sahoka dev_type_close(flashclose);
712b6ee221Sahoka dev_type_read(flashread);
722b6ee221Sahoka dev_type_write(flashwrite);
732b6ee221Sahoka dev_type_ioctl(flashioctl);
742b6ee221Sahoka dev_type_strategy(flashstrategy);
752b6ee221Sahoka dev_type_dump(flashdump);
762b6ee221Sahoka 
772b6ee221Sahoka int flash_print(void *aux, const char *pnp);
782b6ee221Sahoka 
792b6ee221Sahoka bool flash_shutdown(device_t dev, int how);
802b6ee221Sahoka int flash_nsectors(struct buf *bp);
812b6ee221Sahoka int flash_sector(struct buf *bp);
822b6ee221Sahoka 
832b6ee221Sahoka int flash_match(device_t parent, cfdata_t match, void *aux);
842b6ee221Sahoka void flash_attach(device_t parent, device_t self, void *aux);
852b6ee221Sahoka int flash_detach(device_t device, int flags);
862b6ee221Sahoka 
872b6ee221Sahoka CFATTACH_DECL_NEW(flash, sizeof(struct flash_softc),
882b6ee221Sahoka     flash_match, flash_attach, flash_detach, NULL);
892b6ee221Sahoka 
902b6ee221Sahoka /**
912b6ee221Sahoka  * Block device's operation
922b6ee221Sahoka  */
932b6ee221Sahoka const struct bdevsw flash_bdevsw = {
942b6ee221Sahoka 	.d_open = flashopen,
952b6ee221Sahoka 	.d_close = flashclose,
962b6ee221Sahoka 	.d_strategy = flashstrategy,
972b6ee221Sahoka 	.d_ioctl = flashioctl,
982b6ee221Sahoka 	.d_dump = flashdump,
99a148a9e6Sahoka 	.d_psize = nosize,
1008c70ef39Sdholland 	.d_discard = nodiscard,	/* XXX this driver probably wants a discard */
1012b6ee221Sahoka 	.d_flag = D_DISK | D_MPSAFE
1022b6ee221Sahoka };
1032b6ee221Sahoka 
1042b6ee221Sahoka /**
1052b6ee221Sahoka  * Character device's operations
1062b6ee221Sahoka  */
1072b6ee221Sahoka const struct cdevsw flash_cdevsw = {
1082b6ee221Sahoka 	.d_open = flashopen,
1092b6ee221Sahoka 	.d_close = flashclose,
1102b6ee221Sahoka 	.d_read = flashread,
1112b6ee221Sahoka 	.d_write = flashwrite,
1122b6ee221Sahoka 	.d_ioctl = flashioctl,
1132b6ee221Sahoka 	.d_stop = nostop,
1142b6ee221Sahoka 	.d_tty = notty,
1152b6ee221Sahoka 	.d_poll = nopoll,
1162b6ee221Sahoka 	.d_mmap = nommap,
1172b6ee221Sahoka 	.d_kqfilter = nokqfilter,
118f9228f42Sdholland 	.d_discard = nodiscard,
1192b6ee221Sahoka 	.d_flag = D_DISK | D_MPSAFE
1202b6ee221Sahoka };
1212b6ee221Sahoka 
1222b6ee221Sahoka /* ARGSUSED */
1232b6ee221Sahoka int
flash_match(device_t parent,cfdata_t match,void * aux)1242b6ee221Sahoka flash_match(device_t parent, cfdata_t match, void *aux)
1252b6ee221Sahoka {
1262b6ee221Sahoka 	/* pseudo device, always attaches */
1272b6ee221Sahoka 	return 1;
1282b6ee221Sahoka }
1292b6ee221Sahoka 
1302b6ee221Sahoka /* ARGSUSED */
1312b6ee221Sahoka void
flash_attach(device_t parent,device_t self,void * aux)1322b6ee221Sahoka flash_attach(device_t parent, device_t self, void *aux)
1332b6ee221Sahoka {
134fb19d2b7Scliff 	struct flash_softc * const sc = device_private(self);
135fb19d2b7Scliff 	struct flash_attach_args * const faa = aux;
1362b6ee221Sahoka 	char pbuf[2][sizeof("9999 KB")];
1372b6ee221Sahoka 
1382b6ee221Sahoka 	sc->sc_dev = self;
1392b6ee221Sahoka 	sc->sc_parent_dev = parent;
1402b6ee221Sahoka 	sc->flash_if = faa->flash_if;
1410e8f635bSahoka 	sc->sc_partinfo = faa->partinfo;
1422b6ee221Sahoka 	sc->hw_softc = device_private(parent);
1432b6ee221Sahoka 
1440e8f635bSahoka 	format_bytes(pbuf[0], sizeof(pbuf[0]), sc->sc_partinfo.part_size);
1452b6ee221Sahoka 	format_bytes(pbuf[1], sizeof(pbuf[1]), sc->flash_if->erasesize);
1462b6ee221Sahoka 
1472b6ee221Sahoka 	aprint_naive("\n");
1482b6ee221Sahoka 
14940b9d3eeSjmcneill 	aprint_normal(": partition");
15040b9d3eeSjmcneill 	if (sc->sc_partinfo.part_name != NULL)
15140b9d3eeSjmcneill 		aprint_normal(" \"%s\"", sc->sc_partinfo.part_name);
1522b6ee221Sahoka 
15340b9d3eeSjmcneill 	aprint_normal(", size %s, offset %#jx",
154fb19d2b7Scliff 		pbuf[0], (uintmax_t)sc->sc_partinfo.part_offset);
1552b6ee221Sahoka 
1560e8f635bSahoka 	if (sc->sc_partinfo.part_flags & FLASH_PART_READONLY) {
1572b6ee221Sahoka 		sc->sc_readonly = true;
1582b6ee221Sahoka 		aprint_normal(", read only");
1592b6ee221Sahoka 	} else {
1602b6ee221Sahoka 		sc->sc_readonly = false;
1612b6ee221Sahoka 	}
1622b6ee221Sahoka 
1632b6ee221Sahoka 	aprint_normal("\n");
1642b6ee221Sahoka 
1650e8f635bSahoka 	if (sc->sc_partinfo.part_size == 0) {
1662b6ee221Sahoka 		aprint_error_dev(self,
1672b6ee221Sahoka 		    "partition size must be larger than 0\n");
1682b6ee221Sahoka 		return;
1692b6ee221Sahoka 	}
1702b6ee221Sahoka 
1712b6ee221Sahoka 	switch (sc->flash_if->type) {
1722b6ee221Sahoka 	case FLASH_TYPE_NOR:
1732b6ee221Sahoka 		aprint_normal_dev(sc->sc_dev,
1742b6ee221Sahoka 		    "erase size %s bytes, write size %d bytes\n",
1752b6ee221Sahoka 		    pbuf[1], sc->flash_if->writesize);
1762b6ee221Sahoka 		break;
1772b6ee221Sahoka 
1782b6ee221Sahoka 	case FLASH_TYPE_NAND:
1792b6ee221Sahoka 	default:
1802b6ee221Sahoka 		aprint_normal_dev(sc->sc_dev,
1812b6ee221Sahoka 		    "erase size %s, page size %d bytes, write size %d bytes\n",
1822b6ee221Sahoka 		    pbuf[1], sc->flash_if->page_size,
1832b6ee221Sahoka 		    sc->flash_if->writesize);
1842b6ee221Sahoka 		break;
1852b6ee221Sahoka 	}
1862b6ee221Sahoka 
1872b6ee221Sahoka 	if (!pmf_device_register1(sc->sc_dev, NULL, NULL, flash_shutdown))
1882b6ee221Sahoka 		aprint_error_dev(sc->sc_dev,
1892b6ee221Sahoka 		    "couldn't establish power handler\n");
1902b6ee221Sahoka }
1912b6ee221Sahoka 
1922b6ee221Sahoka int
flash_detach(device_t device,int flags)1932b6ee221Sahoka flash_detach(device_t device, int flags)
1942b6ee221Sahoka {
195fb19d2b7Scliff 	struct flash_softc * const sc = device_private(device);
1962b6ee221Sahoka 
1972b6ee221Sahoka 	pmf_device_deregister(sc->sc_dev);
1982b6ee221Sahoka 
1992b6ee221Sahoka 	/* freeing flash_if is our responsibility */
2002b6ee221Sahoka 	kmem_free(sc->flash_if, sizeof(*sc->flash_if));
2012b6ee221Sahoka 
2022b6ee221Sahoka 	return 0;
2032b6ee221Sahoka }
2042b6ee221Sahoka 
2052b6ee221Sahoka int
flash_print(void * aux,const char * pnp)2062b6ee221Sahoka flash_print(void *aux, const char *pnp)
2072b6ee221Sahoka {
2082b6ee221Sahoka 	struct flash_attach_args *arg;
2092b6ee221Sahoka 	const char *type;
2102b6ee221Sahoka 
2112b6ee221Sahoka 	if (pnp != NULL) {
2122b6ee221Sahoka 		arg = aux;
2132b6ee221Sahoka 		switch (arg->flash_if->type) {
2142b6ee221Sahoka 		case FLASH_TYPE_NOR:
2152b6ee221Sahoka 			type = "NOR";
2162b6ee221Sahoka 			break;
2172b6ee221Sahoka 		case FLASH_TYPE_NAND:
2182b6ee221Sahoka 			type = "NAND";
2192b6ee221Sahoka 			break;
2202b6ee221Sahoka 		default:
2212b6ee221Sahoka 			panic("flash_print: unknown type %d",
2222b6ee221Sahoka 			    arg->flash_if->type);
2232b6ee221Sahoka 		}
2242b6ee221Sahoka 		aprint_normal("%s flash at %s", type, pnp);
2252b6ee221Sahoka 	}
2262b6ee221Sahoka 	return UNCONF;
2272b6ee221Sahoka }
2282b6ee221Sahoka 
2292b6ee221Sahoka device_t
flash_attach_mi(struct flash_interface * const flash_if,device_t device)230fb19d2b7Scliff flash_attach_mi(struct flash_interface * const flash_if, device_t device)
2312b6ee221Sahoka {
2322b6ee221Sahoka 	struct flash_attach_args arg;
2332b6ee221Sahoka 
2342b6ee221Sahoka #ifdef DIAGNOSTIC
2352b6ee221Sahoka 	if (flash_if == NULL) {
2362b6ee221Sahoka 		aprint_error("flash_attach_mi: NULL\n");
2372b6ee221Sahoka 		return 0;
2382b6ee221Sahoka 	}
2392b6ee221Sahoka #endif
2402b6ee221Sahoka 	arg.flash_if = flash_if;
2412b6ee221Sahoka 
2422685996bSthorpej 	return config_found(device, &arg, flash_print,
243c7fb772bSthorpej 	    CFARGS(.iattr = "flashbus"));
2442b6ee221Sahoka }
2452b6ee221Sahoka 
2462b6ee221Sahoka /**
2472b6ee221Sahoka  * flash_open - open the character device
2482b6ee221Sahoka  * Checks if there is a driver registered to the minor number of the open
2492b6ee221Sahoka  * request.
2502b6ee221Sahoka  */
2512b6ee221Sahoka int
flashopen(dev_t dev,int flags,int fmt,lwp_t * l)252fb19d2b7Scliff flashopen(dev_t dev, int flags, int fmt, lwp_t *l)
2532b6ee221Sahoka {
2542b6ee221Sahoka 	int unit = minor(dev);
2552b6ee221Sahoka 	struct flash_softc *sc;
2562b6ee221Sahoka 
2570e8f635bSahoka 	FLDPRINTFN(1, ("flash: opening device unit %d\n", unit));
2582b6ee221Sahoka 
2592b6ee221Sahoka 	if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
2602b6ee221Sahoka 		return ENXIO;
2612b6ee221Sahoka 
2622b6ee221Sahoka 	/* TODO return eperm if want to open for writing a read only dev */
2632b6ee221Sahoka 
2642b6ee221Sahoka 	/* reset buffer length */
2652b6ee221Sahoka //	sc->sc_cache->fc_len = 0;
2662b6ee221Sahoka 
2672b6ee221Sahoka 	return 0;
2682b6ee221Sahoka }
2692b6ee221Sahoka 
2702b6ee221Sahoka /**
2712b6ee221Sahoka  * flash_close - close device
2722b6ee221Sahoka  * We don't have to release any resources, so just return 0.
2732b6ee221Sahoka  */
2742b6ee221Sahoka int
flashclose(dev_t dev,int flags,int fmt,lwp_t * l)275fb19d2b7Scliff flashclose(dev_t dev, int flags, int fmt, lwp_t *l)
2762b6ee221Sahoka {
2772b6ee221Sahoka 	int unit = minor(dev);
2782b6ee221Sahoka 	struct flash_softc *sc;
2792b6ee221Sahoka 	int err;
2802b6ee221Sahoka 
2810e8f635bSahoka 	FLDPRINTFN(1, ("flash: closing flash device unit %d\n", unit));
2822b6ee221Sahoka 
2832b6ee221Sahoka 	if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
2842b6ee221Sahoka 		return ENXIO;
2852b6ee221Sahoka 
2862b6ee221Sahoka 	if (!sc->sc_readonly) {
2872b6ee221Sahoka 		err = flash_sync(sc->sc_dev);
2882b6ee221Sahoka 		if (err)
2892b6ee221Sahoka 			return err;
2902b6ee221Sahoka 	}
2912b6ee221Sahoka 
2922b6ee221Sahoka 	return 0;
2932b6ee221Sahoka }
2942b6ee221Sahoka 
2952b6ee221Sahoka /**
2962b6ee221Sahoka  * flash_read - read from character device
297fb19d2b7Scliff  * This function uses the registered driver's read function to read the
298fb19d2b7Scliff  * requested length to * a buffer and then moves this buffer to userspace.
2992b6ee221Sahoka  */
3002b6ee221Sahoka int
flashread(dev_t dev,struct uio * const uio,int flag)301fb19d2b7Scliff flashread(dev_t dev, struct uio * const uio, int flag)
3022b6ee221Sahoka {
3032b6ee221Sahoka 	return physio(flashstrategy, NULL, dev, B_READ, minphys, uio);
3042b6ee221Sahoka }
3052b6ee221Sahoka 
3062b6ee221Sahoka /**
3072b6ee221Sahoka  * flash_write - write to character device
3082b6ee221Sahoka  * This function moves the data into a buffer from userspace to kernel space,
3092b6ee221Sahoka  * then uses the registered driver's write function to write out the data to
3102b6ee221Sahoka  * the media.
3112b6ee221Sahoka  */
3122b6ee221Sahoka int
flashwrite(dev_t dev,struct uio * const uio,int flag)313fb19d2b7Scliff flashwrite(dev_t dev, struct uio * const uio, int flag)
3142b6ee221Sahoka {
3152b6ee221Sahoka 	return physio(flashstrategy, NULL, dev, B_WRITE, minphys, uio);
3162b6ee221Sahoka }
3172b6ee221Sahoka 
3182b6ee221Sahoka void
flashstrategy(struct buf * const bp)319fb19d2b7Scliff flashstrategy(struct buf * const bp)
3202b6ee221Sahoka {
3212b6ee221Sahoka 	struct flash_softc *sc;
3222b6ee221Sahoka 	const struct flash_interface *flash_if;
3232b6ee221Sahoka 	const struct flash_partition *part;
3242b6ee221Sahoka 	int unit, device_blks;
3252b6ee221Sahoka 
3262b6ee221Sahoka 	unit = minor(bp->b_dev);
3272b6ee221Sahoka 	sc = device_lookup_private(&flash_cd, unit);
3282b6ee221Sahoka 	if (sc == NULL) {
3292b6ee221Sahoka 		bp->b_error = ENXIO;
3302b6ee221Sahoka 		goto done;
3312b6ee221Sahoka 	}
3322b6ee221Sahoka 
3332b6ee221Sahoka 	flash_if = sc->flash_if;
3340e8f635bSahoka 	part = &sc->sc_partinfo;
3352b6ee221Sahoka 
3362b6ee221Sahoka 	/* divider */
3372b6ee221Sahoka 	KASSERT(flash_if->writesize != 0);
3382b6ee221Sahoka 
3392b6ee221Sahoka 	aprint_debug_dev(sc->sc_dev, "flash_strategy()\n");
3402b6ee221Sahoka 
3412b6ee221Sahoka 	if (!(bp->b_flags & B_READ) && sc->sc_readonly) {
3422b6ee221Sahoka 		bp->b_error = EACCES;
3432b6ee221Sahoka 		goto done;
3442b6ee221Sahoka 	}
3452b6ee221Sahoka 
3462b6ee221Sahoka 	/* check if length is not negative */
3472b6ee221Sahoka 	if (bp->b_blkno < 0) {
3482b6ee221Sahoka 		bp->b_error = EINVAL;
3492b6ee221Sahoka 		goto done;
3502b6ee221Sahoka 	}
3512b6ee221Sahoka 
35240064e24Smsaitoh 	/* zero length i/o */
3532b6ee221Sahoka 	if (bp->b_bcount == 0) {
3542b6ee221Sahoka 		goto done;
3552b6ee221Sahoka 	}
3562b6ee221Sahoka 
3570e8f635bSahoka 	device_blks = sc->sc_partinfo.part_size / DEV_BSIZE;
3582b6ee221Sahoka 	KASSERT(part->part_offset % DEV_BSIZE == 0);
3592b6ee221Sahoka 	bp->b_rawblkno = bp->b_blkno + (part->part_offset / DEV_BSIZE);
3602b6ee221Sahoka 
3612b6ee221Sahoka 	if (bounds_check_with_mediasize(bp, DEV_BSIZE, device_blks) <= 0) {
3622b6ee221Sahoka 		goto done;
3632b6ee221Sahoka 	}
3642b6ee221Sahoka 
3652b6ee221Sahoka 	bp->b_resid = bp->b_bcount;
3662b6ee221Sahoka 	flash_if->submit(sc->sc_parent_dev, bp);
3672b6ee221Sahoka 
3682b6ee221Sahoka 	return;
3692b6ee221Sahoka done:
3702b6ee221Sahoka 	bp->b_resid = bp->b_bcount;
3712b6ee221Sahoka 	biodone(bp);
3722b6ee221Sahoka }
3732b6ee221Sahoka 
3742b6ee221Sahoka /*
3752b6ee221Sahoka  * Handle the ioctl for the device
3762b6ee221Sahoka  */
3772b6ee221Sahoka int
flashioctl(dev_t dev,u_long command,void * const data,int flags,lwp_t * l)378fb19d2b7Scliff flashioctl(dev_t dev, u_long command, void * const data, int flags, lwp_t *l)
3792b6ee221Sahoka {
3802b6ee221Sahoka 	struct flash_erase_params *ep;
3812b6ee221Sahoka 	struct flash_info_params *ip;
3822b6ee221Sahoka 	struct flash_dump_params *dp;
3832b6ee221Sahoka 	struct flash_badblock_params *bbp;
3842b6ee221Sahoka 	struct flash_erase_instruction ei;
3852b6ee221Sahoka 	struct flash_softc *sc;
3862b6ee221Sahoka 	int unit, err;
3872b6ee221Sahoka 	size_t retlen;
3886adb739dSahoka 	flash_off_t offset;
3896adb739dSahoka 	bool bad;
3902b6ee221Sahoka 
3912b6ee221Sahoka 	unit = minor(dev);
3922b6ee221Sahoka 	if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
3932b6ee221Sahoka 		return ENXIO;
3942b6ee221Sahoka 
3952b6ee221Sahoka 	err = 0;
3962b6ee221Sahoka 	switch (command) {
3972b6ee221Sahoka 	case FLASH_ERASE_BLOCK:
3982b6ee221Sahoka 		/**
3992b6ee221Sahoka 		 * Set up an erase instruction then call the registered
4002b6ee221Sahoka 		 * driver's erase operation.
4012b6ee221Sahoka 		 */
4022b6ee221Sahoka 		ep = data;
4032b6ee221Sahoka 
4042b6ee221Sahoka 		if (sc->sc_readonly) {
4052b6ee221Sahoka 			return EACCES;
4062b6ee221Sahoka 		}
4072b6ee221Sahoka 
4082b6ee221Sahoka 		ei.ei_addr = ep->ep_addr;
4092b6ee221Sahoka 		ei.ei_len = ep->ep_len;
4102b6ee221Sahoka 		ei.ei_callback = NULL;
4112b6ee221Sahoka 
4122b6ee221Sahoka 		err = flash_erase(sc->sc_dev, &ei);
4132b6ee221Sahoka 		if (err) {
4142b6ee221Sahoka 			return err;
4152b6ee221Sahoka 		}
4162b6ee221Sahoka 
4172b6ee221Sahoka 		break;
4182b6ee221Sahoka 	case FLASH_BLOCK_ISBAD:
4192b6ee221Sahoka 		/**
4202b6ee221Sahoka 		 * Set up an erase instruction then call the registered
4212b6ee221Sahoka 		 * driver's erase operation.
4222b6ee221Sahoka 		 */
4232b6ee221Sahoka 		bbp = data;
4242b6ee221Sahoka 
4256adb739dSahoka 		err = flash_block_isbad(sc->sc_dev, bbp->bbp_addr, &bad);
4266adb739dSahoka 		if (err) {
4272b6ee221Sahoka 			return err;
4282b6ee221Sahoka 		}
4296adb739dSahoka 		bbp->bbp_isbad = bad;
4302b6ee221Sahoka 
4312b6ee221Sahoka 		break;
4322b6ee221Sahoka 	case FLASH_BLOCK_MARKBAD:
4332b6ee221Sahoka 		bbp = data;
4342b6ee221Sahoka 
4352b6ee221Sahoka 		err = flash_block_markbad(sc->sc_dev, bbp->bbp_addr);
4362b6ee221Sahoka 
4372b6ee221Sahoka 		break;
4382b6ee221Sahoka 	case FLASH_DUMP:
4392b6ee221Sahoka 		dp = data;
4402b6ee221Sahoka 		offset = dp->dp_block * sc->flash_if->erasesize;
4410e8f635bSahoka 		FLDPRINTF(("Reading from block: %jd len: %jd\n",
4422b6ee221Sahoka 			(intmax_t )dp->dp_block, (intmax_t )dp->dp_len));
4432b6ee221Sahoka 		err = flash_read(sc->sc_parent_dev, offset, dp->dp_len,
4442b6ee221Sahoka 		    &retlen, dp->dp_buf);
4452b6ee221Sahoka 		if (err)
4462b6ee221Sahoka 			return err;
4472b6ee221Sahoka 		if (retlen != dp->dp_len) {
4482b6ee221Sahoka 			dp->dp_len = -1;
4492b6ee221Sahoka 			dp->dp_buf = NULL;
4502b6ee221Sahoka 		}
4512b6ee221Sahoka 
4522b6ee221Sahoka 		break;
4532b6ee221Sahoka 	case FLASH_GET_INFO:
4542b6ee221Sahoka 		ip = data;
4552b6ee221Sahoka 
4562b6ee221Sahoka 		ip->ip_page_size = sc->flash_if->page_size;
4572b6ee221Sahoka 		ip->ip_erase_size = sc->flash_if->erasesize;
4582b6ee221Sahoka 		ip->ip_flash_type = sc->flash_if->type;
4590e8f635bSahoka 		ip->ip_flash_size = sc->sc_partinfo.part_size;
4602b6ee221Sahoka 		break;
4612b6ee221Sahoka 	default:
4622b6ee221Sahoka 		err = ENODEV;
4632b6ee221Sahoka 	}
4642b6ee221Sahoka 
4652b6ee221Sahoka 	return err;
4662b6ee221Sahoka }
467e4215d8aSuebayasi 
4682b6ee221Sahoka int
flashdump(dev_t dev,daddr_t blkno,void * va,size_t size)4692b6ee221Sahoka flashdump(dev_t dev, daddr_t blkno, void *va, size_t size)
4702b6ee221Sahoka {
4712b6ee221Sahoka 	return EACCES;
4722b6ee221Sahoka }
4732b6ee221Sahoka 
4742b6ee221Sahoka bool
flash_shutdown(device_t self,int how)4752b6ee221Sahoka flash_shutdown(device_t self, int how)
4762b6ee221Sahoka {
477fb19d2b7Scliff 	struct flash_softc * const sc = device_private(self);
4782b6ee221Sahoka 
4792b6ee221Sahoka 	if ((how & RB_NOSYNC) == 0 && !sc->sc_readonly)
4802b6ee221Sahoka 		flash_sync(self);
4812b6ee221Sahoka 
4822b6ee221Sahoka 	return true;
4832b6ee221Sahoka }
4842b6ee221Sahoka 
4852b6ee221Sahoka const struct flash_interface *
flash_get_interface(dev_t dev)4862b6ee221Sahoka flash_get_interface(dev_t dev)
4872b6ee221Sahoka {
4882b6ee221Sahoka 	struct flash_softc *sc;
4892b6ee221Sahoka 	int unit;
4902b6ee221Sahoka 
4912b6ee221Sahoka 	unit = minor(dev);
4922b6ee221Sahoka 	if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
4932b6ee221Sahoka 		return NULL;
4942b6ee221Sahoka 
4952b6ee221Sahoka 	return sc->flash_if;
4962b6ee221Sahoka }
4972b6ee221Sahoka 
4982b6ee221Sahoka const struct flash_softc *
flash_get_softc(dev_t dev)4992b6ee221Sahoka flash_get_softc(dev_t dev)
5002b6ee221Sahoka {
5012b6ee221Sahoka 	struct flash_softc *sc;
5022b6ee221Sahoka 	int unit;
5032b6ee221Sahoka 
5042b6ee221Sahoka 	unit = minor(dev);
5052b6ee221Sahoka 	sc = device_lookup_private(&flash_cd, unit);
5062b6ee221Sahoka 
5072b6ee221Sahoka 	return sc;
5082b6ee221Sahoka }
5092b6ee221Sahoka 
5102b6ee221Sahoka device_t
flash_get_device(dev_t dev)5112b6ee221Sahoka flash_get_device(dev_t dev)
5122b6ee221Sahoka {
5132b6ee221Sahoka 	struct flash_softc *sc;
5142b6ee221Sahoka 	int unit;
5152b6ee221Sahoka 
5162b6ee221Sahoka 	unit = minor(dev);
5172b6ee221Sahoka 	sc = device_lookup_private(&flash_cd, unit);
5182b6ee221Sahoka 
5192b6ee221Sahoka 	return sc->sc_dev;
5202b6ee221Sahoka }
5212b6ee221Sahoka 
522ed5192ddSahoka flash_size_t
flash_get_size(dev_t dev)523ed5192ddSahoka flash_get_size(dev_t dev)
524ed5192ddSahoka {
525ed5192ddSahoka 	const struct flash_softc *sc;
526ed5192ddSahoka 
527ed5192ddSahoka 	sc = flash_get_softc(dev);
528ed5192ddSahoka 
529ed5192ddSahoka 	return sc->sc_partinfo.part_size;
530ed5192ddSahoka }
531ed5192ddSahoka 
5322b6ee221Sahoka int
flash_erase(device_t self,struct flash_erase_instruction * const ei)533fb19d2b7Scliff flash_erase(device_t self, struct flash_erase_instruction * const ei)
5342b6ee221Sahoka {
535fb19d2b7Scliff 	struct flash_softc * const sc = device_private(self);
5362b6ee221Sahoka 	KASSERT(ei != NULL);
5372b6ee221Sahoka 	struct flash_erase_instruction e = *ei;
5382b6ee221Sahoka 
5392b6ee221Sahoka 	if (sc->sc_readonly)
5402b6ee221Sahoka 		return EACCES;
5412b6ee221Sahoka 
5422b6ee221Sahoka 	/* adjust for flash partition */
5430e8f635bSahoka 	e.ei_addr += sc->sc_partinfo.part_offset;
5442b6ee221Sahoka 
5452b6ee221Sahoka 	/* bounds check for flash partition */
5460e8f635bSahoka 	if (e.ei_addr + e.ei_len > sc->sc_partinfo.part_size +
5470e8f635bSahoka 	    sc->sc_partinfo.part_offset)
5482b6ee221Sahoka 		return EINVAL;
5492b6ee221Sahoka 
5502b6ee221Sahoka 	return sc->flash_if->erase(device_parent(self), &e);
5512b6ee221Sahoka }
5522b6ee221Sahoka 
5532b6ee221Sahoka int
flash_read(device_t self,flash_off_t offset,size_t len,size_t * const retlen,uint8_t * const buf)554fb19d2b7Scliff flash_read(device_t self, flash_off_t offset, size_t len, size_t * const retlen,
555fb19d2b7Scliff     uint8_t * const buf)
5562b6ee221Sahoka {
557fb19d2b7Scliff 	struct flash_softc * const sc = device_private(self);
5582b6ee221Sahoka 
5590e8f635bSahoka 	offset += sc->sc_partinfo.part_offset;
5602b6ee221Sahoka 
5610e8f635bSahoka 	if (offset + len > sc->sc_partinfo.part_size +
5620e8f635bSahoka 	    sc->sc_partinfo.part_offset)
5632b6ee221Sahoka 		return EINVAL;
5642b6ee221Sahoka 
5652b6ee221Sahoka 	return sc->flash_if->read(device_parent(self),
5662b6ee221Sahoka 	    offset, len, retlen, buf);
5672b6ee221Sahoka }
5682b6ee221Sahoka 
5692b6ee221Sahoka int
flash_write(device_t self,flash_off_t offset,size_t len,size_t * const retlen,const uint8_t * const buf)570fb19d2b7Scliff flash_write(device_t self, flash_off_t offset, size_t len,
571fb19d2b7Scliff     size_t * const retlen, const uint8_t * const buf)
5722b6ee221Sahoka {
573fb19d2b7Scliff 	struct flash_softc * const sc = device_private(self);
5742b6ee221Sahoka 
5752b6ee221Sahoka 	if (sc->sc_readonly)
5762b6ee221Sahoka 		return EACCES;
5772b6ee221Sahoka 
5780e8f635bSahoka 	offset += sc->sc_partinfo.part_offset;
5792b6ee221Sahoka 
5800e8f635bSahoka 	if (offset + len > sc->sc_partinfo.part_size +
5810e8f635bSahoka 	    sc->sc_partinfo.part_offset)
5822b6ee221Sahoka 		return EINVAL;
5832b6ee221Sahoka 
5842b6ee221Sahoka 	return sc->flash_if->write(device_parent(self),
5852b6ee221Sahoka 	    offset, len, retlen, buf);
5862b6ee221Sahoka }
5872b6ee221Sahoka 
5882b6ee221Sahoka int
flash_block_markbad(device_t self,flash_off_t offset)5896adb739dSahoka flash_block_markbad(device_t self, flash_off_t offset)
5902b6ee221Sahoka {
591fb19d2b7Scliff 	struct flash_softc * const sc = device_private(self);
5922b6ee221Sahoka 
5932b6ee221Sahoka 	if (sc->sc_readonly)
5942b6ee221Sahoka 		return EACCES;
5952b6ee221Sahoka 
5960e8f635bSahoka 	offset += sc->sc_partinfo.part_offset;
5972b6ee221Sahoka 
5982b6ee221Sahoka 	if (offset + sc->flash_if->erasesize >=
5990e8f635bSahoka 	    sc->sc_partinfo.part_size +
6000e8f635bSahoka 	    sc->sc_partinfo.part_offset)
6012b6ee221Sahoka 		return EINVAL;
6022b6ee221Sahoka 
6032b6ee221Sahoka 	return sc->flash_if->block_markbad(device_parent(self), offset);
6042b6ee221Sahoka }
6052b6ee221Sahoka 
6062b6ee221Sahoka int
flash_block_isbad(device_t self,flash_off_t offset,bool * const bad)607fb19d2b7Scliff flash_block_isbad(device_t self, flash_off_t offset, bool * const bad)
6082b6ee221Sahoka {
609fb19d2b7Scliff 	struct flash_softc * const sc = device_private(self);
6102b6ee221Sahoka 
6110e8f635bSahoka 	offset += sc->sc_partinfo.part_offset;
6122b6ee221Sahoka 
6132b6ee221Sahoka 	if (offset + sc->flash_if->erasesize >
6140e8f635bSahoka 	    sc->sc_partinfo.part_size +
6150e8f635bSahoka 	    sc->sc_partinfo.part_offset)
6162b6ee221Sahoka 		return EINVAL;
6172b6ee221Sahoka 
6186adb739dSahoka 	return sc->flash_if->block_isbad(device_parent(self), offset, bad);
6192b6ee221Sahoka }
6202b6ee221Sahoka 
6212b6ee221Sahoka int
flash_sync(device_t self)6222b6ee221Sahoka flash_sync(device_t self)
6232b6ee221Sahoka {
624fb19d2b7Scliff 	struct flash_softc * const sc = device_private(self);
6252b6ee221Sahoka 
6262b6ee221Sahoka 	if (sc->sc_readonly)
6272b6ee221Sahoka 		return EACCES;
6282b6ee221Sahoka 
6292b6ee221Sahoka 	/* noop now TODO: implement */
6302b6ee221Sahoka 	return 0;
6312b6ee221Sahoka }
6322b6ee221Sahoka 
6332b6ee221Sahoka MODULE(MODULE_CLASS_DRIVER, flash, NULL);
6342b6ee221Sahoka 
6352b6ee221Sahoka #ifdef _MODULE
6362b6ee221Sahoka #include "ioconf.c"
6372b6ee221Sahoka #endif
6382b6ee221Sahoka 
6392b6ee221Sahoka static int
flash_modcmd(modcmd_t cmd,void * opaque)6402b6ee221Sahoka flash_modcmd(modcmd_t cmd, void *opaque)
6412b6ee221Sahoka {
6422b6ee221Sahoka 	int error = 0;
6432b6ee221Sahoka #ifdef _MODULE
6442b6ee221Sahoka 	int bmaj = -1, cmaj = -1;
6452b6ee221Sahoka #endif
6462b6ee221Sahoka 
6472b6ee221Sahoka 	switch (cmd) {
6482b6ee221Sahoka 	case MODULE_CMD_INIT:
6492b6ee221Sahoka #ifdef _MODULE
6502b6ee221Sahoka 		error = devsw_attach("flash", &flash_bdevsw, &bmaj,
6512b6ee221Sahoka 		    &flash_cdevsw, &cmaj);
6522b6ee221Sahoka 		if (error)
65397f8debdSpgoyette 			return error;
65497f8debdSpgoyette 		error = config_init_component(cfdriver_ioconf_flash,
6552b6ee221Sahoka 		    cfattach_ioconf_flash, cfdata_ioconf_flash);
65697f8debdSpgoyette 		if (error)
65797f8debdSpgoyette 			devsw_detach(&flash_bdevsw, &flash_cdevsw);
6582b6ee221Sahoka #endif
6592b6ee221Sahoka 		return error;
6602b6ee221Sahoka 	case MODULE_CMD_FINI:
6612b6ee221Sahoka #ifdef _MODULE
6622b6ee221Sahoka 		error = config_fini_component(cfdriver_ioconf_flash,
6632b6ee221Sahoka 		    cfattach_ioconf_flash, cfdata_ioconf_flash);
66497f8debdSpgoyette 		devsw_detach(&flash_bdevsw, &flash_cdevsw);
6652b6ee221Sahoka #endif
6662b6ee221Sahoka 		return error;
6672b6ee221Sahoka 	default:
6682b6ee221Sahoka 		return ENOTTY;
6692b6ee221Sahoka 	}
6702b6ee221Sahoka }
671