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 +__FBSDID("$FreeBSD$"); + +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#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