Page MenuHomeFreeBSD

D24467.diff
No OneTemporary

D24467.diff

Index: sys/arm64/conf/GENERIC
===================================================================
--- sys/arm64/conf/GENERIC
+++ sys/arm64/conf/GENERIC
@@ -293,6 +293,7 @@
device spibus
device a37x0_spi # Marvell Armada 37x0 SPI Controller
device bcm2835_spi # Broadcom BCM283x SPI bus
+device fsl_qspi
device rk_spi # RockChip SPI controller
# PWM
Index: sys/arm64/qoriq/fsl_qspi.h
===================================================================
--- /dev/null
+++ sys/arm64/qoriq/fsl_qspi.h
@@ -0,0 +1,295 @@
+/*-
+ * Copyright (c) 2020 Alstom Group.
+ * Copyright (c) 2020 Semihalf.
+ *
+ * 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.
+ */
+
+#ifndef FSL_QSPI_H
+#define FSL_QSPI_H
+
+#define TSTATE_STOPPED 0
+#define TSTATE_STOPPING 1
+#define TSTATE_RUNNING 2
+
+/*
+ * LUT commands
+ */
+#define LUT_STOP 0
+#define LUT_CMD 1
+#define LUT_ADDR 2
+#define LUT_DUMMY 3
+#define LUT_MODE 4
+#define LUT_MODE2 5
+#define LUT_MODE4 6
+#define LUT_READ 7
+#define LUT_WRITE 8
+#define LUT_JMP_ON_CS 9
+#define LUT_ADDR_DDR 10
+#define LUT_MODE_DDR 11
+#define LUT_MODE2_DDR 12
+#define LUT_MODE4_DDR 13
+#define LUT_FSL_READ_DDR 14
+#define LUT_FSL_WRITE_DDR 15
+#define LUT_DATA_LEARN 16
+
+/* LUT addr sizes */
+#define LUT_ADDR24BIT 0x18
+#define LUT_ADDR32BIT 0x20
+
+/*
+ * LUT SEQIDs
+ */
+#define SEQID_WREN 1
+#define SEQID_FAST_READ 2
+#define SEQID_RDSR 3
+#define SEQID_SE 4
+#define SEQID_PP 6
+#define SEQID_RDID 7
+
+/*
+ * Commands
+ */
+#define QSPI_CMD_WRITE_ENABLE 0x06
+#define QSPI_CMD_WRITE_DISABLE 0x04
+#define QSPI_CMD_READ_IDENT 0x9F
+#define QSPI_CMD_READ_STATUS 0x05
+#define QSPI_CMD_WRITE_STATUS 0x01
+#define QSPI_CMD_READ 0x03
+#define QSPI_CMD_FAST_READ 0x0B
+#define QSPI_CMD_PAGE_PROGRAM 0x02
+#define QSPI_CMD_SECTOR_ERASE 0xD8
+#define QSPI_CMD_BULK_ERASE 0xC7
+#define QSPI_CMD_BLOCK_4K_ERASE 0x20
+#define QSPI_CMD_BLOCK_32K_ERASE 0x52
+#define QSPI_CMD_ENTER_4B_MODE 0xB7
+#define QSPI_CMD_EXIT_4B_MODE 0xE9
+#define QSPI_CMD_READ_CTRL_REG 0x35
+#define QSPI_CMD_BANK_REG_WRITE 0x17 /* (spansion) */
+#define QSPI_CMD_SECTOR_ERASE_4B 0xDC
+#define QSPI_CMD_BLOCK_4K_ERASE_4B 0x21
+#define QSPI_CMD_BLOCK_32K_ERASE_4B 0x5C
+#define QSPI_CMD_PAGE_PROGRAM_4B 0x12
+#define QSPI_CMD_FAST_READ_4B 0x0C
+
+/*
+ * Status register flags
+ */
+#define STATUS_SRWD (1 << 7)
+#define STATUS_BP2 (1 << 4)
+#define STATUS_BP1 (1 << 3)
+#define STATUS_BP0 (1 << 2)
+#define STATUS_WEL (1 << 1)
+#define STATUS_WIP (1 << 0)
+
+/* Based on values from u-boot. */
+#define AHB_PREFETCH_SIZE 0x80
+#define TX_BURST_SIZE 0x40
+
+#define FLASH_PAGE_SIZE 256
+
+#define FL_NONE 0x00
+#define FL_ERASE_4K 0x01
+#define FL_ERASE_32K 0x02
+#define FL_ENABLE_4B_ADDR 0x04
+#define FL_DISABLE_4B_ADDR 0x08
+
+/*
+ * Define the sectorsize to be a smaller size rather than the flash
+ * sector size. Trying to run FFS off of a 64k flash sector size
+ * results in a completely un-usable system.
+ */
+#define FLASH_SECTORSIZE 512
+
+#define LUT_PAD1 0
+#define LUT_PAD2 1
+#define LUT_PAD4 2
+
+#define OPRND0_SHIFT 0
+#define OPRND0(x) ((x) << OPRND0_SHIFT)
+#define PAD0_SHIFT 8
+#define PAD0(x) ((x) << PAD0_SHIFT)
+#define INSTR0_SHIFT 10
+#define INSTR0(x) ((x) << INSTR0_SHIFT)
+#define OPRND1_SHIFT 16
+#define OPRND1(x) ((x) << OPRND1_SHIFT)
+#define PAD1_SHIFT 24
+#define PAD1(x) ((x) << PAD1_SHIFT)
+#define INSTR1_SHIFT 26
+#define INSTR1(x) ((x) << INSTR1_SHIFT)
+
+#define QSPI_DEFAULT_SPI_CLOCK 10000000
+
+/* equivalent to ALIGN_DOWN in Linux */
+#define ALIGN_DOWN(x, a) ((x) & -(a))
+
+/* Device registers */
+#define QSPI_MCR 0x0000
+#define QSPI_MCR_RSV (0xf << 16)
+#define QSPI_MCR_MDIS (1 << 14)
+#define QSPI_MCR_CLR_TXF (1 << 11)
+#define QSPI_MCR_CLR_RXF (1 << 10)
+#define QSPI_MCR_DATA_BE (0x3 << 2) /* Switch data endianess */
+#define QSPI_MCR_SWRSTHD (1 << 1)
+#define QSPI_MCR_SWRSTSD (1 << 0)
+
+#define QSPI_IPCR 0x0008
+#define QSPI_IPCR_SEQID(x) ((x) << 24)
+
+#define QSPI_BUF0CR 0x0010
+#define QSPI_BUF1CR 0x0014
+#define QSPI_BUF2CR 0x0018
+#define QSPI_BUF3CR 0x001C
+#define QSPI_BUFXCR_INVALID_ID 0xE
+#define QSPI_BUFXCR_ADATSZ(x) ((x) << 8)
+#define QSPI_BUFXCR_ADATSZ_MASK (0xff << 8)
+#define QSPI_BUFXCR_HPEN (1 << 31)
+
+#define QSPI_BFGENCR 0x20
+#define QSPI_BFGENCR_SEQID(x) ((x) << 16)
+
+#define QSPI_BUF0INDR 0x0030
+#define QSPI_BUF1INDR 0x0034
+#define QSPI_BUF2INDR 0x0038
+#define QSPI_SFAR 0x0100
+
+#define QSPI_SMPR 0x0108
+#define QSPI_SMPR_DDRSMP (0x7 << 16)
+#define QSPI_SMPR_FSDLY (1 << 6)
+#define QSPI_SMPR_FSPHS (1 << 5)
+#define QSPI_SMPR_HSENA (1 << 0)
+
+#define QSPI_RBSR 0x10C
+#define QSPI_RBSR_RDBFL_SHIFT 8 /* The amount of bytes in RX buffer */
+#define QSPI_RBSR_RDBFL_MASK (0x3f << QSPI_RBSR_RDBFL_SHIFT)
+
+#define QSPI_RBCR 0x0110
+#define QSPI_RBCR_WMRK 0x100
+#define QSPI_RBCR_RXBRD_USEIPS (1 << 8)
+
+#define QSPI_TBSR 0x0150
+
+#define QSPI_TBDR 0x0154
+
+#define QSPI_SR 0x015C
+#define QSPI_SR_BUSY (1 << 0)
+#define QSPI_SR_IP_ACC (1 << 1)
+#define QSPI_SR_AHB_ACC (1 << 2)
+#define QSPI_SR_DEV_BUSY (QSPI_SR_BUSY | QSPI_SR_IP_ACC | QSPI_SR_AHB_ACC)
+
+#define QSPI_FR 0x0160
+#define QSPI_FR_TFF (1 << 0)
+#define QSPI_FR_TBUF (1 << 27)
+
+#define QSPI_RSER 0x0164
+#define QSPI_RSER_TFIE (1 << 0)
+
+#define QSPI_SPTRCLR 0x016C
+#define QSPI_SPTRCLR_IPPTRC (1 << 8)
+#define QSPI_SPTRCLR_BFPTRC (1 << 0)
+
+#define QSPI_SFA1AD 0x0180
+#define QSPI_SFA2AD 0x0184
+#define QSPI_SFB1AD 0x0188
+#define QSPI_SFB2AD 0x018C
+#define QSPI_RBDR(n) (0x0200 + n * 0x0004)
+
+#define QSPI_LUTKEYR 0x0300
+#define QSPI_LUTKEY_VALUE 0x5AF05AF0
+
+#define QSPI_LCKCR 0x0304
+#define QSPI_LCKER_LOCK (1 << 0)
+#define QSPI_LCKER_UNLOCK (1 << 1)
+
+#define QSPI_LUT_BASE 0x310
+#define QSPI_LUT_REG(idx) (QSPI_LUT_BASE + (idx) * 4)
+
+#define QSPI_RBDR_MAX 31
+#define QSPI_LUT_MAX 63
+
+struct fsl_qspi_flash_ident
+{
+ const char *name;
+ uint8_t manufacturer_id;
+ uint16_t device_id;
+ unsigned int sectorsize;
+ unsigned int sectorcount;
+ unsigned int flags;
+};
+
+struct fsl_qspi_softc
+{
+ device_t dev;
+ struct mtx dev_mtx;
+ unsigned int erasesize;
+ unsigned int flags;
+
+ struct bio_queue_head bio_queue;
+ struct mtx disk_mtx;
+ struct disk *disk;
+ struct proc *p;
+ unsigned int taskstate;
+
+ struct resource *ahb_mem_res;
+ struct resource *mem_res;
+
+ uint64_t ahb_phys;
+};
+
+static struct fsl_qspi_flash_ident flash_devices[] = {
+ { "en25f32", 0x1c, 0x3116, 64 * 1024, 64, FL_NONE },
+ { "en25p32", 0x1c, 0x2016, 64 * 1024, 64, FL_NONE },
+ { "en25p64", 0x1c, 0x2017, 64 * 1024, 128, FL_NONE },
+ { "en25q32", 0x1c, 0x3016, 64 * 1024, 64, FL_NONE },
+ { "en25q64", 0x1c, 0x3017, 64 * 1024, 128, FL_ERASE_4K },
+ { "m25p32", 0x20, 0x2016, 64 * 1024, 64, FL_NONE },
+ { "m25p64", 0x20, 0x2017, 64 * 1024, 128, FL_NONE },
+ { "fsl_qspil32", 0xc2, 0x2016, 64 * 1024, 64, FL_NONE },
+ { "fsl_qspil64", 0xc2, 0x2017, 64 * 1024, 128, FL_NONE },
+ { "fsl_qspil128", 0xc2, 0x2018, 64 * 1024, 256, FL_ERASE_32K },
+ { "fsl_qspil256", 0xc2, 0x2019, 64 * 1024, 512,
+ FL_ERASE_4K | FL_ERASE_32K | FL_ENABLE_4B_ADDR },
+ { "s25fl032", 0x01, 0x0215, 64 * 1024, 64, FL_NONE },
+ { "s25fl064", 0x01, 0x0216, 64 * 1024, 128, FL_NONE },
+ { "s25fl128", 0x01, 0x2018, 64 * 1024, 256, FL_NONE },
+ { "s25fl256s", 0x01, 0x0219, 64 * 1024, 512, FL_NONE },
+ { "s25fs512s", 0x01, 0x0220, 256 * 1024, 1024, FL_NONE },
+ { "SST25VF010A", 0xbf, 0x2549, 4 * 1024, 32, FL_ERASE_32K },
+ { "SST25VF032B", 0xbf, 0x254a, 64 * 1024, 64, FL_ERASE_32K },
+
+ /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
+ { "w25x32", 0xef, 0x3016, 64 * 1024, 64, FL_ERASE_4K },
+ { "w25x64", 0xef, 0x3017, 64 * 1024, 128, FL_ERASE_4K },
+ { "w25q32", 0xef, 0x4016, 64 * 1024, 64, FL_ERASE_4K },
+ { "w25q64", 0xef, 0x4017, 64 * 1024, 128, FL_ERASE_4K },
+ { "w25q64bv", 0xef, 0x4017, 64 * 1024, 128, FL_ERASE_4K },
+ { "w25q128", 0xef, 0x4018, 64 * 1024, 256, FL_ERASE_4K },
+ { "w25q256", 0xef, 0x4019, 64 * 1024, 512, FL_ERASE_4K },
+ { "w25q256jw", 0xef, 0x6019, 64 * 1024, 512, FL_NONE },
+ { "w25q256jw-im", 0xef, 0x8019, 64 * 1024, 512, FL_NONE },
+
+ /* Atmel */
+ { "at25df641", 0x1f, 0x4800, 64 * 1024, 128, FL_ERASE_4K },
+
+ /* GigaDevice */
+ { "gd25q64", 0xc8, 0x4017, 64 * 1024, 128, FL_ERASE_4K },
+};
+#endif /* FSL_QSPI_H */
Index: sys/arm64/qoriq/fsl_qspi.c
===================================================================
--- /dev/null
+++ sys/arm64/qoriq/fsl_qspi.c
@@ -0,0 +1,746 @@
+/*-
+ * Copyright (c) 2006 M. Warner Losh. All rights reserved.
+ * Copyright (c) 2009 Oleksandr Tymoshenko. All rights reserved.
+ * Copyright (c) 2018 Ian Lepore. All rights reserved.
+ * Copyright (c) 2020 Alstom Group.
+ * Copyright (c) 2020 Semihalf.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bio.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+
+#include <geom/geom_disk.h>
+
+#include <machine/bus.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <vm/pmap.h>
+
+#include "fsl_qspi.h"
+
+MALLOC_DEFINE(SECTOR_BUFFER, "FSL_QSPI", "FSL QSPI sector buffer memory");
+
+static uint32_t read_reg_le(struct fsl_qspi_softc *sc, uint32_t offset);
+static uint32_t read_reg_be(struct fsl_qspi_softc *sc, uint32_t offset);
+static void write_reg_be(struct fsl_qspi_softc *sc, uint32_t offset,
+ uint32_t value);
+
+static void fsl_qspi_init_lut(struct fsl_qspi_softc *sc, bool use_4b_cmds, int);
+static int fsl_qspi_init_hw(struct fsl_qspi_softc *sc);
+static struct fsl_qspi_flash_ident*
+fsl_qspi_get_device_ident(struct fsl_qspi_softc *sc);
+static void fsl_qspi_invalidate_ahb(struct fsl_qspi_softc *sc);
+static void fsl_qspi_wait_for_controller(struct fsl_qspi_softc *sc);
+static void fsl_qspi_wait_for_flash(struct fsl_qspi_softc *sc);
+static int fsl_qspi_read(struct fsl_qspi_softc *sc, off_t offset, caddr_t data,
+ size_t count);
+static int fsl_qspi_write_enable(struct fsl_qspi_softc *sc);
+static void fsl_qspi_erase(struct fsl_qspi_softc *sc, off_t offset);
+static void fsl_qspi_write_burst(struct fsl_qspi_softc *sc, size_t offs,
+ uint8_t *data, size_t size);
+static void fsl_qspi_write_sector(struct fsl_qspi_softc *sc, off_t sector_offset,
+ uint8_t *data);
+static int fsl_qspi_write(struct fsl_qspi_softc *sc, off_t offset, uint8_t *data,
+ size_t size);
+
+static int fsl_qspi_attach(device_t dev);
+static int fsl_qspi_probe(device_t dev);
+static int fsl_qspi_detach(device_t dev);
+
+/* disk routines */
+static int fsl_qspi_open(struct disk *dp);
+static int fsl_qspi_close(struct disk *dp);
+static int fsl_qspi_ioctl(struct disk *, u_long, void *, int, struct thread *);
+static void fsl_qspi_strategy(struct bio *bp);
+static int fsl_qspi_getattr(struct bio *bp);
+static void fsl_qspi_task(void *arg);
+
+static uint32_t
+read_reg_le(struct fsl_qspi_softc *sc, uint32_t offset)
+{
+
+ return (bus_read_4(sc->mem_res, offset));
+}
+
+static uint32_t
+read_reg_be(struct fsl_qspi_softc *sc, uint32_t offset)
+{
+
+ return (be32toh(bus_read_4(sc->mem_res, offset)));
+}
+
+static void
+write_reg_be(struct fsl_qspi_softc *sc, uint32_t offset, uint32_t value)
+{
+
+ bus_write_4(sc->mem_res, offset, htobe32(value));
+}
+
+static void
+fsl_qspi_init_lut(struct fsl_qspi_softc *sc, bool use_4b_cmds, int erase_cmd)
+{
+ uint32_t lut_id;
+ int qspi_cmd;
+
+ /*
+ * Lookup table for all flash commands we will use.
+ * The following sequences are programmed:
+ * ID reading, erasing, fast reading(for AHB), status reading and writing.
+ * Each sequence can have up to 4 commands.
+ * Set the next cmd in sequence to 0 to indicate that
+ * we only do a single one.
+ */
+
+ /* unlock LUT */
+ write_reg_be(sc, QSPI_LUTKEYR, QSPI_LUTKEY_VALUE);
+ write_reg_be(sc, QSPI_LCKCR, QSPI_LCKER_UNLOCK);
+
+ /* Write enable */
+ lut_id = 4 * SEQID_WREN;
+ write_reg_be(sc, QSPI_LUT_REG(lut_id),
+ (OPRND0(QSPI_CMD_WRITE_ENABLE) | PAD0(LUT_PAD1) | INSTR0(LUT_CMD)));
+ write_reg_be(sc, QSPI_LUT_REG(lut_id + 1), 0);
+
+ if (use_4b_cmds)
+ qspi_cmd = QSPI_CMD_FAST_READ_4B;
+ else
+ qspi_cmd = QSPI_CMD_FAST_READ;
+
+ /* Fast read (for AHB) */
+ lut_id = 4 * SEQID_FAST_READ;
+ write_reg_be(sc, QSPI_LUT_REG(lut_id),
+ (OPRND0(qspi_cmd) | PAD0(LUT_PAD1) | INSTR0(LUT_CMD)) |
+ (OPRND1(LUT_ADDR32BIT) | PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)));
+ write_reg_be(sc, QSPI_LUT_REG(lut_id + 1),
+ (OPRND0(8) | PAD0(LUT_PAD1) | INSTR0(LUT_DUMMY)) |
+ (OPRND1(AHB_PREFETCH_SIZE) | PAD1(LUT_PAD1) | INSTR1(LUT_READ)));
+ write_reg_be(sc, QSPI_LUT_REG(lut_id + 2), 0);
+
+ lut_id = 4 * SEQID_RDSR;
+ write_reg_be(sc, QSPI_LUT_REG(lut_id),
+ (OPRND0(QSPI_CMD_READ_STATUS) | PAD0(LUT_PAD1) | INSTR0(LUT_CMD)) |
+ (OPRND1(QSPI_CMD_WRITE_STATUS) | PAD1(LUT_PAD1) | INSTR1(LUT_READ)));
+ write_reg_be(sc, QSPI_LUT_REG(lut_id + 1), 0);
+
+ /* Erase a sector */
+ lut_id = 4 * SEQID_SE;
+ write_reg_be(sc, QSPI_LUT_REG(lut_id),
+ (OPRND0(erase_cmd) | PAD0(LUT_PAD1) | INSTR0(LUT_CMD)) |
+ (OPRND1(LUT_ADDR32BIT)) | PAD1(LUT_PAD1) | INSTR1(LUT_ADDR));
+ write_reg_be(sc, QSPI_LUT_REG(lut_id + 1), 0);
+
+ if (use_4b_cmds)
+ qspi_cmd = QSPI_CMD_PAGE_PROGRAM_4B;
+ else
+ qspi_cmd = QSPI_CMD_PAGE_PROGRAM;
+
+ lut_id = 4 * SEQID_PP;
+ write_reg_be(sc, QSPI_LUT_REG(lut_id),
+ (OPRND0(qspi_cmd) | PAD0(LUT_PAD1) | INSTR0(LUT_CMD)) |
+ (OPRND1(LUT_ADDR32BIT) | PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)));
+ write_reg_be(sc, QSPI_LUT_REG(lut_id + 1),
+ (OPRND0(TX_BURST_SIZE) | PAD0(LUT_PAD1) | INSTR0(LUT_WRITE)));
+ write_reg_be(sc, QSPI_LUT_REG(lut_id + 2), 0);
+
+ /* READ ID */
+ lut_id = 4 * SEQID_RDID;
+ write_reg_be(sc, QSPI_LUT_REG(lut_id),
+ (OPRND0(QSPI_CMD_READ_IDENT) | PAD0(LUT_PAD1) | INSTR0(LUT_CMD)) |
+ (OPRND1(8) | PAD1(LUT_PAD1) | INSTR1(LUT_READ)));
+ write_reg_be(sc, QSPI_LUT_REG(lut_id + 1), 0);
+
+ /* lock LUT */
+ write_reg_be(sc, QSPI_LUTKEYR, QSPI_LUTKEY_VALUE);
+ write_reg_be(sc, QSPI_LCKCR, QSPI_LCKER_LOCK);
+}
+
+static int
+fsl_qspi_init_hw(struct fsl_qspi_softc *sc)
+{
+ struct fsl_qspi_flash_ident *ident;
+ bool use_4b_cmds;
+ int erase_cmd;
+ uint32_t reg;
+
+ write_reg_be(sc, QSPI_MCR,
+ QSPI_MCR_MDIS | QSPI_MCR_RSV | QSPI_MCR_DATA_BE);
+
+ reg = read_reg_be(sc, QSPI_SMPR);
+ reg &= ~(QSPI_SMPR_FSDLY | QSPI_SMPR_DDRSMP | QSPI_SMPR_FSPHS |
+ QSPI_SMPR_HSENA);
+ write_reg_be(sc, QSPI_SMPR, reg);
+
+ use_4b_cmds = false;
+
+ /* Configure LUT to defaults before identification */
+ fsl_qspi_init_lut(sc, use_4b_cmds, QSPI_CMD_SECTOR_ERASE);
+ write_reg_be(sc, QSPI_MCR, read_reg_be(sc, QSPI_MCR) & ~QSPI_MCR_MDIS);
+
+ ident = fsl_qspi_get_device_ident(sc);
+ if (ident == NULL)
+ return (ENXIO);
+
+ /* Use 4B commands for devices larger than 16MB */
+ if ((ident->sectorsize * ident->sectorcount) > (16 * 1024 * 1024))
+ use_4b_cmds = true;
+
+ sc->flags = ident->flags;
+
+ if (sc->flags & FL_ERASE_4K ) {
+ sc->erasesize = 4 * 1024;
+ if (use_4b_cmds)
+ erase_cmd = QSPI_CMD_BLOCK_4K_ERASE_4B;
+ else
+ erase_cmd = QSPI_CMD_BLOCK_4K_ERASE;
+ }
+ else if (sc->flags & FL_ERASE_32K) {
+ sc->erasesize = 32 * 1024;
+ if (use_4b_cmds)
+ erase_cmd = QSPI_CMD_BLOCK_32K_ERASE_4B;
+ else
+ erase_cmd = QSPI_CMD_BLOCK_32K_ERASE;
+ }
+ else {
+ sc->erasesize = ident->sectorsize;
+ if (use_4b_cmds)
+ erase_cmd = QSPI_CMD_SECTOR_ERASE_4B;
+ else
+ erase_cmd = QSPI_CMD_SECTOR_ERASE;
+ }
+
+ /* Reconfigure LUT with updated parameters */
+ fsl_qspi_init_lut(sc, use_4b_cmds, erase_cmd);
+
+ strlcpy(sc->disk->d_descr, ident->name, sizeof(sc->disk->d_descr));
+ sc->disk->d_mediasize = ident->sectorsize * ident->sectorcount;
+ sc->disk->d_stripesize = sc->erasesize;
+
+ write_reg_be(sc, QSPI_MCR, read_reg_be(sc, QSPI_MCR) | QSPI_MCR_MDIS);
+
+ /* Point all buffers to the end of flashes. */
+ write_reg_be(sc, QSPI_SFA1AD, sc->ahb_phys + (ident->sectorsize * ident->sectorcount));
+ write_reg_be(sc, QSPI_SFA2AD, sc->ahb_phys + (ident->sectorsize * ident->sectorcount));
+ write_reg_be(sc, QSPI_SFB1AD, sc->ahb_phys + (ident->sectorsize * ident->sectorcount));
+ write_reg_be(sc, QSPI_SFA2AD, sc->ahb_phys + (ident->sectorsize * ident->sectorcount));
+
+ /* Disable buffers 0 - 2 as we only use buffer nr 3 */
+ write_reg_be(sc, QSPI_BUF0CR, QSPI_BUFXCR_INVALID_ID);
+ write_reg_be(sc, QSPI_BUF1CR, QSPI_BUFXCR_INVALID_ID);
+ write_reg_be(sc, QSPI_BUF2CR, QSPI_BUFXCR_INVALID_ID);
+ write_reg_be(sc, QSPI_BUF0INDR, 0);
+ write_reg_be(sc, QSPI_BUF1INDR, 0);
+ write_reg_be(sc, QSPI_BUF2INDR, 0);
+
+ /* Enable buffer 3, set high priority bit and pre-fetch size */
+ write_reg_be(sc, QSPI_BUF3CR,
+ QSPI_BUFXCR_HPEN | QSPI_BUFXCR_ADATSZ(AHB_PREFETCH_SIZE));
+ write_reg_be(sc, QSPI_BFGENCR, QSPI_BFGENCR_SEQID(SEQID_FAST_READ));
+
+ write_reg_be(sc, QSPI_MCR, read_reg_be(sc, QSPI_MCR) & ~QSPI_MCR_MDIS);
+
+ return (0);
+}
+
+static struct fsl_qspi_flash_ident *
+fsl_qspi_get_device_ident(struct fsl_qspi_softc *sc)
+{
+ uint32_t mcr, rbdr0, i, dev_id, manufacturer_id;
+
+ mcr = read_reg_be(sc, QSPI_MCR);
+ write_reg_be(sc, QSPI_MCR, mcr | QSPI_MCR_CLR_RXF);
+
+ write_reg_be(sc, QSPI_SFAR, sc->ahb_phys);
+ write_reg_be(sc, QSPI_RBCR, QSPI_RBCR_RXBRD_USEIPS);
+
+ dsb(sy);
+
+ write_reg_be(sc, QSPI_IPCR, QSPI_IPCR_SEQID(SEQID_RDID) | 4);
+
+ fsl_qspi_wait_for_controller(sc);
+
+ rbdr0 = read_reg_le(sc, QSPI_RBDR(0));
+
+ manufacturer_id = rbdr0 >> 24;
+ dev_id = (rbdr0 >> 8) & 0xffff;
+
+ for (i = 0; i < nitems(flash_devices); i++) {
+ if ((flash_devices[i].manufacturer_id == manufacturer_id) &&
+ (flash_devices[i].device_id == dev_id))
+ return (&flash_devices[i]);
+ }
+
+ device_printf(sc->dev,
+ "Unknown SPI flash device. Vendor: %02x, device id: %04x\n",
+ manufacturer_id, dev_id);
+ return (NULL);
+}
+
+
+static void
+fsl_qspi_invalidate_ahb(struct fsl_qspi_softc *sc)
+{
+ uint32_t mcr;
+
+ mcr = read_reg_be(sc, QSPI_MCR);
+ write_reg_be(sc, QSPI_MCR, mcr | QSPI_MCR_SWRSTHD | QSPI_MCR_SWRSTSD);
+
+ DELAY(10);
+ write_reg_be(sc, QSPI_MCR, mcr);
+}
+
+static void
+fsl_qspi_wait_for_controller(struct fsl_qspi_softc *sc)
+{
+ dsb(sy);
+
+ while (read_reg_be(sc, QSPI_SR) & QSPI_SR_DEV_BUSY)
+ DELAY(10);
+}
+
+static void
+fsl_qspi_wait_for_flash(struct fsl_qspi_softc *sc)
+{
+ uint32_t mcr, rbsr, flash_status;
+
+ fsl_qspi_wait_for_controller(sc);
+ mcr = read_reg_be(sc, QSPI_MCR);
+
+ do {
+ /* Clear RX buffer */
+ write_reg_be(sc, QSPI_MCR, mcr | QSPI_MCR_CLR_RXF);
+
+ /* Poll device for its status, we expect 1 byte in response */
+ write_reg_be(sc, QSPI_IPCR, QSPI_IPCR_SEQID(SEQID_RDSR) | 1);
+ fsl_qspi_wait_for_controller(sc);
+
+ /* Check if we got any response */
+ rbsr = read_reg_be(sc, QSPI_RBSR);
+ /* We have at least 1 byte in RX buffer */
+ if (rbsr & QSPI_RBSR_RDBFL_MASK)
+ flash_status = read_reg_be(sc, QSPI_RBDR(0));
+ else
+ flash_status = 0;
+
+ } while ((flash_status & STATUS_WIP));
+
+ /* Clear RX buffer */
+ write_reg_be(sc, QSPI_MCR, mcr | QSPI_MCR_CLR_RXF);
+}
+
+static int
+fsl_qspi_read(struct fsl_qspi_softc *sc, off_t offset, caddr_t data,
+ size_t count)
+{
+ uint32_t mcr_reg;
+
+ mtx_lock(&sc->dev_mtx);
+
+ mcr_reg = read_reg_be(sc, QSPI_MCR);
+ write_reg_be(sc, QSPI_MCR,
+ mcr_reg | QSPI_MCR_CLR_RXF | QSPI_MCR_CLR_TXF);
+
+ dsb(sy);
+
+ bus_read_region_4(sc->ahb_mem_res, offset, (uint32_t *)data, count / 4);
+ if (count % 4 != 0)
+ bus_read_region_1(sc->ahb_mem_res, offset + count / 4,
+ (uint8_t *)data + count / 4, count % 4);
+
+ mtx_unlock(&sc->dev_mtx);
+
+ return (0);
+}
+
+static int
+fsl_qspi_write_enable(struct fsl_qspi_softc *sc)
+{
+ uint32_t mcr;
+
+ mcr = read_reg_be(sc, QSPI_MCR);
+ write_reg_be(sc, QSPI_MCR, mcr | QSPI_MCR_CLR_RXF | QSPI_MCR_CLR_TXF);
+ write_reg_be(sc, QSPI_RBCR, QSPI_RBCR_RXBRD_USEIPS);
+
+ dsb(sy);
+
+ write_reg_be(sc, QSPI_IPCR, QSPI_IPCR_SEQID(SEQID_WREN));
+ fsl_qspi_wait_for_flash(sc);
+
+ return (0);
+}
+
+static void
+fsl_qspi_erase(struct fsl_qspi_softc *sc, off_t offset)
+{
+ uint32_t mcr;
+
+ fsl_qspi_write_enable(sc);
+
+ mcr = read_reg_be(sc, QSPI_MCR);
+ write_reg_be(sc, QSPI_MCR, mcr | QSPI_MCR_CLR_RXF | QSPI_MCR_CLR_TXF);
+
+ write_reg_be(sc, QSPI_RBCR, QSPI_RBCR_RXBRD_USEIPS);
+ write_reg_be(sc, QSPI_SFAR, sc->ahb_phys + offset);
+
+ dsb(sy);
+
+ write_reg_be(sc, QSPI_IPCR, QSPI_IPCR_SEQID(SEQID_SE));
+ fsl_qspi_wait_for_flash(sc);
+}
+
+static void
+fsl_qspi_write_burst(struct fsl_qspi_softc *sc, size_t offs, uint8_t *data,
+ size_t size)
+{
+ uint32_t mcr, tbdr;
+ int i;
+
+ fsl_qspi_write_enable(sc);
+
+ mcr = read_reg_be(sc, QSPI_MCR);
+ write_reg_be(sc, QSPI_MCR, mcr | QSPI_MCR_CLR_RXF | QSPI_MCR_CLR_TXF);
+ write_reg_be(sc, QSPI_RBCR, QSPI_RBCR_RXBRD_USEIPS);
+
+ fsl_qspi_wait_for_controller(sc);
+ write_reg_be(sc, QSPI_SFAR, sc->ahb_phys + offs);
+
+ for (i = 0; i < size / 4; i++) {
+ memcpy(&tbdr, data, 4);
+ write_reg_be(sc, QSPI_TBDR, tbdr);
+ data += 4;
+ }
+
+ if (size % 4 != 0) {
+ tbdr = 0;
+ memcpy(&tbdr, data, size % 4);
+ write_reg_be(sc, QSPI_TBDR, tbdr);
+ }
+
+ write_reg_be(sc, QSPI_IPCR, QSPI_IPCR_SEQID(SEQID_PP) | size);
+ fsl_qspi_wait_for_flash(sc);
+}
+
+static void
+fsl_qspi_write_sector(struct fsl_qspi_softc *sc, off_t sector_offset,
+ uint8_t *data)
+{
+ size_t written, burst_len, length;
+
+ written = 0;
+ length = sc->erasesize;
+
+ while (length > 0 && data[length - 1] == 0xff)
+ length--;
+
+ while (written < length) {
+ burst_len = MIN(TX_BURST_SIZE, length - written);
+ fsl_qspi_write_burst(sc, sector_offset + written, data, burst_len);
+
+ data += burst_len;
+ written += burst_len;
+ }
+}
+
+static int
+fsl_qspi_write(struct fsl_qspi_softc *sc, off_t offset, uint8_t *data,
+ size_t size)
+{
+ size_t offset_in_sector, input_offset, single_chunk_size;
+ size_t sector_offset, sector_size;
+ char *sector_buf;
+
+ sector_size = sc->erasesize;
+ sector_buf = malloc(sector_size, SECTOR_BUFFER, M_WAITOK);
+ offset_in_sector = offset % sector_size;
+
+ mtx_lock(&sc->dev_mtx);
+ sector_offset = rounddown2(offset, sector_size);
+ input_offset = 0;
+ while (size != 0) {
+ fsl_qspi_read(sc, sector_offset, sector_buf, sector_size);
+ single_chunk_size = MIN(sector_size - offset_in_sector, size);
+ memcpy(sector_buf + offset_in_sector, data + input_offset,
+ single_chunk_size);
+ size -= single_chunk_size;
+ input_offset += single_chunk_size;
+
+ fsl_qspi_erase(sc, sector_offset);
+ fsl_qspi_write_sector(sc, sector_offset, sector_buf);
+ fsl_qspi_invalidate_ahb(sc);
+ offset_in_sector = 0;
+ sector_offset += sector_size;
+ }
+ mtx_unlock(&sc->dev_mtx);
+
+ free(sector_buf, SECTOR_BUFFER);
+
+ return (0);
+}
+
+static int
+fsl_qspi_probe(device_t dev)
+{
+
+ if (!ofw_bus_is_compatible(dev, "fsl,ls1021a-qspi"))
+ return (ENXIO);
+
+ device_set_desc(dev, "FSL QSPI Flash");
+ return (BUS_PROBE_SPECIFIC);
+}
+
+static int
+fsl_qspi_attach(device_t dev)
+{
+ struct fsl_qspi_softc *sc;
+ int rid, error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ mtx_init(&sc->disk_mtx, "FSL_QSPI_DISK", "QSPI disk mtx", MTX_DEF);
+ mtx_init(&sc->dev_mtx, "FSL_QSPI_DEV", "QSPI device mtx", MTX_DEF);
+
+ /* Get memory resources. */
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+
+ rid = 1;
+ sc->ahb_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+
+ sc->ahb_phys = vtophys(rman_get_bushandle(sc->ahb_mem_res));
+
+ if (sc->mem_res == NULL || sc->ahb_mem_res == NULL) {
+ device_printf(dev, "could not allocate memory resources\n");
+ fsl_qspi_detach(dev);
+ return (ENOMEM);
+ }
+
+ sc->disk = disk_alloc();
+ sc->disk->d_open = fsl_qspi_open;
+ sc->disk->d_close = fsl_qspi_close;
+ sc->disk->d_strategy = fsl_qspi_strategy;
+ sc->disk->d_getattr = fsl_qspi_getattr;
+ sc->disk->d_ioctl = fsl_qspi_ioctl;
+ sc->disk->d_name = "flash/qspi";
+ sc->disk->d_drv1 = sc;
+ /* the most that can fit in a single spi transaction */
+ sc->disk->d_maxsize = DFLTPHYS;
+ sc->disk->d_sectorsize = FLASH_SECTORSIZE;
+ sc->disk->d_unit = device_get_unit(sc->dev);
+ sc->disk->d_dump = NULL;
+
+ error = fsl_qspi_init_hw(sc);
+ if (error != 0) {
+ fsl_qspi_detach(dev);
+ return (error);
+ }
+
+ disk_create(sc->disk, DISK_VERSION);
+ bioq_init(&sc->bio_queue);
+
+ kproc_create(&fsl_qspi_task, sc, &sc->p, 0, 0, "task: qspi flash");
+ sc->taskstate = TSTATE_RUNNING;
+
+ return (0);
+}
+
+static int
+fsl_qspi_detach(device_t dev)
+{
+ struct fsl_qspi_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ err = 0;
+
+ mtx_lock(&sc->disk_mtx);
+ if (sc->taskstate == TSTATE_RUNNING) {
+ sc->taskstate = TSTATE_STOPPING;
+ wakeup(sc->disk);
+ while (err == 0 && sc->taskstate != TSTATE_STOPPED) {
+ err = mtx_sleep(sc->disk, &sc->disk_mtx, 0, "FSL_QSPI", hz * 3);
+ if (err != 0) {
+ sc->taskstate = TSTATE_RUNNING;
+ device_printf(sc->dev,
+ "Failed to stop queue task\n");
+ }
+ }
+ }
+
+ mtx_unlock(&sc->disk_mtx);
+ mtx_destroy(&sc->disk_mtx);
+ mtx_destroy(&sc->dev_mtx);
+
+ if (err == 0 && sc->taskstate == TSTATE_STOPPED) {
+ disk_destroy(sc->disk);
+ bioq_flush(&sc->bio_queue, NULL, ENXIO);
+ }
+
+ /* Disable hardware. */
+ write_reg_be(sc, QSPI_MCR, QSPI_MCR_MDIS);
+ write_reg_be(sc, QSPI_RSER, 0x0);
+
+ /* Release memory resource. */
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->mem_res), sc->mem_res);
+
+ if (sc->ahb_mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->ahb_mem_res), sc->ahb_mem_res);
+
+ return (err);
+}
+
+static int
+fsl_qspi_open(struct disk *dp)
+{
+
+ return (0);
+}
+
+static int
+fsl_qspi_close(struct disk *dp)
+{
+
+ return (0);
+}
+
+static int
+fsl_qspi_ioctl(struct disk *dp, u_long cmd, void *data, int fflag,
+ struct thread *td)
+{
+
+ return (ENOTSUP);
+}
+
+static void
+fsl_qspi_strategy(struct bio *bp)
+{
+ struct fsl_qspi_softc *sc;
+
+ sc = (struct fsl_qspi_softc *) bp->bio_disk->d_drv1;
+ mtx_lock(&sc->disk_mtx);
+ bioq_disksort(&sc->bio_queue, bp);
+ mtx_unlock(&sc->disk_mtx);
+ wakeup(sc->disk);
+}
+
+static int
+fsl_qspi_getattr(struct bio *bp)
+{
+ struct fsl_qspi_softc *sc;
+ device_t dev;
+
+ if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL)
+ return (ENXIO);
+
+ sc = bp->bio_disk->d_drv1;
+ dev = sc->dev;
+
+ if (strcmp(bp->bio_attribute, "QSPI::device") == 0) {
+ if (bp->bio_length != sizeof(dev))
+ return (EFAULT);
+ bcopy(&dev, bp->bio_data, sizeof(dev));
+ } else {
+ return (-1);
+ }
+ return (0);
+}
+
+static void
+fsl_qspi_task(void *arg)
+{
+ struct fsl_qspi_softc *sc;
+ struct bio *bp;
+ device_t dev;
+
+ sc = (struct fsl_qspi_softc *)arg;
+ for (;;) {
+ dev = sc->dev;
+ mtx_lock(&sc->disk_mtx);
+ do {
+ if (sc->taskstate == TSTATE_STOPPING) {
+ sc->taskstate = TSTATE_STOPPED;
+ mtx_unlock(&sc->disk_mtx);
+ wakeup(sc->disk);
+ kproc_exit(0);
+ }
+ bp = bioq_first(&sc->bio_queue);
+ if (bp == NULL) {
+ mtx_sleep(sc->disk, &sc->disk_mtx, PRIBIO, "FSL_QSPI", 0);
+ }
+ } while (bp == NULL);
+ bioq_remove(&sc->bio_queue, bp);
+ mtx_unlock(&sc->disk_mtx);
+
+ switch (bp->bio_cmd) {
+ case BIO_READ:
+ bp->bio_error = fsl_qspi_read(sc, bp->bio_offset,
+ bp->bio_data, bp->bio_bcount);
+ break;
+ case BIO_WRITE:
+ bp->bio_error = fsl_qspi_write(sc, bp->bio_offset,
+ bp->bio_data, bp->bio_bcount);
+ break;
+ default:
+ bp->bio_error = EINVAL;
+ }
+ biodone(bp);
+ }
+}
+
+static devclass_t fsl_qspi_devclass;
+static device_method_t fsl_qspi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, fsl_qspi_probe),
+ DEVMETHOD(device_attach, fsl_qspi_attach),
+ DEVMETHOD(device_detach, fsl_qspi_detach),
+
+ { 0, 0 }
+};
+
+static driver_t fsl_qspi_driver = {
+ "fsl_qspi",
+ fsl_qspi_methods,
+ sizeof(struct fsl_qspi_softc),
+};
+
+DRIVER_MODULE(fsl_qspi, simplebus, fsl_qspi_driver, fsl_qspi_devclass, 0, 0);
Index: sys/conf/files.arm64
===================================================================
--- sys/conf/files.arm64
+++ sys/conf/files.arm64
@@ -194,6 +194,7 @@
arm64/intel/firmware.c optional soc_intel_stratix10
arm64/intel/stratix10-soc-fpga-mgr.c optional soc_intel_stratix10
arm64/intel/stratix10-svc.c optional soc_intel_stratix10
+arm64/qoriq/fsl_qspi.c optional fsl_qspi fdt
arm64/qoriq/ls1046_gpio.c optional gpio fdt
arm64/qoriq/clk/ls1046a_clkgen.c optional fdt qoriq_clk ls1046a_clk
arm64/qoriq/clk/qoriq_clkgen.c optional fdt qoriq_clk

File Metadata

Mime Type
text/plain
Expires
Fri, Dec 27, 12:08 AM (11 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15609291
Default Alt Text
D24467.diff (30 KB)

Event Timeline