Here is a diff that adds a new flash driver for octeon that allows me to
access the internal memory of my DSR500.

This follows the CFI specification with code borrowed from zrouter
(FreeBSD). The idea, once the current driver is thoroughly tested, would
be to move it to MI land.

The prerequisites to MI are width, shift and row detection and handling.
In the long run I hope to be able to also add wdc support.

For now I would like to commit my work in the octeon tree with write
support disabled. Comments? Okays?


Index: conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/octeon/conf/GENERIC,v
retrieving revision 1.21
diff -u -p -r1.21 GENERIC
--- conf/GENERIC        19 Jul 2015 23:46:50 -0000      1.21
+++ conf/GENERIC        20 Jul 2015 18:51:58 -0000
@@ -35,6 +35,7 @@ uartbus0      at mainbus0
 octrtc0                at mainbus0
 
 octcf0         at iobus0
+amdcf0         at iobus0
 octrng0                at iobus0
 
 com0           at uartbus0
Index: conf/files.octeon
===================================================================
RCS file: /cvs/src/sys/arch/octeon/conf/files.octeon,v
retrieving revision 1.23
diff -u -p -r1.23 files.octeon
--- conf/files.octeon   19 Jul 2015 23:46:50 -0000      1.23
+++ conf/files.octeon   20 Jul 2015 18:51:58 -0000
@@ -10,6 +10,7 @@ major { cd = 3 }
 major  { wd = 4 }
 major  { rd = 8 }
 major  { octcf = 15 }
+major  { amdcf = 19 }
 
 file   dev/cninit.c
 file   arch/octeon/octeon/autoconf.c
@@ -74,6 +75,11 @@ file arch/octeon/dev/octdwctwo.c                     octdw
 device octcf: disk
 attach octcf at iobus
 file   arch/octeon/dev/octcf.c                         octcf
+
+device amdcf: disk
+attach amdcf at iobus
+file   arch/octeon/dev/amdcf.c                         amdcf
+
 
 # On-board RNG
 device octrng
Index: dev/octeon_iobus.c
===================================================================
RCS file: /cvs/src/sys/arch/octeon/dev/octeon_iobus.c,v
retrieving revision 1.13
diff -u -p -r1.13 octeon_iobus.c
--- dev/octeon_iobus.c  19 Jul 2015 23:46:50 -0000      1.13
+++ dev/octeon_iobus.c  20 Jul 2015 18:51:58 -0000
@@ -149,6 +149,7 @@ static const struct octeon_iobus_addrs i
        { "cn30xxgmx",  GMX0_BASE_PORT0 },
        { "octrng",     OCTEON_RNG_BASE },
        { "dwctwo",     USBN_BASE       },
+       { "amdcf",      OCTEON_AMDCF_BASE},
 };
 
 /* There can only be one. */
Index: dev/amdcf.c
===================================================================
RCS file: dev/amdcf.c
diff -N dev/amdcf.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/amdcf.c 20 Jul 2015 18:51:58 -0000
@@ -0,0 +1,952 @@
+/*     $OpenBSD$       */
+
+/*
+ * Copyright (c) 2007, Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 2009 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 2015 Paul Irofti.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mutex.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/disk.h>
+#include <sys/syslog.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/dkio.h>
+
+#include <machine/intr.h>
+#include <machine/bus.h>
+#include <machine/autoconf.h>
+
+#include <octeon/dev/iobusvar.h>
+#include <machine/octeonreg.h>
+#include <machine/octeonvar.h>
+
+
+#define CFI_QRY_CMD_ADDR       0x55
+#define CFI_QRY_CMD_DATA       0x98
+
+#define CFI_QRY_TTO_WRITE      0x1f
+#define CFI_QRY_TTO_ERASE      0x21
+#define CFI_QRY_MTO_WRITE      0x23
+#define CFI_QRY_MTO_ERASE      0x25
+
+#define CFI_QRY_SIZE           0x27
+#define        CFI_QRY_NREGIONS        0x2c
+#define CFI_QRY_REGION0        0x31
+#define CFI_QRY_REGION(x)      (CFI_QRY_REGION0 + (x) * 4)
+
+#define CFI_BCS_READ_ARRAY     0xff
+
+#define CFI_DISK_SECSIZE       512
+#define CFI_DISK_MAXIOSIZE     65536
+
+#define AMDCF_MAP_SIZE         0x02000000
+
+#define CFI_AMD_BLOCK_ERASE    0x30
+#define CFI_AMD_UNLOCK         0xaa
+#define CFI_AMD_UNLOCK_ACK     0x55
+#define CFI_AMD_PROGRAM                0xa0
+#define CFI_AMD_RESET          0xf0
+
+#define AMD_ADDR_START         0x555
+#define AMD_ADDR_ACK           0x2aa
+
+#define BOOTLOADER_ADDR                0xa0000
+
+struct cfi_region {
+        u_int r_blocks;
+        u_int r_blksz;
+};
+
+struct amdcf_softc {
+       /* General disk infos */
+       struct device sc_dev;
+       struct disk sc_dk;
+       struct bufq sc_bufq;
+       struct buf *sc_bp;
+
+       int sc_flags;
+#define AMDCF_LOADED   0x10
+
+       struct iobus_attach_args *sc_io;
+       bus_space_tag_t sc_iot;
+       bus_space_handle_t sc_ioh;
+
+       size_t sc_size;         /* Disk size in bytes */
+       u_int sc_regions;       /* Erase regions. */
+       struct cfi_region *sc_region;   /* Array of region info. */
+
+       u_int sc_width;
+       u_int sc_shift;
+       u_int sc_mask;
+
+       u_int sc_erase_timeout;
+       u_int sc_erase_max_timeout;
+       u_int sc_write_timeout;
+       u_int sc_write_max_timeout;
+       u_int sc_rstcmd;
+
+       u_char *sc_wrbuf;
+       u_int sc_wrbufsz;
+       u_int sc_wrofs;
+       u_int sc_writing;
+};
+
+int    amdcf_match(struct device *, void *, void *);
+void   amdcf_attach(struct device *, struct device *, void *);
+int    amdcf_detach(struct device *, int);
+
+struct cfattach amdcf_ca = {
+       sizeof(struct amdcf_softc), amdcf_match, amdcf_attach, amdcf_detach
+};
+
+struct cfdriver amdcf_cd = {
+       NULL, "amdcf", DV_DISK
+};
+
+cdev_decl(amdcf);
+bdev_decl(amdcf);
+
+#define amdcflookup(unit) (struct amdcf_softc *)disk_lookup(&amdcf_cd, (unit))
+int amdcfgetdisklabel(dev_t, struct amdcf_softc *, struct disklabel *, int);
+
+void amdcfstart(void *);
+void _amdcfstart(struct amdcf_softc *, struct buf *);
+void amdcfdone(void *);
+
+void amdcf_disk_read(struct amdcf_softc *, struct buf *, off_t);
+void amdcf_disk_write(struct amdcf_softc *, struct buf *, off_t);
+
+int cfi_block_start(struct amdcf_softc *, u_int);
+int cfi_write_block(struct amdcf_softc *);
+int cfi_erase_block(struct amdcf_softc *, u_int);
+int cfi_block_finish(struct amdcf_softc *);
+
+void cfi_array_write(struct amdcf_softc *sc, u_int, u_int, u_int);
+void cfi_amd_write(struct amdcf_softc *, u_int, u_int, u_int);
+
+uint8_t cfi_read_qry(struct amdcf_softc *, uint64_t);
+uint8_t cfi_read(struct amdcf_softc *, bus_size_t, bus_size_t);
+void cfi_write(struct amdcf_softc *, bus_size_t, bus_size_t, uint8_t);
+int cfi_wait_ready(struct amdcf_softc *, u_int, u_int, u_int);
+int cfi_make_cmd(uint8_t, u_int);
+
+int
+amdcf_match(struct device *parent, void *match, void *aux)
+{
+       struct mainbus_attach_args *maa = aux;
+       struct cfdata *cf = match;
+
+       if (strcmp(maa->maa_name, cf->cf_driver->cd_name) != 0)
+               return 0;
+
+       /* Only for DSR machines */
+       if (octeon_boot_info->board_type != BOARD_TYPE_DSR_500)
+               return 0;
+
+       return 1;
+}
+
+void
+amdcf_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct amdcf_softc *sc = (void *)self;
+       u_int blksz, blocks, r;
+
+       sc->sc_io = aux;
+       sc->sc_iot = sc->sc_io->aa_bust;
+
+       if (bus_space_map(sc->sc_iot, OCTEON_AMDCF_BASE, AMDCF_MAP_SIZE, 0,
+           &sc->sc_ioh)) {
+               printf(": can't map registers");
+       }
+
+       /* should be detected in the generic driver */
+       sc->sc_width = 1;
+        sc->sc_shift = 2;
+       sc->sc_mask = 0x000000ff;
+       sc->sc_rstcmd = CFI_AMD_RESET;
+
+       /* Initialize the Query Database from the CF */
+       cfi_array_write(sc, 0, 0, sc->sc_rstcmd);
+       cfi_write(sc, 0, CFI_QRY_CMD_ADDR, CFI_QRY_CMD_DATA);
+
+       /* Get time-out values for erase and write. */
+       sc->sc_write_timeout = 1 << cfi_read(sc, 0, CFI_QRY_TTO_WRITE);
+       sc->sc_erase_timeout = 1 << cfi_read(sc, 0, CFI_QRY_TTO_ERASE);
+       sc->sc_write_max_timeout = 1 << cfi_read(sc, 0, CFI_QRY_MTO_WRITE);
+       sc->sc_erase_max_timeout = 1 << cfi_read(sc, 0, CFI_QRY_MTO_ERASE);
+
+       /* Get the device size. */
+        sc->sc_size = 1U << cfi_read(sc, 0, CFI_QRY_SIZE);
+       printf(": AMD/Fujitsu %zu bytes\n", sc->sc_size);
+
+       /* Get erase regions. */
+       sc->sc_regions = cfi_read(sc, 0, CFI_QRY_NREGIONS);
+       sc->sc_region = malloc(sc->sc_regions *
+           sizeof(struct cfi_region), M_TEMP, M_WAITOK | M_ZERO);
+
+       for (r = 0; r < sc->sc_regions; r++) {
+               blocks = cfi_read(sc, 0, CFI_QRY_REGION(r)) |
+                   (cfi_read(sc, 0, CFI_QRY_REGION(r) + 1) << 8);
+               sc->sc_region[r].r_blocks = blocks + 1;
+
+               blksz = cfi_read(sc, 0, CFI_QRY_REGION(r) + 2) |
+                   (cfi_read(sc, 0, CFI_QRY_REGION(r) + 3) << 8);
+               sc->sc_region[r].r_blksz = (blksz == 0) ? 128 :
+                   blksz * 256;
+       }
+
+       /* Reset the device to the default state */
+       cfi_array_write(sc, 0, 0, sc->sc_rstcmd);
+
+       /*
+        * Initialize disk structures.
+        */
+       sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
+       bufq_init(&sc->sc_bufq, BUFQ_DEFAULT);
+
+       /* Attach disk. */
+       disk_attach(&sc->sc_dev, &sc->sc_dk);
+
+}
+
+int
+amdcf_detach(struct device *self, int flags)
+{
+       struct amdcf_softc *sc = (struct amdcf_softc *)self;
+
+       bufq_drain(&sc->sc_bufq);
+
+       disk_gone(amdcfopen, self->dv_unit);
+
+       /* Detach disk. */
+       bufq_destroy(&sc->sc_bufq);
+       disk_detach(&sc->sc_dk);
+
+       return 0;
+}
+
+
+int
+amdcfopen(dev_t dev, int flag, int fmt, struct proc *p)
+{
+       struct amdcf_softc *sc;
+       int unit, part;
+       int error;
+
+       unit = DISKUNIT(dev);
+       sc = amdcflookup(unit);
+       if (sc == NULL)
+               return ENXIO;
+
+       /*
+        * If this is the first open of this device, add a reference
+        * to the adapter.
+        */
+       if ((error = disk_lock(&sc->sc_dk)) != 0)
+               goto out1;
+
+       if (sc->sc_dk.dk_openmask != 0) {
+               /*
+                * If any partition is open, but the disk has been invalidated,
+                * disallow further opens.
+                */
+               if ((sc->sc_flags & AMDCF_LOADED) == 0) {
+                       error = EIO;
+                       goto out;
+               }
+       } else {
+               if ((sc->sc_flags & AMDCF_LOADED) == 0) {
+                       sc->sc_flags |= AMDCF_LOADED;
+
+                       /* Load the partition info if not already loaded. */
+                       if (amdcfgetdisklabel(dev, sc,
+                           sc->sc_dk.dk_label, 0) == EIO) {
+                               error = EIO;
+                               goto out;
+                       }
+               }
+       }
+
+       part = DISKPART(dev);
+
+       if ((error = disk_openpart(&sc->sc_dk, part, fmt, 1)) != 0)
+               goto out;
+
+       disk_unlock(&sc->sc_dk);
+       device_unref(&sc->sc_dev);
+       return 0;
+
+out:
+       disk_unlock(&sc->sc_dk);
+out1:
+       device_unref(&sc->sc_dev);
+       return error;
+}
+
+/*
+ * Load the label information on the named device
+ */
+int
+amdcfgetdisklabel(dev_t dev, struct amdcf_softc *sc, struct disklabel *lp,
+    int spoofonly)
+{
+       memset(lp, 0, sizeof(struct disklabel));
+
+       lp->d_secsize = DEV_BSIZE;
+       lp->d_nsectors = 1;     /* bogus */
+       lp->d_ntracks = 1;      /* bogus */
+       lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
+       lp->d_ncylinders = sc->sc_size / lp->d_secpercyl;
+
+       strlcpy(lp->d_typename, "amdcf device", sizeof(lp->d_typename));
+       lp->d_type = DTYPE_SCSI;        /* bogus type, can be anything */
+       strlcpy(lp->d_packname, "CFI Disk", sizeof(lp->d_packname));
+       DL_SETDSIZE(lp, sc->sc_size / DEV_BSIZE);
+       lp->d_flags = 0;
+       lp->d_version = 1;
+
+       lp->d_magic = DISKMAGIC;
+       lp->d_magic2 = DISKMAGIC;
+       lp->d_checksum = dkcksum(lp);
+
+       /* Call the generic disklabel extraction routine */
+       return readdisklabel(DISKLABELDEV(dev), amdcfstrategy, lp, spoofonly);
+}
+
+int
+amdcfclose(dev_t dev, int flag, int fmt, struct proc *p)
+{
+       struct amdcf_softc *sc;
+       int part = DISKPART(dev);
+
+       sc = amdcflookup(DISKUNIT(dev));
+       if (sc == NULL)
+               return ENXIO;
+
+       disk_lock_nointr(&sc->sc_dk);
+
+       disk_closepart(&sc->sc_dk, part, fmt);
+
+       disk_unlock(&sc->sc_dk);
+
+       device_unref(&sc->sc_dev);
+       return 0;
+}
+
+int
+amdcfread(dev_t dev, struct uio *uio, int flags)
+{
+       return (physio(amdcfstrategy, dev, B_READ, minphys, uio));
+}
+
+int
+amdcfwrite(dev_t dev, struct uio *uio, int flags)
+{
+#ifdef AMDCF_DISK_WRITE_ENABLE
+       return (physio(amdcfstrategy, dev, B_WRITE, minphys, uio));
+#else
+       return 0;
+#endif
+}
+
+void
+amdcfstrategy(struct buf *bp)
+{
+       struct amdcf_softc *sc;
+       int s;
+
+       sc = amdcflookup(DISKUNIT(bp->b_dev));
+       if (sc == NULL) {
+               bp->b_error = ENXIO;
+               goto bad;
+       }
+       /* If device invalidated (e.g. media change, door open), error. */
+       if ((sc->sc_flags & AMDCF_LOADED) == 0) {
+               bp->b_error = EIO;
+               goto bad;
+       }
+
+       /* Validate the request. */
+       if (bounds_check_with_label(bp, sc->sc_dk.dk_label) == -1)
+               goto done;
+
+       /* Check that the number of sectors can fit in a byte. */
+       if ((bp->b_bcount / sc->sc_dk.dk_label->d_secsize) >= (1 << NBBY)) {
+               bp->b_error = EINVAL;
+               goto bad;
+       }
+
+       /* Queue transfer on drive, activate drive and controller if idle. */
+       bufq_queue(&sc->sc_bufq, bp);
+       s = splbio();
+       amdcfstart(sc);
+       splx(s);
+       device_unref(&sc->sc_dev);
+       return;
+
+ bad:
+       bp->b_flags |= B_ERROR;
+       bp->b_resid = bp->b_bcount;
+ done:
+       s = splbio();
+       biodone(bp);
+       splx(s);
+       if (sc != NULL)
+               device_unref(&sc->sc_dev);
+}
+
+int
+amdcfioctl(dev_t dev, u_long xfer, caddr_t addr, int flag, struct proc *p)
+{
+       struct amdcf_softc *sc;
+       struct disklabel *lp;
+       int error = 0;
+
+       sc = amdcflookup(DISKUNIT(dev));
+       if (sc == NULL)
+               return ENXIO;
+
+       if ((sc->sc_flags & AMDCF_LOADED) == 0) {
+               error = EIO;
+               goto exit;
+       }
+
+       switch (xfer) {
+       case DIOCRLDINFO:
+               lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK);
+               amdcfgetdisklabel(dev, sc, lp, 0);
+               bcopy(lp, sc->sc_dk.dk_label, sizeof(*lp));
+               free(lp, M_TEMP, 0);
+               goto exit;
+
+       case DIOCGPDINFO:
+               amdcfgetdisklabel(dev, sc, (struct disklabel *)addr, 1);
+               goto exit;
+
+       case DIOCGDINFO:
+               *(struct disklabel *)addr = *(sc->sc_dk.dk_label);
+               goto exit;
+
+       case DIOCGPART:
+               ((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
+               ((struct partinfo *)addr)->part =
+                   &sc->sc_dk.dk_label->d_partitions[DISKPART(dev)];
+               goto exit;
+
+       case DIOCWDINFO:
+       case DIOCSDINFO:
+               if ((flag & FWRITE) == 0) {
+                       error = EBADF;
+                       goto exit;
+               }
+
+               if ((error = disk_lock(&sc->sc_dk)) != 0)
+                       goto exit;
+
+               error = setdisklabel(sc->sc_dk.dk_label,
+                   (struct disklabel *)addr, sc->sc_dk.dk_openmask);
+               if (error == 0) {
+                       if (xfer == DIOCWDINFO)
+                               error = writedisklabel(DISKLABELDEV(dev),
+                                   amdcfstrategy, sc->sc_dk.dk_label);
+               }
+
+               disk_unlock(&sc->sc_dk);
+               goto exit;
+
+#ifdef notyet
+       case DIOCWFORMAT:
+               if ((flag & FWRITE) == 0)
+                       return EBADF;
+               {
+               struct format_op *fop;
+               struct iovec aiov;
+               struct uio auio;
+
+               fop = (struct format_op *)addr;
+               aiov.iov_base = fop->df_buf;
+               aiov.iov_len = fop->df_count;
+               auio.uio_iov = &aiov;
+               auio.uio_iovcnt = 1;
+               auio.uio_resid = fop->df_count;
+               auio.uio_segflg = 0;
+               auio.uio_offset =
+                       fop->df_startblk * sc->sc_dk.dk_label->d_secsize;
+               auio.uio_procp = p;
+               error = physio(scformat, dev, B_WRITE, minphys, &auio);
+               fop->df_count -= auio.uio_resid;
+               fop->df_reg[0] = scc->sc_status;
+               fop->df_reg[1] = scc->sc_error;
+               goto exit;
+               }
+#endif
+
+       default:
+               error = ENOTTY;
+               goto exit;
+       }
+
+#ifdef DIAGNOSTIC
+       panic("amdcfioctl: impossible");
+#endif
+
+ exit:
+       device_unref(&sc->sc_dev);
+       return error;
+}
+
+/*
+ * Dump core after a system crash.
+ */
+int
+amdcfdump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
+{
+       return ENXIO;
+}
+
+daddr_t
+amdcfsize(dev_t dev)
+{
+       struct amdcf_softc *sc;
+       struct disklabel *lp;
+       int part, omask;
+       daddr_t size;
+
+       sc = amdcflookup(DISKUNIT(dev));
+       if (sc == NULL)
+               return (-1);
+
+       part = DISKPART(dev);
+       omask = sc->sc_dk.dk_openmask & (1 << part);
+
+       if (omask == 0 && amdcfopen(dev, 0, S_IFBLK, NULL) != 0) {
+               size = -1;
+               goto exit;
+       }
+
+       lp = sc->sc_dk.dk_label;
+       size = DL_SECTOBLK(lp, DL_GETPSIZE(&lp->d_partitions[part]));
+       if (omask == 0 && amdcfclose(dev, 0, S_IFBLK, NULL) != 0)
+               size = -1;
+
+ exit:
+       device_unref(&sc->sc_dev);
+       return size;
+}
+
+
+/*
+ * Queue a drive for I/O.
+ */
+void
+amdcfstart(void *arg)
+{
+       struct amdcf_softc *sc = arg;
+       struct buf *bp;
+
+       while ((bp = bufq_dequeue(&sc->sc_bufq)) != NULL) {
+               /* Transfer this buffer now. */
+               _amdcfstart(sc, bp);
+       }
+}
+
+void
+_amdcfstart(struct amdcf_softc *sc, struct buf *bp)
+{
+       off_t off;
+       struct partition *p;
+
+       sc->sc_bp = bp;
+
+       /* Fetch buffer's read/write offset */
+       p = &sc->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)];
+       off = DL_GETPOFFSET(p) * sc->sc_dk.dk_label->d_secsize +
+           (u_int64_t)bp->b_blkno * DEV_BSIZE;
+       if (off > sc->sc_size) {
+               bp->b_flags |= B_ERROR;
+               bp->b_error = EIO;
+               return;
+       }
+
+       /* Instrumentation. */
+       disk_busy(&sc->sc_dk);
+
+       if (bp->b_flags & B_READ)
+               amdcf_disk_read(sc, bp, off);
+#ifdef AMDCF_DISK_WRITE_ENABLE
+       else
+               amdcf_disk_write(sc, bp, off);
+#endif
+
+       amdcfdone(sc);
+}
+
+void
+amdcfdone(void *arg)
+{
+       struct amdcf_softc *sc = arg;
+       struct buf *bp = sc->sc_bp;
+
+       if (bp->b_error == 0)
+               bp->b_resid = 0;
+       else
+               bp->b_flags |= B_ERROR;
+
+       disk_unbusy(&sc->sc_dk, (bp->b_bcount - bp->b_resid),
+           (bp->b_flags & B_READ));
+       biodone(bp);
+}
+
+void
+amdcf_disk_read(struct amdcf_softc *sc, struct buf *bp, off_t off)
+{
+       long resid;
+
+       if (sc->sc_writing) {
+               bp->b_error = cfi_block_finish(sc);
+               if (bp->b_error) {
+                       bp->b_flags |= B_ERROR;
+                       return;
+               }
+       }
+
+       resid = bp->b_bcount;
+       uint8_t *dp = (uint8_t *)bp->b_data;
+       while (resid > 0 && off < sc->sc_size) {
+               *dp++ = cfi_read(sc, off, 0);
+               off += 1, resid -= 1;
+       }
+       bp->b_resid = resid;
+}
+
+void
+amdcf_disk_write(struct amdcf_softc *sc, struct buf *bp, off_t off)
+{
+       long resid;
+       u_int top;
+
+       resid = bp->b_bcount;
+       while (resid > 0) {
+               /*
+                * Finish the current block if we're about to write
+                * to a different block.
+                */
+               if (sc->sc_writing) {
+                       top = sc->sc_wrofs + sc->sc_wrbufsz;
+                       if (off < sc->sc_wrofs || off >= top)
+                               cfi_block_finish(sc);
+               }
+
+               /* Start writing to a (new) block if applicable. */
+               if (!sc->sc_writing) {
+                       bp->b_error = cfi_block_start(sc, off);
+                       if (bp->b_error) {
+                               bp->b_flags |= B_ERROR;
+                               return;
+                       }
+               }
+
+               top = sc->sc_wrofs + sc->sc_wrbufsz;
+               bcopy(bp->b_data,
+                   sc->sc_wrbuf + off - sc->sc_wrofs,
+                   MIN(top - off, resid));
+               resid -= MIN(top - off, resid);
+       }
+       bp->b_resid = resid;
+}
+
+/*
+ * Begin writing into a new block/sector.  We read the sector into
+ * memory and keep updating that, until we move into another sector
+ * or the process stops writing. At that time we write the whole
+ * sector to flash (see cfi_block_finish).
+ */
+int
+cfi_block_start(struct amdcf_softc *sc, u_int ofs)
+{
+        u_int rofs, rsz;
+        int r;
+       uint8_t *ptr;
+
+        rofs = 0;
+        for (r = 0; r < sc->sc_regions; r++) {
+                rsz = sc->sc_region[r].r_blocks * sc->sc_region[r].r_blksz;
+                if (ofs < rofs + rsz)
+                        break;
+                rofs += rsz;
+        }
+        if (r == sc->sc_regions)
+                return (EFAULT);
+
+        sc->sc_wrbufsz = sc->sc_region[r].r_blksz;
+        sc->sc_wrbuf = malloc(sc->sc_wrbufsz, M_TEMP, M_WAITOK);
+        sc->sc_wrofs = ofs - (ofs - rofs) % sc->sc_wrbufsz;
+
+       ptr = sc->sc_wrbuf;
+        /* Read the block from flash for byte-serving. */
+        for (r = 0; r < sc->sc_wrbufsz; r++)
+                *(ptr)++ = cfi_read(sc, sc->sc_wrofs + r, 0);
+
+        sc->sc_writing = 1;
+        return (0);
+}
+
+/*
+ * Finish updating the current block/sector by writing the compound
+ * set of changes to the flash.
+ */
+int
+cfi_block_finish(struct amdcf_softc *sc)
+{
+       int error;
+
+       error = cfi_write_block(sc);
+       free(sc->sc_wrbuf, M_TEMP, sc->sc_wrbufsz);
+       sc->sc_wrbuf = NULL;
+       sc->sc_wrbufsz = 0;
+       sc->sc_wrofs = 0;
+       sc->sc_writing = 0;
+       return (error);
+}
+
+int
+cfi_write_block(struct amdcf_softc *sc)
+{
+       uint8_t *ptr;
+       int error, i, s;
+
+       if (sc->sc_wrofs > sc->sc_size)
+               panic("CFI: write offset (%x) bigger "
+                   "than cfi array size (%zu)\n",
+                   sc->sc_wrofs, sc->sc_size);
+
+       if ((sc->sc_wrofs < BOOTLOADER_ADDR) ||
+           ((sc->sc_wrofs + sc->sc_wrbufsz) < BOOTLOADER_ADDR))
+               return EOPNOTSUPP;
+
+       error = cfi_erase_block(sc, sc->sc_wrofs);
+       if (error)
+               goto out;
+
+       /* Write the block. */
+       ptr = sc->sc_wrbuf;
+
+       for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) {
+
+               /*
+                * Make sure the command to start a write and the
+                * actual write happens back-to-back without any
+                * excessive delays.
+                */
+               s = splbio();
+
+               cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START,
+                           CFI_AMD_PROGRAM);
+               /* Raw data do not use cfi_array_write */
+               cfi_write(sc, sc->sc_wrofs + i, 0, *(ptr)++);
+
+               splx(s);
+
+               error = cfi_wait_ready(sc, sc->sc_wrofs + i,
+                   sc->sc_write_timeout, sc->sc_write_max_timeout);
+               if (error)
+                       goto out;
+       }
+
+out:
+       cfi_array_write(sc, sc->sc_wrofs, 0, sc->sc_rstcmd);
+       return error;
+}
+
+int
+cfi_erase_block(struct amdcf_softc *sc, u_int offset)
+{
+        int error = 0;
+
+        if (offset > sc->sc_size)
+                panic("CFI: erase offset (%x) bigger "
+                    "than cfi array size (%zu)\n",
+                    sc->sc_wrofs, sc->sc_size);
+
+        /* Erase the block. */
+       cfi_amd_write(sc, offset, 0, CFI_AMD_BLOCK_ERASE);
+
+        error = cfi_wait_ready(sc, offset, sc->sc_erase_timeout,
+            sc->sc_erase_max_timeout);
+
+        return error;
+}
+
+
+
+int
+cfi_wait_ready(struct amdcf_softc *sc, u_int ofs, u_int timeout, u_int count)
+{
+       int done, error;
+       u_int st0 = 0, st = 0;
+
+       done = 0;
+       error = 0;
+
+       if (!timeout)
+               timeout = 100;  /* Default to 100 uS */
+       if (!count)
+               count = 100;    /* Max timeout is 10 mS */
+
+       while (!done && !error && count) {
+               DELAY(timeout);
+
+               count--;
+
+               /*
+                * read sc->sc_width bytes, and check for toggle bit.
+                */
+               st0 = cfi_read(sc, ofs, 0);
+               st = cfi_read(sc, ofs, 0);
+               done = ((st & cfi_make_cmd(0x40, sc->sc_mask)) ==
+                   (st0 & cfi_make_cmd(0x40, sc->sc_mask))) ? 1 : 0;
+
+               break;
+       }
+       if (!done && !error)
+               error = ETIMEDOUT;
+       if (error)
+               printf("\nerror=%d (st 0x%x st0 0x%x) at offset=%x\n",
+                   error, st, st0, ofs);
+       return error;
+}
+
+/*
+ * cfi_array_write
+ * fill "bus width" word with value of var data by array mask sc->sc_mask
+ */
+void
+cfi_array_write(struct amdcf_softc *sc, u_int ofs, u_int addr, u_int data)
+{
+       data &= 0xff;
+       cfi_write(sc, ofs, addr, cfi_make_cmd(data, sc->sc_mask));
+}
+
+void
+cfi_amd_write(struct amdcf_softc *sc, u_int ofs, u_int addr, u_int data)
+{
+        cfi_array_write(sc, ofs, AMD_ADDR_START, CFI_AMD_UNLOCK);
+        cfi_array_write(sc, ofs, AMD_ADDR_ACK, CFI_AMD_UNLOCK_ACK);
+        cfi_array_write(sc, ofs, addr, data);
+}
+
+
+
+/*
+ * The following routines assume width=1 and shift=2 as that is
+ * the case on the Octeon DSR machines.
+ * If this assumption fails a new detection routine should be written
+ * and called during attach.
+ */
+uint8_t
+cfi_read(struct amdcf_softc *sc, bus_size_t base, bus_size_t offset)
+{
+       return bus_space_read_1(sc->sc_iot, sc->sc_ioh,
+           base | (offset * sc->sc_shift));
+}
+
+void
+cfi_write(struct amdcf_softc *sc, bus_size_t base, bus_size_t offset,
+    uint8_t val)
+{
+       bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+           base | (offset * sc->sc_shift), val);
+}
+
+int
+cfi_make_cmd(uint8_t cmd, u_int mask)
+{
+       int i;
+       u_int data = 0;
+
+       for (i = 0; i < sizeof(int); i ++) {
+               if (mask & (0xff << (i*8)))
+                       data |= cmd << (i*8);
+       }
+
+       return data;
+}
Index: include/octeonreg.h
===================================================================
RCS file: /cvs/src/sys/arch/octeon/include/octeonreg.h,v
retrieving revision 1.4
diff -u -p -r1.4 octeonreg.h
--- include/octeonreg.h 17 Jun 2014 01:33:04 -0000      1.4
+++ include/octeonreg.h 20 Jul 2015 18:51:58 -0000
@@ -36,6 +36,7 @@
 #define OCTEON_UART0_BASE      0x1180000000800ULL
 #define OCTEON_UART1_BASE      0x1180000000C00ULL
 #define OCTEON_RNG_BASE                0x1400000000000ULL
+#define OCTEON_AMDCF_BASE      0x1dc00000ULL
 
 #define MIO_BOOT_REG_CFG0      0x0
 #define MIO_BOOT_REG_CFG(x)    (MIO_BOOT_REG_CFG0+((x)*8))
Index: include/octeonvar.h
===================================================================
RCS file: /cvs/src/sys/arch/octeon/include/octeonvar.h,v
retrieving revision 1.23
diff -u -p -r1.23 octeonvar.h
--- include/octeonvar.h 15 Jul 2015 23:22:40 -0000      1.23
+++ include/octeonvar.h 20 Jul 2015 18:51:58 -0000
@@ -199,6 +199,7 @@ struct octeon_fau_map {
  */
 #define        BOARD_TYPE_UBIQUITI_E100        20002
 #define        BOARD_TYPE_UBIQUITI_E200        20003
+#define        BOARD_TYPE_DSR_500              20015
 
 #if defined(_KERNEL) || defined(_STANDALONE)
 #define OCTEON_ARGV_MAX 64
Index: octeon/autoconf.c
===================================================================
RCS file: /cvs/src/sys/arch/octeon/octeon/autoconf.c,v
retrieving revision 1.6
diff -u -p -r1.6 autoconf.c
--- octeon/autoconf.c   30 Jun 2015 06:10:21 -0000      1.6
+++ octeon/autoconf.c   20 Jul 2015 18:51:58 -0000
@@ -93,7 +93,8 @@ device_register(struct device *dev, void
        case DV_DISK:
                if ((strcmp(drvrname, "wd") == 0 ||
                    strcmp(drvrname, "sd") == 0 ||
-                   strcmp(drvrname, "octcf") == 0) &&
+                   strcmp(drvrname, "octcf") == 0 ||
+                   strcmp(drvrname, "amdcf") == 0) &&
                    strcmp(name, bootdev) == 0)
                        bootdv = dev;
                break;
@@ -116,5 +117,6 @@ struct nam2blk nam2blk[] = {
        { "rd",         8 },
        { "vnd",        2 },
        { "octcf",      15 },
+       { "amdcf",      19 },
        { NULL,         -1 }
 };
Index: octeon/conf.c
===================================================================
RCS file: /cvs/src/sys/arch/octeon/octeon/conf.c,v
retrieving revision 1.14
diff -u -p -r1.14 conf.c
--- octeon/conf.c       4 May 2015 21:45:23 -0000       1.14
+++ octeon/conf.c       20 Jul 2015 18:51:58 -0000
@@ -62,6 +62,9 @@ bdev_decl(wd);
 #define NOCTCF 1
 bdev_decl(octcf);
 
+#define NAMDCF 1
+bdev_decl(amdcf);
+
 struct bdevsw  bdevsw[] =
 {
        bdev_disk_init(NSD,sd),         /* 0: SCSI disk */
@@ -80,6 +83,10 @@ struct bdevsw        bdevsw[] =
        bdev_notdef(),                  /* 13:  */
        bdev_notdef(),                  /* 14:  */
        bdev_disk_init(NOCTCF,octcf),   /* 15: CF disk */
+       bdev_notdef(),                  /* 16:  */
+       bdev_notdef(),                  /* 17:  */
+       bdev_notdef(),                  /* 18:  */
+       bdev_disk_init(NAMDCF,amdcf),   /* 19: CF disk */
 };
 
 int    nblkdev = nitems(bdevsw);
@@ -111,6 +118,7 @@ cdev_decl(wd);
 #include "audio.h"
 #include "video.h"
 cdev_decl(octcf);
+cdev_decl(amdcf);
 
 #include "ksyms.h"
 
@@ -159,7 +167,7 @@ struct cdevsw       cdevsw[] =
        cdev_lpt_init(NLPT,lpt),        /* 16: Parallel printer interface */
        cdev_tty_init(NCOM,com),        /* 17: 16C450 serial interface */
        cdev_disk_init(NWD,wd),         /* 18: ST506/ESDI/IDE disk */
-       cdev_notdef(),                  /* 19: */
+       cdev_disk_init(NAMDCF,amdcf),   /* 19: CF disk */
        cdev_notdef(),                  /* 20: */
        cdev_notdef(),                  /* 21: */
        cdev_disk_init(NRD,rd),         /* 22: ramdisk device */
@@ -285,7 +293,7 @@ int chrtoblktbl[] =  {
        /* 16 */        NODEV,
        /* 17 */        NODEV,
        /* 18 */        4,              /* wd */
-       /* 19 */        NODEV,
+       /* 19 */        19,             /* amdcf */
        /* 20 */        NODEV,
        /* 21 */        NODEV,
        /* 22 */        8               /* rd */

Reply via email to