Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F106136162
D24467.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
30 KB
Referenced Files
None
Subscribers
None
D24467.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D24467: Add support for QorIQ FSL QSPI flash controller.
Attached
Detach File
Event Timeline
Log In to Comment