xref: /openbsd-src/sys/arch/octeon/dev/amdcf.c (revision a8b581972213782bec450017fe45a05ea431addf)
1*a8b58197Sjsg /*	$OpenBSD: amdcf.c,v 1.10 2024/05/20 23:13:33 jsg Exp $	*/
26920b493Spirofti 
36920b493Spirofti /*
46920b493Spirofti  * Copyright (c) 2007, Juniper Networks, Inc.
56920b493Spirofti  * All rights reserved.
66920b493Spirofti  *
76920b493Spirofti  * Redistribution and use in source and binary forms, with or without
86920b493Spirofti  * modification, are permitted provided that the following conditions
96920b493Spirofti  * are met:
106920b493Spirofti  * 1. Redistributions of source code must retain the above copyright
116920b493Spirofti  *    notice, this list of conditions and the following disclaimer.
126920b493Spirofti  * 2. Redistributions in binary form must reproduce the above copyright
136920b493Spirofti  *    notice, this list of conditions and the following disclaimer in the
146920b493Spirofti  *    documentation and/or other materials provided with the distribution.
156920b493Spirofti  * 3. Neither the name of the author nor the names of any co-contributors
166920b493Spirofti  *    may be used to endorse or promote products derived from this software
176920b493Spirofti  *    without specific prior written permission.
186920b493Spirofti  *
196920b493Spirofti  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
206920b493Spirofti  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
216920b493Spirofti  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
226920b493Spirofti  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
236920b493Spirofti  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
246920b493Spirofti  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
256920b493Spirofti  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
266920b493Spirofti  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
276920b493Spirofti  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
286920b493Spirofti  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
296920b493Spirofti  * SUCH DAMAGE.
306920b493Spirofti  */
316920b493Spirofti 
326920b493Spirofti /*
336920b493Spirofti  * Copyright (c) 2009 Sam Leffler, Errno Consulting
346920b493Spirofti  * All rights reserved.
356920b493Spirofti  *
366920b493Spirofti  * Redistribution and use in source and binary forms, with or without
376920b493Spirofti  * modification, are permitted provided that the following conditions
386920b493Spirofti  * are met:
396920b493Spirofti  * 1. Redistributions of source code must retain the above copyright
406920b493Spirofti  *    notice, this list of conditions and the following disclaimer.
416920b493Spirofti  * 2. Redistributions in binary form must reproduce the above copyright
426920b493Spirofti  *    notice, this list of conditions and the following disclaimer in the
436920b493Spirofti  *    documentation and/or other materials provided with the distribution.
446920b493Spirofti  *
456920b493Spirofti  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
466920b493Spirofti  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
476920b493Spirofti  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
486920b493Spirofti  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
496920b493Spirofti  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
506920b493Spirofti  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
516920b493Spirofti  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
526920b493Spirofti  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
536920b493Spirofti  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
546920b493Spirofti  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
556920b493Spirofti  */
566920b493Spirofti 
576920b493Spirofti /*
586920b493Spirofti  * Copyright (c) 2015 Paul Irofti.
596920b493Spirofti  *
606920b493Spirofti  * Permission to use, copy, modify, and distribute this software for any
616920b493Spirofti  * purpose with or without fee is hereby granted, provided that the above
626920b493Spirofti  * copyright notice and this permission notice appear in all copies.
636920b493Spirofti  *
646920b493Spirofti  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
656920b493Spirofti  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
666920b493Spirofti  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
676920b493Spirofti  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
686920b493Spirofti  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
696920b493Spirofti  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
706920b493Spirofti  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
716920b493Spirofti  */
726920b493Spirofti 
736920b493Spirofti 
746920b493Spirofti #include <sys/param.h>
756920b493Spirofti #include <sys/systm.h>
766920b493Spirofti #include <sys/kernel.h>
776920b493Spirofti #include <sys/conf.h>
78c0cd3489Sguenther #include <sys/fcntl.h>
796920b493Spirofti #include <sys/stat.h>
806920b493Spirofti #include <sys/ioctl.h>
816920b493Spirofti #include <sys/mutex.h>
826920b493Spirofti #include <sys/buf.h>
836920b493Spirofti #include <sys/uio.h>
846920b493Spirofti #include <sys/malloc.h>
856920b493Spirofti #include <sys/device.h>
866920b493Spirofti #include <sys/disklabel.h>
876920b493Spirofti #include <sys/disk.h>
886920b493Spirofti #include <sys/syslog.h>
896920b493Spirofti #include <sys/proc.h>
906920b493Spirofti #include <sys/vnode.h>
916920b493Spirofti #include <sys/dkio.h>
926920b493Spirofti 
936920b493Spirofti #include <machine/intr.h>
946920b493Spirofti #include <machine/bus.h>
956920b493Spirofti #include <machine/autoconf.h>
966920b493Spirofti 
976920b493Spirofti #include <octeon/dev/iobusvar.h>
986920b493Spirofti #include <machine/octeonreg.h>
996920b493Spirofti #include <machine/octeonvar.h>
1006920b493Spirofti 
1016920b493Spirofti 
1026920b493Spirofti #define CFI_QRY_CMD_ADDR	0x55
1036920b493Spirofti #define CFI_QRY_CMD_DATA	0x98
1046920b493Spirofti 
1056920b493Spirofti #define CFI_QRY_TTO_WRITE	0x1f
1066920b493Spirofti #define CFI_QRY_TTO_ERASE	0x21
1076920b493Spirofti #define CFI_QRY_MTO_WRITE	0x23
1086920b493Spirofti #define CFI_QRY_MTO_ERASE	0x25
1096920b493Spirofti 
1106920b493Spirofti #define CFI_QRY_SIZE		0x27
1116920b493Spirofti #define	CFI_QRY_NREGIONS	0x2c
1126920b493Spirofti #define CFI_QRY_REGION0 	0x31
1136920b493Spirofti #define CFI_QRY_REGION(x)	(CFI_QRY_REGION0 + (x) * 4)
1146920b493Spirofti 
1156920b493Spirofti #define CFI_BCS_READ_ARRAY	0xff
1166920b493Spirofti 
1176920b493Spirofti #define CFI_DISK_SECSIZE	512
1186920b493Spirofti #define CFI_DISK_MAXIOSIZE	65536
1196920b493Spirofti 
1206920b493Spirofti #define AMDCF_MAP_SIZE		0x02000000
1216920b493Spirofti 
1226920b493Spirofti #define CFI_AMD_BLOCK_ERASE	0x30
1236920b493Spirofti #define CFI_AMD_UNLOCK		0xaa
1246920b493Spirofti #define CFI_AMD_UNLOCK_ACK	0x55
1256920b493Spirofti #define CFI_AMD_PROGRAM		0xa0
1266920b493Spirofti #define CFI_AMD_RESET		0xf0
1276920b493Spirofti 
1286920b493Spirofti #define AMD_ADDR_START		0x555
1296920b493Spirofti #define AMD_ADDR_ACK		0x2aa
1306920b493Spirofti 
1316920b493Spirofti #define BOOTLOADER_ADDR		0xa0000
1326920b493Spirofti 
1336920b493Spirofti struct cfi_region {
1346920b493Spirofti 	u_int r_blocks;
1356920b493Spirofti 	u_int r_blksz;
1366920b493Spirofti };
1376920b493Spirofti 
1386920b493Spirofti struct amdcf_softc {
1396920b493Spirofti 	/* General disk infos */
1406920b493Spirofti 	struct device sc_dev;
1416920b493Spirofti 	struct disk sc_dk;
1426920b493Spirofti 	struct bufq sc_bufq;
1436920b493Spirofti 	struct buf *sc_bp;
1446920b493Spirofti 
1456920b493Spirofti 	int sc_flags;
1466920b493Spirofti #define AMDCF_LOADED	0x10
1476920b493Spirofti 
1486920b493Spirofti 	struct iobus_attach_args *sc_io;
1496920b493Spirofti 	bus_space_tag_t sc_iot;
1506920b493Spirofti 	bus_space_handle_t sc_ioh;
1516920b493Spirofti 
1526920b493Spirofti 	size_t sc_size;		/* Disk size in bytes */
1536920b493Spirofti 	u_int sc_regions;	/* Erase regions. */
1546920b493Spirofti 	struct cfi_region *sc_region;	/* Array of region info. */
1556920b493Spirofti 
1566920b493Spirofti 	u_int sc_width;
1576920b493Spirofti 	u_int sc_shift;
1586920b493Spirofti 	u_int sc_mask;
1596920b493Spirofti 
1606920b493Spirofti 	u_int sc_erase_timeout;
1616920b493Spirofti 	u_int sc_erase_max_timeout;
1626920b493Spirofti 	u_int sc_write_timeout;
1636920b493Spirofti 	u_int sc_write_max_timeout;
1646920b493Spirofti 	u_int sc_rstcmd;
1656920b493Spirofti 
1666920b493Spirofti 	u_char *sc_wrbuf;
1676920b493Spirofti 	u_int sc_wrbufsz;
1686920b493Spirofti 	u_int sc_wrofs;
1696920b493Spirofti 	u_int sc_writing;
1706920b493Spirofti };
1716920b493Spirofti 
1726920b493Spirofti int	amdcf_match(struct device *, void *, void *);
1736920b493Spirofti void	amdcf_attach(struct device *, struct device *, void *);
1746920b493Spirofti int	amdcf_detach(struct device *, int);
1756920b493Spirofti 
176471aeecfSnaddy const struct cfattach amdcf_ca = {
1776920b493Spirofti 	sizeof(struct amdcf_softc), amdcf_match, amdcf_attach, amdcf_detach
1786920b493Spirofti };
1796920b493Spirofti 
1806920b493Spirofti struct cfdriver amdcf_cd = {
1816920b493Spirofti 	NULL, "amdcf", DV_DISK
1826920b493Spirofti };
1836920b493Spirofti 
1846920b493Spirofti cdev_decl(amdcf);
1856920b493Spirofti bdev_decl(amdcf);
1866920b493Spirofti 
1876920b493Spirofti #define amdcflookup(unit) (struct amdcf_softc *)disk_lookup(&amdcf_cd, (unit))
1886920b493Spirofti int amdcfgetdisklabel(dev_t, struct amdcf_softc *, struct disklabel *, int);
1896920b493Spirofti 
1906920b493Spirofti void amdcfstart(void *);
1916920b493Spirofti void _amdcfstart(struct amdcf_softc *, struct buf *);
1926920b493Spirofti void amdcfdone(void *);
1936920b493Spirofti 
1946920b493Spirofti void amdcf_disk_read(struct amdcf_softc *, struct buf *, off_t);
1956920b493Spirofti void amdcf_disk_write(struct amdcf_softc *, struct buf *, off_t);
1966920b493Spirofti 
1976920b493Spirofti int cfi_block_start(struct amdcf_softc *, u_int);
1986920b493Spirofti int cfi_write_block(struct amdcf_softc *);
1996920b493Spirofti int cfi_erase_block(struct amdcf_softc *, u_int);
2006920b493Spirofti int cfi_block_finish(struct amdcf_softc *);
2016920b493Spirofti 
2026920b493Spirofti void cfi_array_write(struct amdcf_softc *sc, u_int, u_int, u_int);
2036920b493Spirofti void cfi_amd_write(struct amdcf_softc *, u_int, u_int, u_int);
2046920b493Spirofti 
2056920b493Spirofti uint8_t cfi_read(struct amdcf_softc *, bus_size_t, bus_size_t);
2066920b493Spirofti void cfi_write(struct amdcf_softc *, bus_size_t, bus_size_t, uint8_t);
2076920b493Spirofti int cfi_wait_ready(struct amdcf_softc *, u_int, u_int, u_int);
2086920b493Spirofti int cfi_make_cmd(uint8_t, u_int);
2096920b493Spirofti 
2106920b493Spirofti int
amdcf_match(struct device * parent,void * match,void * aux)2116920b493Spirofti amdcf_match(struct device *parent, void *match, void *aux)
2126920b493Spirofti {
2136920b493Spirofti 	struct mainbus_attach_args *maa = aux;
2146920b493Spirofti 	struct cfdata *cf = match;
2156920b493Spirofti 
2166920b493Spirofti 	if (strcmp(maa->maa_name, cf->cf_driver->cd_name) != 0)
2176920b493Spirofti 		return 0;
2186920b493Spirofti 
2196920b493Spirofti 	/* Only for DSR machines */
2202bbf581cSvisa 	if (octeon_board != BOARD_DLINK_DSR_500)
2216920b493Spirofti 		return 0;
2226920b493Spirofti 
2236920b493Spirofti 	return 1;
2246920b493Spirofti }
2256920b493Spirofti 
2266920b493Spirofti void
amdcf_attach(struct device * parent,struct device * self,void * aux)2276920b493Spirofti amdcf_attach(struct device *parent, struct device *self, void *aux)
2286920b493Spirofti {
2296920b493Spirofti 	struct amdcf_softc *sc = (void *)self;
2306920b493Spirofti 	u_int blksz, blocks, r;
2316920b493Spirofti 
2326920b493Spirofti 	sc->sc_io = aux;
2336920b493Spirofti 	sc->sc_iot = sc->sc_io->aa_bust;
2346920b493Spirofti 
2356920b493Spirofti 	if (bus_space_map(sc->sc_iot, OCTEON_AMDCF_BASE, AMDCF_MAP_SIZE, 0,
2366920b493Spirofti 	    &sc->sc_ioh)) {
2376920b493Spirofti 		printf(": can't map registers");
2386920b493Spirofti 	}
2396920b493Spirofti 
2406920b493Spirofti 	/* should be detected in the generic driver */
2416920b493Spirofti 	sc->sc_width = 1;
2426920b493Spirofti 	sc->sc_shift = 2;
2436920b493Spirofti 	sc->sc_mask = 0x000000ff;
2446920b493Spirofti 	sc->sc_rstcmd = CFI_AMD_RESET;
2456920b493Spirofti 
2466920b493Spirofti 	/* Initialize the Query Database from the CF */
2476920b493Spirofti 	cfi_array_write(sc, 0, 0, sc->sc_rstcmd);
2486920b493Spirofti 	cfi_write(sc, 0, CFI_QRY_CMD_ADDR, CFI_QRY_CMD_DATA);
2496920b493Spirofti 
2506920b493Spirofti 	/* Get time-out values for erase and write. */
2516920b493Spirofti 	sc->sc_write_timeout = 1 << cfi_read(sc, 0, CFI_QRY_TTO_WRITE);
2526920b493Spirofti 	sc->sc_erase_timeout = 1 << cfi_read(sc, 0, CFI_QRY_TTO_ERASE);
2536920b493Spirofti 	sc->sc_write_max_timeout = 1 << cfi_read(sc, 0, CFI_QRY_MTO_WRITE);
2546920b493Spirofti 	sc->sc_erase_max_timeout = 1 << cfi_read(sc, 0, CFI_QRY_MTO_ERASE);
2556920b493Spirofti 
2566920b493Spirofti 	/* Get the device size. */
2576920b493Spirofti 	sc->sc_size = 1U << cfi_read(sc, 0, CFI_QRY_SIZE);
2586920b493Spirofti 	printf(": AMD/Fujitsu %zu bytes\n", sc->sc_size);
2596920b493Spirofti 
2606920b493Spirofti 	/* Get erase regions. */
2616920b493Spirofti 	sc->sc_regions = cfi_read(sc, 0, CFI_QRY_NREGIONS);
2626920b493Spirofti 	sc->sc_region = malloc(sc->sc_regions *
2636920b493Spirofti 	    sizeof(struct cfi_region), M_TEMP, M_WAITOK | M_ZERO);
2646920b493Spirofti 
2656920b493Spirofti 	for (r = 0; r < sc->sc_regions; r++) {
2666920b493Spirofti 		blocks = cfi_read(sc, 0, CFI_QRY_REGION(r)) |
2676920b493Spirofti 		    (cfi_read(sc, 0, CFI_QRY_REGION(r) + 1) << 8);
2686920b493Spirofti 		sc->sc_region[r].r_blocks = blocks + 1;
2696920b493Spirofti 
2706920b493Spirofti 		blksz = cfi_read(sc, 0, CFI_QRY_REGION(r) + 2) |
2716920b493Spirofti 		    (cfi_read(sc, 0, CFI_QRY_REGION(r) + 3) << 8);
2726920b493Spirofti 		sc->sc_region[r].r_blksz = (blksz == 0) ? 128 :
2736920b493Spirofti 		    blksz * 256;
2746920b493Spirofti 	}
2756920b493Spirofti 
2766920b493Spirofti 	/* Reset the device to the default state */
2776920b493Spirofti 	cfi_array_write(sc, 0, 0, sc->sc_rstcmd);
2786920b493Spirofti 
2796920b493Spirofti 	/*
2806920b493Spirofti 	 * Initialize disk structures.
2816920b493Spirofti 	 */
2826920b493Spirofti 	sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
2836920b493Spirofti 	bufq_init(&sc->sc_bufq, BUFQ_DEFAULT);
2846920b493Spirofti 
2856920b493Spirofti 	/* Attach disk. */
2866920b493Spirofti 	disk_attach(&sc->sc_dev, &sc->sc_dk);
2876920b493Spirofti 
2886920b493Spirofti }
2896920b493Spirofti 
2906920b493Spirofti int
amdcf_detach(struct device * self,int flags)2916920b493Spirofti amdcf_detach(struct device *self, int flags)
2926920b493Spirofti {
2936920b493Spirofti 	struct amdcf_softc *sc = (struct amdcf_softc *)self;
2946920b493Spirofti 
2956920b493Spirofti 	bufq_drain(&sc->sc_bufq);
2966920b493Spirofti 
2976920b493Spirofti 	disk_gone(amdcfopen, self->dv_unit);
2986920b493Spirofti 
2996920b493Spirofti 	/* Detach disk. */
3006920b493Spirofti 	bufq_destroy(&sc->sc_bufq);
3016920b493Spirofti 	disk_detach(&sc->sc_dk);
3026920b493Spirofti 
3036920b493Spirofti 	return 0;
3046920b493Spirofti }
3056920b493Spirofti 
3066920b493Spirofti 
3076920b493Spirofti int
amdcfopen(dev_t dev,int flag,int fmt,struct proc * p)3086920b493Spirofti amdcfopen(dev_t dev, int flag, int fmt, struct proc *p)
3096920b493Spirofti {
3106920b493Spirofti 	struct amdcf_softc *sc;
3116920b493Spirofti 	int unit, part;
3126920b493Spirofti 	int error;
3136920b493Spirofti 
3146920b493Spirofti 	unit = DISKUNIT(dev);
3156920b493Spirofti 	sc = amdcflookup(unit);
3166920b493Spirofti 	if (sc == NULL)
3176920b493Spirofti 		return ENXIO;
3186920b493Spirofti 
3196920b493Spirofti 	/*
3206920b493Spirofti 	 * If this is the first open of this device, add a reference
3216920b493Spirofti 	 * to the adapter.
3226920b493Spirofti 	 */
3236920b493Spirofti 	if ((error = disk_lock(&sc->sc_dk)) != 0)
3246920b493Spirofti 		goto out1;
3256920b493Spirofti 
3266920b493Spirofti 	if (sc->sc_dk.dk_openmask != 0) {
3276920b493Spirofti 		/*
3286920b493Spirofti 		 * If any partition is open, but the disk has been invalidated,
3296920b493Spirofti 		 * disallow further opens.
3306920b493Spirofti 		 */
3316920b493Spirofti 		if ((sc->sc_flags & AMDCF_LOADED) == 0) {
3326920b493Spirofti 			error = EIO;
3336920b493Spirofti 			goto out;
3346920b493Spirofti 		}
3356920b493Spirofti 	} else {
3366920b493Spirofti 		if ((sc->sc_flags & AMDCF_LOADED) == 0) {
3376920b493Spirofti 			sc->sc_flags |= AMDCF_LOADED;
3386920b493Spirofti 
3396920b493Spirofti 			/* Load the partition info if not already loaded. */
3406920b493Spirofti 			if (amdcfgetdisklabel(dev, sc,
3416920b493Spirofti 			    sc->sc_dk.dk_label, 0) == EIO) {
3426920b493Spirofti 				error = EIO;
3436920b493Spirofti 				goto out;
3446920b493Spirofti 			}
3456920b493Spirofti 		}
3466920b493Spirofti 	}
3476920b493Spirofti 
3486920b493Spirofti 	part = DISKPART(dev);
3496920b493Spirofti 
3506920b493Spirofti 	if ((error = disk_openpart(&sc->sc_dk, part, fmt, 1)) != 0)
3516920b493Spirofti 		goto out;
3526920b493Spirofti 
3536920b493Spirofti 	disk_unlock(&sc->sc_dk);
3546920b493Spirofti 	device_unref(&sc->sc_dev);
3556920b493Spirofti 	return 0;
3566920b493Spirofti 
3576920b493Spirofti out:
3586920b493Spirofti 	disk_unlock(&sc->sc_dk);
3596920b493Spirofti out1:
3606920b493Spirofti 	device_unref(&sc->sc_dev);
3616920b493Spirofti 	return error;
3626920b493Spirofti }
3636920b493Spirofti 
3646920b493Spirofti /*
3656920b493Spirofti  * Load the label information on the named device
3666920b493Spirofti  */
3676920b493Spirofti int
amdcfgetdisklabel(dev_t dev,struct amdcf_softc * sc,struct disklabel * lp,int spoofonly)3686920b493Spirofti amdcfgetdisklabel(dev_t dev, struct amdcf_softc *sc, struct disklabel *lp,
3696920b493Spirofti     int spoofonly)
3706920b493Spirofti {
3716920b493Spirofti 	memset(lp, 0, sizeof(struct disklabel));
3726920b493Spirofti 
3736920b493Spirofti 	lp->d_secsize = DEV_BSIZE;
3746920b493Spirofti 	lp->d_nsectors = 1;	/* bogus */
3756920b493Spirofti 	lp->d_ntracks = 1;	/* bogus */
3766920b493Spirofti 	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
3776920b493Spirofti 	lp->d_ncylinders = sc->sc_size / lp->d_secpercyl;
3786920b493Spirofti 
3796920b493Spirofti 	strlcpy(lp->d_typename, "amdcf device", sizeof(lp->d_typename));
3806920b493Spirofti 	lp->d_type = DTYPE_SCSI;	/* bogus type, can be anything */
3816920b493Spirofti 	strlcpy(lp->d_packname, "CFI Disk", sizeof(lp->d_packname));
3826920b493Spirofti 	DL_SETDSIZE(lp, sc->sc_size / DEV_BSIZE);
3836920b493Spirofti 	lp->d_version = 1;
3846920b493Spirofti 
3856920b493Spirofti 	lp->d_magic = DISKMAGIC;
3866920b493Spirofti 	lp->d_magic2 = DISKMAGIC;
3876920b493Spirofti 	lp->d_checksum = dkcksum(lp);
3886920b493Spirofti 
3896920b493Spirofti 	/* Call the generic disklabel extraction routine */
3906920b493Spirofti 	return readdisklabel(DISKLABELDEV(dev), amdcfstrategy, lp, spoofonly);
3916920b493Spirofti }
3926920b493Spirofti 
3936920b493Spirofti int
amdcfclose(dev_t dev,int flag,int fmt,struct proc * p)3946920b493Spirofti amdcfclose(dev_t dev, int flag, int fmt, struct proc *p)
3956920b493Spirofti {
3966920b493Spirofti 	struct amdcf_softc *sc;
3976920b493Spirofti 	int part = DISKPART(dev);
3986920b493Spirofti 
3996920b493Spirofti 	sc = amdcflookup(DISKUNIT(dev));
4006920b493Spirofti 	if (sc == NULL)
4016920b493Spirofti 		return ENXIO;
4026920b493Spirofti 
4036920b493Spirofti 	disk_lock_nointr(&sc->sc_dk);
4046920b493Spirofti 
4056920b493Spirofti 	disk_closepart(&sc->sc_dk, part, fmt);
4066920b493Spirofti 
4076920b493Spirofti 	disk_unlock(&sc->sc_dk);
4086920b493Spirofti 
4096920b493Spirofti 	device_unref(&sc->sc_dev);
4106920b493Spirofti 	return 0;
4116920b493Spirofti }
4126920b493Spirofti 
4136920b493Spirofti int
amdcfread(dev_t dev,struct uio * uio,int flags)4146920b493Spirofti amdcfread(dev_t dev, struct uio *uio, int flags)
4156920b493Spirofti {
4166920b493Spirofti 	return (physio(amdcfstrategy, dev, B_READ, minphys, uio));
4176920b493Spirofti }
4186920b493Spirofti 
4196920b493Spirofti int
amdcfwrite(dev_t dev,struct uio * uio,int flags)4206920b493Spirofti amdcfwrite(dev_t dev, struct uio *uio, int flags)
4216920b493Spirofti {
4226920b493Spirofti #ifdef AMDCF_DISK_WRITE_ENABLE
4236920b493Spirofti 	return (physio(amdcfstrategy, dev, B_WRITE, minphys, uio));
4246920b493Spirofti #else
4256920b493Spirofti 	return 0;
4266920b493Spirofti #endif
4276920b493Spirofti }
4286920b493Spirofti 
4296920b493Spirofti void
amdcfstrategy(struct buf * bp)4306920b493Spirofti amdcfstrategy(struct buf *bp)
4316920b493Spirofti {
4326920b493Spirofti 	struct amdcf_softc *sc;
4336920b493Spirofti 	int s;
4346920b493Spirofti 
4356920b493Spirofti 	sc = amdcflookup(DISKUNIT(bp->b_dev));
4366920b493Spirofti 	if (sc == NULL) {
4376920b493Spirofti 		bp->b_error = ENXIO;
4386920b493Spirofti 		goto bad;
4396920b493Spirofti 	}
4406920b493Spirofti 	/* If device invalidated (e.g. media change, door open), error. */
4416920b493Spirofti 	if ((sc->sc_flags & AMDCF_LOADED) == 0) {
4426920b493Spirofti 		bp->b_error = EIO;
4436920b493Spirofti 		goto bad;
4446920b493Spirofti 	}
4456920b493Spirofti 
4466920b493Spirofti 	/* Validate the request. */
4476920b493Spirofti 	if (bounds_check_with_label(bp, sc->sc_dk.dk_label) == -1)
4486920b493Spirofti 		goto done;
4496920b493Spirofti 
4506920b493Spirofti 	/* Check that the number of sectors can fit in a byte. */
4516920b493Spirofti 	if ((bp->b_bcount / sc->sc_dk.dk_label->d_secsize) >= (1 << NBBY)) {
4526920b493Spirofti 		bp->b_error = EINVAL;
4536920b493Spirofti 		goto bad;
4546920b493Spirofti 	}
4556920b493Spirofti 
4566920b493Spirofti 	/* Queue transfer on drive, activate drive and controller if idle. */
4576920b493Spirofti 	bufq_queue(&sc->sc_bufq, bp);
4586920b493Spirofti 	s = splbio();
4596920b493Spirofti 	amdcfstart(sc);
4606920b493Spirofti 	splx(s);
4616920b493Spirofti 	device_unref(&sc->sc_dev);
4626920b493Spirofti 	return;
4636920b493Spirofti 
4646920b493Spirofti  bad:
4656920b493Spirofti 	bp->b_flags |= B_ERROR;
4666920b493Spirofti 	bp->b_resid = bp->b_bcount;
4676920b493Spirofti  done:
4686920b493Spirofti 	s = splbio();
4696920b493Spirofti 	biodone(bp);
4706920b493Spirofti 	splx(s);
4716920b493Spirofti 	if (sc != NULL)
4726920b493Spirofti 		device_unref(&sc->sc_dev);
4736920b493Spirofti }
4746920b493Spirofti 
4756920b493Spirofti int
amdcfioctl(dev_t dev,u_long xfer,caddr_t addr,int flag,struct proc * p)4766920b493Spirofti amdcfioctl(dev_t dev, u_long xfer, caddr_t addr, int flag, struct proc *p)
4776920b493Spirofti {
4786920b493Spirofti 	struct amdcf_softc *sc;
4796920b493Spirofti 	struct disklabel *lp;
4806920b493Spirofti 	int error = 0;
4816920b493Spirofti 
4826920b493Spirofti 	sc = amdcflookup(DISKUNIT(dev));
4836920b493Spirofti 	if (sc == NULL)
4846920b493Spirofti 		return ENXIO;
4856920b493Spirofti 
4866920b493Spirofti 	if ((sc->sc_flags & AMDCF_LOADED) == 0) {
4876920b493Spirofti 		error = EIO;
4886920b493Spirofti 		goto exit;
4896920b493Spirofti 	}
4906920b493Spirofti 
4916920b493Spirofti 	switch (xfer) {
4926920b493Spirofti 	case DIOCRLDINFO:
4936920b493Spirofti 		lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK);
4946920b493Spirofti 		amdcfgetdisklabel(dev, sc, lp, 0);
4956920b493Spirofti 		bcopy(lp, sc->sc_dk.dk_label, sizeof(*lp));
4969df86521Sfcambus 		free(lp, M_TEMP, sizeof(*lp));
4976920b493Spirofti 		goto exit;
4986920b493Spirofti 
4996920b493Spirofti 	case DIOCGPDINFO:
5006920b493Spirofti 		amdcfgetdisklabel(dev, sc, (struct disklabel *)addr, 1);
5016920b493Spirofti 		goto exit;
5026920b493Spirofti 
5036920b493Spirofti 	case DIOCGDINFO:
5046920b493Spirofti 		*(struct disklabel *)addr = *(sc->sc_dk.dk_label);
5056920b493Spirofti 		goto exit;
5066920b493Spirofti 
5076920b493Spirofti 	case DIOCGPART:
5086920b493Spirofti 		((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
5096920b493Spirofti 		((struct partinfo *)addr)->part =
5106920b493Spirofti 		    &sc->sc_dk.dk_label->d_partitions[DISKPART(dev)];
5116920b493Spirofti 		goto exit;
5126920b493Spirofti 
5136920b493Spirofti 	case DIOCWDINFO:
5146920b493Spirofti 	case DIOCSDINFO:
5156920b493Spirofti 		if ((flag & FWRITE) == 0) {
5166920b493Spirofti 			error = EBADF;
5176920b493Spirofti 			goto exit;
5186920b493Spirofti 		}
5196920b493Spirofti 
5206920b493Spirofti 		if ((error = disk_lock(&sc->sc_dk)) != 0)
5216920b493Spirofti 			goto exit;
5226920b493Spirofti 
5236920b493Spirofti 		error = setdisklabel(sc->sc_dk.dk_label,
5246920b493Spirofti 		    (struct disklabel *)addr, sc->sc_dk.dk_openmask);
5256920b493Spirofti 		if (error == 0) {
5266920b493Spirofti 			if (xfer == DIOCWDINFO)
5276920b493Spirofti 				error = writedisklabel(DISKLABELDEV(dev),
5286920b493Spirofti 				    amdcfstrategy, sc->sc_dk.dk_label);
5296920b493Spirofti 		}
5306920b493Spirofti 
5316920b493Spirofti 		disk_unlock(&sc->sc_dk);
5326920b493Spirofti 		goto exit;
5336920b493Spirofti 
5346920b493Spirofti 	default:
5356920b493Spirofti 		error = ENOTTY;
5366920b493Spirofti 		goto exit;
5376920b493Spirofti 	}
5386920b493Spirofti 
5396920b493Spirofti #ifdef DIAGNOSTIC
5406920b493Spirofti 	panic("amdcfioctl: impossible");
5416920b493Spirofti #endif
5426920b493Spirofti 
5436920b493Spirofti  exit:
5446920b493Spirofti 	device_unref(&sc->sc_dev);
5456920b493Spirofti 	return error;
5466920b493Spirofti }
5476920b493Spirofti 
5486920b493Spirofti /*
5496920b493Spirofti  * Dump core after a system crash.
5506920b493Spirofti  */
5516920b493Spirofti int
amdcfdump(dev_t dev,daddr_t blkno,caddr_t va,size_t size)5526920b493Spirofti amdcfdump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
5536920b493Spirofti {
5546920b493Spirofti 	return ENXIO;
5556920b493Spirofti }
5566920b493Spirofti 
5576920b493Spirofti daddr_t
amdcfsize(dev_t dev)5586920b493Spirofti amdcfsize(dev_t dev)
5596920b493Spirofti {
5606920b493Spirofti 	struct amdcf_softc *sc;
5616920b493Spirofti 	struct disklabel *lp;
5626920b493Spirofti 	int part, omask;
5636920b493Spirofti 	daddr_t size;
5646920b493Spirofti 
5656920b493Spirofti 	sc = amdcflookup(DISKUNIT(dev));
5666920b493Spirofti 	if (sc == NULL)
5676920b493Spirofti 		return (-1);
5686920b493Spirofti 
5696920b493Spirofti 	part = DISKPART(dev);
5706920b493Spirofti 	omask = sc->sc_dk.dk_openmask & (1 << part);
5716920b493Spirofti 
5726920b493Spirofti 	if (omask == 0 && amdcfopen(dev, 0, S_IFBLK, NULL) != 0) {
5736920b493Spirofti 		size = -1;
5746920b493Spirofti 		goto exit;
5756920b493Spirofti 	}
5766920b493Spirofti 
5776920b493Spirofti 	lp = sc->sc_dk.dk_label;
5786920b493Spirofti 	size = DL_SECTOBLK(lp, DL_GETPSIZE(&lp->d_partitions[part]));
5796920b493Spirofti 	if (omask == 0 && amdcfclose(dev, 0, S_IFBLK, NULL) != 0)
5806920b493Spirofti 		size = -1;
5816920b493Spirofti 
5826920b493Spirofti  exit:
5836920b493Spirofti 	device_unref(&sc->sc_dev);
5846920b493Spirofti 	return size;
5856920b493Spirofti }
5866920b493Spirofti 
5876920b493Spirofti 
5886920b493Spirofti /*
5896920b493Spirofti  * Queue a drive for I/O.
5906920b493Spirofti  */
5916920b493Spirofti void
amdcfstart(void * arg)5926920b493Spirofti amdcfstart(void *arg)
5936920b493Spirofti {
5946920b493Spirofti 	struct amdcf_softc *sc = arg;
5956920b493Spirofti 	struct buf *bp;
5966920b493Spirofti 
5976920b493Spirofti 	while ((bp = bufq_dequeue(&sc->sc_bufq)) != NULL) {
5986920b493Spirofti 		/* Transfer this buffer now. */
5996920b493Spirofti 		_amdcfstart(sc, bp);
6006920b493Spirofti 	}
6016920b493Spirofti }
6026920b493Spirofti 
6036920b493Spirofti void
_amdcfstart(struct amdcf_softc * sc,struct buf * bp)6046920b493Spirofti _amdcfstart(struct amdcf_softc *sc, struct buf *bp)
6056920b493Spirofti {
6066920b493Spirofti 	off_t off;
6076920b493Spirofti 	struct partition *p;
6086920b493Spirofti 
6096920b493Spirofti 	sc->sc_bp = bp;
6106920b493Spirofti 
6116920b493Spirofti 	/* Fetch buffer's read/write offset */
6126920b493Spirofti 	p = &sc->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)];
6136920b493Spirofti 	off = DL_GETPOFFSET(p) * sc->sc_dk.dk_label->d_secsize +
6146920b493Spirofti 	    (u_int64_t)bp->b_blkno * DEV_BSIZE;
6156920b493Spirofti 	if (off > sc->sc_size) {
6166920b493Spirofti 		bp->b_flags |= B_ERROR;
6176920b493Spirofti 		bp->b_error = EIO;
6186920b493Spirofti 		return;
6196920b493Spirofti 	}
6206920b493Spirofti 
6216920b493Spirofti 	/* Instrumentation. */
6226920b493Spirofti 	disk_busy(&sc->sc_dk);
6236920b493Spirofti 
6246920b493Spirofti 	if (bp->b_flags & B_READ)
6256920b493Spirofti 		amdcf_disk_read(sc, bp, off);
6266920b493Spirofti #ifdef AMDCF_DISK_WRITE_ENABLE
6276920b493Spirofti 	else
6286920b493Spirofti 		amdcf_disk_write(sc, bp, off);
6296920b493Spirofti #endif
6306920b493Spirofti 
6316920b493Spirofti 	amdcfdone(sc);
6326920b493Spirofti }
6336920b493Spirofti 
6346920b493Spirofti void
amdcfdone(void * arg)6356920b493Spirofti amdcfdone(void *arg)
6366920b493Spirofti {
6376920b493Spirofti 	struct amdcf_softc *sc = arg;
6386920b493Spirofti 	struct buf *bp = sc->sc_bp;
6396920b493Spirofti 
6406920b493Spirofti 	if (bp->b_error == 0)
6416920b493Spirofti 		bp->b_resid = 0;
6426920b493Spirofti 	else
6436920b493Spirofti 		bp->b_flags |= B_ERROR;
6446920b493Spirofti 
6456920b493Spirofti 	disk_unbusy(&sc->sc_dk, (bp->b_bcount - bp->b_resid),
646d40269afSderaadt 	    bp->b_blkno, (bp->b_flags & B_READ));
6476920b493Spirofti 	biodone(bp);
6486920b493Spirofti }
6496920b493Spirofti 
6506920b493Spirofti void
amdcf_disk_read(struct amdcf_softc * sc,struct buf * bp,off_t off)6516920b493Spirofti amdcf_disk_read(struct amdcf_softc *sc, struct buf *bp, off_t off)
6526920b493Spirofti {
6536920b493Spirofti 	long resid;
6546920b493Spirofti 
6556920b493Spirofti 	if (sc->sc_writing) {
6566920b493Spirofti 		bp->b_error = cfi_block_finish(sc);
6576920b493Spirofti 		if (bp->b_error) {
6586920b493Spirofti 			bp->b_flags |= B_ERROR;
6596920b493Spirofti 			return;
6606920b493Spirofti 		}
6616920b493Spirofti 	}
6626920b493Spirofti 
6636920b493Spirofti 	resid = bp->b_bcount;
6646920b493Spirofti 	uint8_t *dp = (uint8_t *)bp->b_data;
6656920b493Spirofti 	while (resid > 0 && off < sc->sc_size) {
6666920b493Spirofti 		*dp++ = cfi_read(sc, off, 0);
6676920b493Spirofti 		off += 1, resid -= 1;
6686920b493Spirofti 	}
6696920b493Spirofti 	bp->b_resid = resid;
6706920b493Spirofti }
6716920b493Spirofti 
6726920b493Spirofti void
amdcf_disk_write(struct amdcf_softc * sc,struct buf * bp,off_t off)6736920b493Spirofti amdcf_disk_write(struct amdcf_softc *sc, struct buf *bp, off_t off)
6746920b493Spirofti {
6756920b493Spirofti 	long resid;
6766920b493Spirofti 	u_int top;
6776920b493Spirofti 
6786920b493Spirofti 	resid = bp->b_bcount;
6796920b493Spirofti 	while (resid > 0) {
6806920b493Spirofti 		/*
6816920b493Spirofti 		 * Finish the current block if we're about to write
6826920b493Spirofti 		 * to a different block.
6836920b493Spirofti 		 */
6846920b493Spirofti 		if (sc->sc_writing) {
6856920b493Spirofti 			top = sc->sc_wrofs + sc->sc_wrbufsz;
6866920b493Spirofti 			if (off < sc->sc_wrofs || off >= top)
6876920b493Spirofti 				cfi_block_finish(sc);
6886920b493Spirofti 		}
6896920b493Spirofti 
6906920b493Spirofti 		/* Start writing to a (new) block if applicable. */
6916920b493Spirofti 		if (!sc->sc_writing) {
6926920b493Spirofti 			bp->b_error = cfi_block_start(sc, off);
6936920b493Spirofti 			if (bp->b_error) {
6946920b493Spirofti 				bp->b_flags |= B_ERROR;
6956920b493Spirofti 				return;
6966920b493Spirofti 			}
6976920b493Spirofti 		}
6986920b493Spirofti 
6996920b493Spirofti 		top = sc->sc_wrofs + sc->sc_wrbufsz;
7006920b493Spirofti 		bcopy(bp->b_data,
7016920b493Spirofti 		    sc->sc_wrbuf + off - sc->sc_wrofs,
7026920b493Spirofti 		    MIN(top - off, resid));
7036920b493Spirofti 		resid -= MIN(top - off, resid);
7046920b493Spirofti 	}
7056920b493Spirofti 	bp->b_resid = resid;
7066920b493Spirofti }
7076920b493Spirofti 
7086920b493Spirofti /*
7096920b493Spirofti  * Begin writing into a new block/sector.  We read the sector into
7106920b493Spirofti  * memory and keep updating that, until we move into another sector
7116920b493Spirofti  * or the process stops writing. At that time we write the whole
7126920b493Spirofti  * sector to flash (see cfi_block_finish).
7136920b493Spirofti  */
7146920b493Spirofti int
cfi_block_start(struct amdcf_softc * sc,u_int ofs)7156920b493Spirofti cfi_block_start(struct amdcf_softc *sc, u_int ofs)
7166920b493Spirofti {
7176920b493Spirofti 	u_int rofs, rsz;
7186920b493Spirofti 	int r;
7196920b493Spirofti 	uint8_t *ptr;
7206920b493Spirofti 
7216920b493Spirofti 	rofs = 0;
7226920b493Spirofti 	for (r = 0; r < sc->sc_regions; r++) {
7236920b493Spirofti 		rsz = sc->sc_region[r].r_blocks * sc->sc_region[r].r_blksz;
7246920b493Spirofti 		if (ofs < rofs + rsz)
7256920b493Spirofti 			break;
7266920b493Spirofti 		rofs += rsz;
7276920b493Spirofti 	}
7286920b493Spirofti 	if (r == sc->sc_regions)
7296920b493Spirofti 		return (EFAULT);
7306920b493Spirofti 
7316920b493Spirofti 	sc->sc_wrbufsz = sc->sc_region[r].r_blksz;
7326920b493Spirofti 	sc->sc_wrbuf = malloc(sc->sc_wrbufsz, M_TEMP, M_WAITOK);
7336920b493Spirofti 	sc->sc_wrofs = ofs - (ofs - rofs) % sc->sc_wrbufsz;
7346920b493Spirofti 
7356920b493Spirofti 	ptr = sc->sc_wrbuf;
7366920b493Spirofti 	/* Read the block from flash for byte-serving. */
7376920b493Spirofti 	for (r = 0; r < sc->sc_wrbufsz; r++)
7386920b493Spirofti 		*(ptr)++ = cfi_read(sc, sc->sc_wrofs + r, 0);
7396920b493Spirofti 
7406920b493Spirofti 	sc->sc_writing = 1;
7416920b493Spirofti 	return (0);
7426920b493Spirofti }
7436920b493Spirofti 
7446920b493Spirofti /*
7456920b493Spirofti  * Finish updating the current block/sector by writing the compound
7466920b493Spirofti  * set of changes to the flash.
7476920b493Spirofti  */
7486920b493Spirofti int
cfi_block_finish(struct amdcf_softc * sc)7496920b493Spirofti cfi_block_finish(struct amdcf_softc *sc)
7506920b493Spirofti {
7516920b493Spirofti 	int error;
7526920b493Spirofti 
7536920b493Spirofti 	error = cfi_write_block(sc);
7546920b493Spirofti 	free(sc->sc_wrbuf, M_TEMP, sc->sc_wrbufsz);
7556920b493Spirofti 	sc->sc_wrbuf = NULL;
7566920b493Spirofti 	sc->sc_wrbufsz = 0;
7576920b493Spirofti 	sc->sc_wrofs = 0;
7586920b493Spirofti 	sc->sc_writing = 0;
7596920b493Spirofti 	return (error);
7606920b493Spirofti }
7616920b493Spirofti 
7626920b493Spirofti int
cfi_write_block(struct amdcf_softc * sc)7636920b493Spirofti cfi_write_block(struct amdcf_softc *sc)
7646920b493Spirofti {
7656920b493Spirofti 	uint8_t *ptr;
7666920b493Spirofti 	int error, i, s;
7676920b493Spirofti 
7686920b493Spirofti 	if (sc->sc_wrofs > sc->sc_size)
7696920b493Spirofti 		panic("CFI: write offset (%x) bigger "
7706920b493Spirofti 		    "than cfi array size (%zu)\n",
7716920b493Spirofti 		    sc->sc_wrofs, sc->sc_size);
7726920b493Spirofti 
7736920b493Spirofti 	if ((sc->sc_wrofs < BOOTLOADER_ADDR) ||
7746920b493Spirofti 	    ((sc->sc_wrofs + sc->sc_wrbufsz) < BOOTLOADER_ADDR))
7756920b493Spirofti 		return EOPNOTSUPP;
7766920b493Spirofti 
7776920b493Spirofti 	error = cfi_erase_block(sc, sc->sc_wrofs);
7786920b493Spirofti 	if (error)
7796920b493Spirofti 		goto out;
7806920b493Spirofti 
7816920b493Spirofti 	/* Write the block. */
7826920b493Spirofti 	ptr = sc->sc_wrbuf;
7836920b493Spirofti 
7846920b493Spirofti 	for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) {
7856920b493Spirofti 
7866920b493Spirofti 		/*
7876920b493Spirofti 		 * Make sure the command to start a write and the
7886920b493Spirofti 		 * actual write happens back-to-back without any
7896920b493Spirofti 		 * excessive delays.
7906920b493Spirofti 		 */
7916920b493Spirofti 		s = splbio();
7926920b493Spirofti 
7936920b493Spirofti 		cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START,
7946920b493Spirofti 			    CFI_AMD_PROGRAM);
7956920b493Spirofti 		/* Raw data do not use cfi_array_write */
7966920b493Spirofti 		cfi_write(sc, sc->sc_wrofs + i, 0, *(ptr)++);
7976920b493Spirofti 
7986920b493Spirofti 		splx(s);
7996920b493Spirofti 
8006920b493Spirofti 		error = cfi_wait_ready(sc, sc->sc_wrofs + i,
8016920b493Spirofti 		    sc->sc_write_timeout, sc->sc_write_max_timeout);
8026920b493Spirofti 		if (error)
8036920b493Spirofti 			goto out;
8046920b493Spirofti 	}
8056920b493Spirofti 
8066920b493Spirofti out:
8076920b493Spirofti 	cfi_array_write(sc, sc->sc_wrofs, 0, sc->sc_rstcmd);
8086920b493Spirofti 	return error;
8096920b493Spirofti }
8106920b493Spirofti 
8116920b493Spirofti int
cfi_erase_block(struct amdcf_softc * sc,u_int offset)8126920b493Spirofti cfi_erase_block(struct amdcf_softc *sc, u_int offset)
8136920b493Spirofti {
8146920b493Spirofti 	int error = 0;
8156920b493Spirofti 
8166920b493Spirofti 	if (offset > sc->sc_size)
8176920b493Spirofti 		panic("CFI: erase offset (%x) bigger "
8186920b493Spirofti 		    "than cfi array size (%zu)\n",
8196920b493Spirofti 		    sc->sc_wrofs, sc->sc_size);
8206920b493Spirofti 
8216920b493Spirofti 	/* Erase the block. */
8226920b493Spirofti 	cfi_amd_write(sc, offset, 0, CFI_AMD_BLOCK_ERASE);
8236920b493Spirofti 
8246920b493Spirofti 	error = cfi_wait_ready(sc, offset, sc->sc_erase_timeout,
8256920b493Spirofti 	    sc->sc_erase_max_timeout);
8266920b493Spirofti 
8276920b493Spirofti 	return error;
8286920b493Spirofti }
8296920b493Spirofti 
8306920b493Spirofti 
8316920b493Spirofti 
8326920b493Spirofti int
cfi_wait_ready(struct amdcf_softc * sc,u_int ofs,u_int timeout,u_int count)8336920b493Spirofti cfi_wait_ready(struct amdcf_softc *sc, u_int ofs, u_int timeout, u_int count)
8346920b493Spirofti {
8356920b493Spirofti 	int done, error;
8366920b493Spirofti 	u_int st0 = 0, st = 0;
8376920b493Spirofti 
8386920b493Spirofti 	done = 0;
8396920b493Spirofti 	error = 0;
8406920b493Spirofti 
8416920b493Spirofti 	if (!timeout)
8426920b493Spirofti 		timeout = 100;  /* Default to 100 uS */
8436920b493Spirofti 	if (!count)
8446920b493Spirofti 		count = 100;    /* Max timeout is 10 mS */
8456920b493Spirofti 
8466920b493Spirofti 	while (!done && !error && count) {
8476920b493Spirofti 		DELAY(timeout);
8486920b493Spirofti 
8496920b493Spirofti 		count--;
8506920b493Spirofti 
8516920b493Spirofti 		/*
8526920b493Spirofti 		 * read sc->sc_width bytes, and check for toggle bit.
8536920b493Spirofti 		 */
8546920b493Spirofti 		st0 = cfi_read(sc, ofs, 0);
8556920b493Spirofti 		st = cfi_read(sc, ofs, 0);
8566920b493Spirofti 		done = ((st & cfi_make_cmd(0x40, sc->sc_mask)) ==
8576920b493Spirofti 		    (st0 & cfi_make_cmd(0x40, sc->sc_mask))) ? 1 : 0;
8586920b493Spirofti 
8596920b493Spirofti 		break;
8606920b493Spirofti 	}
8616920b493Spirofti 	if (!done && !error)
8626920b493Spirofti 		error = ETIMEDOUT;
8636920b493Spirofti 	if (error)
8646920b493Spirofti 		printf("\nerror=%d (st 0x%x st0 0x%x) at offset=%x\n",
8656920b493Spirofti 		    error, st, st0, ofs);
8666920b493Spirofti 	return error;
8676920b493Spirofti }
8686920b493Spirofti 
8696920b493Spirofti /*
8706920b493Spirofti  * cfi_array_write
8716920b493Spirofti  * fill "bus width" word with value of var data by array mask sc->sc_mask
8726920b493Spirofti  */
8736920b493Spirofti void
cfi_array_write(struct amdcf_softc * sc,u_int ofs,u_int addr,u_int data)8746920b493Spirofti cfi_array_write(struct amdcf_softc *sc, u_int ofs, u_int addr, u_int data)
8756920b493Spirofti {
8766920b493Spirofti 	data &= 0xff;
8776920b493Spirofti 	cfi_write(sc, ofs, addr, cfi_make_cmd(data, sc->sc_mask));
8786920b493Spirofti }
8796920b493Spirofti 
8806920b493Spirofti void
cfi_amd_write(struct amdcf_softc * sc,u_int ofs,u_int addr,u_int data)8816920b493Spirofti cfi_amd_write(struct amdcf_softc *sc, u_int ofs, u_int addr, u_int data)
8826920b493Spirofti {
8836920b493Spirofti 	cfi_array_write(sc, ofs, AMD_ADDR_START, CFI_AMD_UNLOCK);
8846920b493Spirofti 	cfi_array_write(sc, ofs, AMD_ADDR_ACK, CFI_AMD_UNLOCK_ACK);
8856920b493Spirofti 	cfi_array_write(sc, ofs, addr, data);
8866920b493Spirofti }
8876920b493Spirofti 
8886920b493Spirofti 
8896920b493Spirofti 
8906920b493Spirofti /*
8916920b493Spirofti  * The following routines assume width=1 and shift=2 as that is
8926920b493Spirofti  * the case on the Octeon DSR machines.
8936920b493Spirofti  * If this assumption fails a new detection routine should be written
8946920b493Spirofti  * and called during attach.
8956920b493Spirofti  */
8966920b493Spirofti uint8_t
cfi_read(struct amdcf_softc * sc,bus_size_t base,bus_size_t offset)8976920b493Spirofti cfi_read(struct amdcf_softc *sc, bus_size_t base, bus_size_t offset)
8986920b493Spirofti {
8996920b493Spirofti 	return bus_space_read_1(sc->sc_iot, sc->sc_ioh,
9006920b493Spirofti 	    base | (offset * sc->sc_shift));
9016920b493Spirofti }
9026920b493Spirofti 
9036920b493Spirofti void
cfi_write(struct amdcf_softc * sc,bus_size_t base,bus_size_t offset,uint8_t val)9046920b493Spirofti cfi_write(struct amdcf_softc *sc, bus_size_t base, bus_size_t offset,
9056920b493Spirofti     uint8_t val)
9066920b493Spirofti {
9076920b493Spirofti 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
9086920b493Spirofti 	    base | (offset * sc->sc_shift), val);
9096920b493Spirofti }
9106920b493Spirofti 
9116920b493Spirofti int
cfi_make_cmd(uint8_t cmd,u_int mask)9126920b493Spirofti cfi_make_cmd(uint8_t cmd, u_int mask)
9136920b493Spirofti {
9146920b493Spirofti 	int i;
9156920b493Spirofti 	u_int data = 0;
9166920b493Spirofti 
9176920b493Spirofti 	for (i = 0; i < sizeof(int); i ++) {
9186920b493Spirofti 		if (mask & (0xff << (i*8)))
9196920b493Spirofti 			data |= cmd << (i*8);
9206920b493Spirofti 	}
9216920b493Spirofti 
9226920b493Spirofti 	return data;
9236920b493Spirofti }
924