From 9e125c4346d57267ecae2843813b084b042c0fa8 Mon Sep 17 00:00:00 2001 From: Jukka Laitinen <jukkax@ssrc.tii.ae> Date: Wed, 3 Jul 2024 15:27:44 +0300 Subject: [PATCH] arch/arm64/src/imx9: Add an mtd driver for NOR flash on FlexSPI interface This is an initial FlexSPI SPI NOR MTD driver for IMX9 This supprts M25P SPI NOR on FlexSPI for now, and can later be extended to other SPINOR devices if needed. The following configurations are needed to use this driver: CONFIG_IMX9_FLEXSPI_NOR=y CONFIG_MTD_M25P=y In addition, board initialization logic needs to call the imx9_flexspi_nor_initialize to receive a pointer to the mtd device. Signed-off-by: Jukka Laitinen <jukkax@ssrc.tii.ae> Co-authored-by: Jouni Ukkonen <jouni.ukkonen@unikie.com> --- arch/arm64/src/imx9/Kconfig | 4 + arch/arm64/src/imx9/Make.defs | 4 + arch/arm64/src/imx9/imx9_flexspi_nor.c | 1081 ++++++++++++++++++++++++ arch/arm64/src/imx9/imx9_flexspi_nor.h | 83 ++ 4 files changed, 1172 insertions(+) create mode 100644 arch/arm64/src/imx9/imx9_flexspi_nor.c create mode 100644 arch/arm64/src/imx9/imx9_flexspi_nor.h diff --git a/arch/arm64/src/imx9/Kconfig b/arch/arm64/src/imx9/Kconfig index 67ee9c7454e2b..0370495e9780b 100644 --- a/arch/arm64/src/imx9/Kconfig +++ b/arch/arm64/src/imx9/Kconfig @@ -250,6 +250,10 @@ config IMX9_FLEXSPI bool "ENABLE FLEXSPI interface" default n +config IMX9_FLEXSPI_NOR + bool "Enable NOR flash on FLEXSPI interface" + select IMX9_FLEXSPI + default n config IMX9_FLEXIO1_PWM depends on PWM diff --git a/arch/arm64/src/imx9/Make.defs b/arch/arm64/src/imx9/Make.defs index f97bb2a676ad6..89e12744d997e 100644 --- a/arch/arm64/src/imx9/Make.defs +++ b/arch/arm64/src/imx9/Make.defs @@ -71,3 +71,7 @@ endif ifeq ($(CONFIG_IMX9_FLEXSPI), y) CHIP_CSRCS += imx9_flexspi.c endif + +ifeq ($(CONFIG_IMX9_FLEXSPI_NOR), y) + CHIP_CSRCS += imx9_flexspi_nor.c +endif diff --git a/arch/arm64/src/imx9/imx9_flexspi_nor.c b/arch/arm64/src/imx9/imx9_flexspi_nor.c new file mode 100644 index 0000000000000..4bf471c197f80 --- /dev/null +++ b/arch/arm64/src/imx9/imx9_flexspi_nor.c @@ -0,0 +1,1081 @@ +/**************************************************************************** + * arch/arm64/src/imx9/imx9_flexspi_nor.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdbool.h> +#include <sys/param.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/kmalloc.h> +#include <nuttx/signal.h> +#include <nuttx/fs/fs.h> +#include <nuttx/mtd/mtd.h> +#include <nuttx/fs/ioctl.h> +#include <nuttx/drivers/drivers.h> +#include <arch/board/board.h> + +#include "chip.h" +#include "imx9_flexspi.h" +#include "imx9_iomuxc.h" +#include "hardware/imx9_flexspi.h" +#include "hardware/imx9_pinmux.h" + +#ifdef CONFIG_IMX9_FLEXSPI_NOR + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_M25P_SUBSECTOR_ERASE +#error This driver currently requires CONFIG_M25P_SUBSECTOR_ERASE +#endif + +# if !defined(ARMV8A_DCACHE_LINESIZE) || ARMV8A_DCACHE_LINESIZE == 0 +# undef ARMV8A_DCACHE_LINESIZE +# define ARMV8A_DCACHE_LINESIZE 64 +# endif + +#define ALIGN_MASK (ARMV8A_DCACHE_LINESIZE - 1) +#define ALIGN_UP(n) (((n)+ALIGN_MASK) & ~ALIGN_MASK) + +/* Configuration ************************************************************/ + +/* Per the data sheet, M25P10 parts can be driven with either SPI mode 0 + * (CPOL=0 and CPHA=0) or mode 3 (CPOL=1 and CPHA=1). But I have heard that + * other devices can operated in mode 0 or 1. + * So you may need to specify CONFIG_M25P_SPIMODE to + * select the best mode for your device. + * If CONFIG_M25P_SPIMODE is not defined, mode 0 will be used. + */ + +#ifndef CONFIG_M25P_SPIMODE +# define CONFIG_M25P_SPIMODE SPIDEV_MODE0 +#endif + +#ifndef CONFIG_M25P_SPIFREQUENCY +# define CONFIG_M25P_SPIFREQUENCY 20000000 +#endif + +/* Various manufacturers may have produced the parts. + * 0x20 is the manufacturer ID for the STMicro MP25x serial FLASH. + * If, for example, you are using the a Macronix International MX25 + * serial FLASH, the correct manufacturer ID would be 0xc2. + */ + +#ifndef CONFIG_M25P_MANUFACTURER +# define CONFIG_M25P_MANUFACTURER 0x20 +#endif + +#ifndef CONFIG_M25P_MEMORY_TYPE +# define CONFIG_M25P_MEMORY_TYPE 0x20 +#endif + +#ifndef CONFIG_MT25Q_MEMORY_TYPE +# define CONFIG_MT25Q_MEMORY_TYPE 0xBA +#endif + +#ifndef CONFIG_MT25QU_MEMORY_TYPE +# define CONFIG_MT25QU_MEMORY_TYPE 0xBB +#endif + +/* M25P Registers ***********************************************************/ + +/* Identification register values */ + +#define M25P_MANUFACTURER CONFIG_M25P_MANUFACTURER +#define M25P_MEMORY_TYPE CONFIG_M25P_MEMORY_TYPE +#define MT25Q_MEMORY_TYPE CONFIG_MT25Q_MEMORY_TYPE +#define MT25QU_MEMORY_TYPE CONFIG_MT25QU_MEMORY_TYPE +#define M25P_RES_ID 0x13 +#define M25P_M25P1_CAPACITY 0x11 /* 1 M-bit */ +#define M25P_EN25F80_CAPACITY 0x14 /* 8 M-bit */ +#define M25P_M25P16_CAPACITY 0x15 /* 16 M-bit */ +#define M25P_M25P32_CAPACITY 0x16 /* 32 M-bit */ +#define M25P_M25P64_CAPACITY 0x17 /* 64 M-bit */ +#define M25P_M25P128_CAPACITY 0x18 /* 128 M-bit */ +#define M25P_MT25Q128_CAPACITY 0x18 /* 128 M-bit */ +#define M25P_MT25Q256_CAPACITY 0x19 /* 256 M-bit */ +#define M25P_MT25Q512_CAPACITY 0x20 /* 512 M-bit */ +#define M25P_MT25Q1G_CAPACITY 0x21 /* 1 G-bit */ + +/* M25P1 capacity is 131,072 bytes: + * (4 sectors) * (32,768 bytes per sector) + * (512 pages) * (256 bytes per page) + */ + +#define M25P_M25P1_SECTOR_SHIFT 15 /* Sector size 1 << 15 = 65,536 */ +#define M25P_M25P1_NSECTORS 4 +#define M25P_M25P1_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */ +#define M25P_M25P1_NPAGES 512 + +/* EN25F80 capacity is 1,048,576 bytes: + * (16 sectors) * (65,536 bytes per sector) + * (512 pages) * (256 bytes per page) + */ + +#define M25P_EN25F80_SECTOR_SHIFT 16 /* Sector size 1 << 15 = 65,536 */ +#define M25P_EN25F80_NSECTORS 16 +#define M25P_EN25F80_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */ +#define M25P_EN25F80_NPAGES 4096 +#define M25P_EN25F80_SUBSECT_SHIFT 12 /* Sub-Sector size 1 << 12 = 4,096 */ +#define M25P_EN25F80_NSUBSECTORS 256 + +/* M25P16 capacity is 2,097,152 bytes: + * (32 sectors) * (65,536 bytes per sector) + * (8192 pages) * (256 bytes per page) + */ + +#define M25P_M25P16_SECTOR_SHIFT 16 /* Sector size 1 << 16 = 65,536 */ +#define M25P_M25P16_NSECTORS 32 +#define M25P_M25P16_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */ +#define M25P_M25P16_NPAGES 8192 +#define M25P_M25PX16_SUBSECT_SHIFT 12 /* Sub-Sector size 1 << 12 = 4,096 */ + +/* M25P32 capacity is 4,194,304 bytes: + * (64 sectors) * (65,536 bytes per sector) + * (16384 pages) * (256 bytes per page) + */ + +#define M25P_M25P32_SECTOR_SHIFT 16 /* Sector size 1 << 16 = 65,536 */ +#define M25P_M25P32_NSECTORS 64 +#define M25P_M25P32_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */ +#define M25P_M25P32_NPAGES 16384 +#define M25P_M25PX32_SUBSECT_SHIFT 12 /* Sub-Sector size 1 << 12 = 4,096 */ + +/* M25P64 capacity is 8,338,608 bytes: + * (128 sectors) * (65,536 bytes per sector) + * (32768 pages) * (256 bytes per page) + */ + +#define M25P_M25P64_SECTOR_SHIFT 16 /* Sector size 1 << 16 = 65,536 */ +#define M25P_M25P64_NSECTORS 128 +#define M25P_M25P64_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */ +#define M25P_M25P64_NPAGES 32768 + +/* M25P128 capacity is 16,777,216 bytes: + * (64 sectors) * (262,144 bytes per sector) + * (65536 pages) * (256 bytes per page) + */ + +#define M25P_M25P128_SECTOR_SHIFT 18 /* Sector size 1 << 18 = 262,144 */ +#define M25P_M25P128_NSECTORS 64 +#define M25P_M25P128_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */ +#define M25P_M25P128_NPAGES 65536 + +/* MT25Q128 capacity is 16,777,216 bytes: + * (256 sectors) * (65,536 bytes per sector) + * (65536 pages) * (256 bytes per page) + */ + +#define M25P_MT25Q128_SECTOR_SHIFT 16 /* Sector size 1 << 16 = 65,536 */ +#define M25P_MT25Q128_NSECTORS 256 +#define M25P_MT25Q128_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */ +#define M25P_MT25Q128_NPAGES 65536 +#define M25P_MT25Q128_SUBSECT_SHIFT 12 /* Sub-Sector size 1 << 12 = 4,096 */ + +/* MT25Q256 capacity is 33,554,432 bytes: + * (512 sectors) * (65,536 bytes per sector) + * (131072 pages) * (256 bytes per page) + */ + +#define M25P_MT25Q256_SECTOR_SHIFT 16 /* Sector size 1 << 16 = 65,536 */ +#define M25P_MT25Q256_NSECTORS 512 +#define M25P_MT25Q256_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */ +#define M25P_MT25Q256_NPAGES 131072 +#define M25P_MT25Q256_SUBSECT_SHIFT 12 /* Sub-Sector size 1 << 12 = 4,096 */ + +/* MT25Q512 capacity is 67,108,864 bytes: + * (1024 sectors) * (65,536 bytes per sector) + * (262144 pages) * (256 bytes per page) + */ + +#define M25P_MT25Q512_SECTOR_SHIFT 16 /* Sector size 1 << 16 = 65,536 */ +#define M25P_MT25Q512_NSECTORS 1024 +#define M25P_MT25Q512_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */ +#define M25P_MT25Q512_NPAGES 262144 +#define M25P_MT25Q512_SUBSECT_SHIFT 12 /* Sub-Sector size 1 << 12 = 4,096 */ + +/* MT25Q1G capacity is 134,217,728 bytes: + * (2048 sectors) * (65,536 bytes per sector) + * (524288 pages) * (256 bytes per page) + */ + +#define M25P_MT25Q1G_SECTOR_SHIFT 16 /* Sector size 1 << 16 = 65,536 */ +#define M25P_MT25Q1G_NSECTORS 2048 +#define M25P_MT25Q1G_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */ +#define M25P_MT25Q1G_NPAGES 524288 +#define M25P_MT25Q1G_SUBSECT_SHIFT 12 /* Sub-Sector size 1 << 12 = 4,096 */ + +/* Instructions */ + +/* Command Value N Description Addr Dummy Data */ +#define M25P_WREN 0x06 /* 1 Write Enable 0 0 0 */ +#define M25P_WRDI 0x04 /* 1 Write Disable 0 0 0 */ +#define M25P_RDID 0x9f /* 1 Read Identification 0 0 1-3 */ +#define M25P_RDSR 0x05 /* 1 Read Status Register 0 0 >=1 */ +#define M25P_WRSR 0x01 /* 1 Write Status Register 0 0 1 */ +#define M25P_READ 0x03 /* 1 Read Data Bytes 3 0 >=1 */ +#define M25P_FAST_READ 0x0b /* 1 Higher speed read 3 1 >=1 */ +#define M25P_PP 0x02 /* 1 Page Program 3 0 1-256 */ +#define M25P_SE 0xd8 /* 1 Sector Erase 3 0 0 */ +#define M25P_BE 0xc7 /* 1 Bulk Erase 0 0 0 */ +#define M25P_DP 0xb9 /* 2 Deep power down 0 0 0 */ +#define M25P_RES 0xab /* 2 Read Electronic Signature 0 3 >=1 */ +#define M25P_SSE 0x20 /* 3 Sub-Sector Erase 0 0 0 */ +#define M25P_WECR 0x61 /* 1 Write Enhanched config 0 0 1 */ + +/* Quad commands */ +#define M25P_Q_FAST_RD 0x6b /* 1 Quad output fast read 3/4 0 1-256 */ +#define M25P_Q_FAST_PP 0x32 /* 1 Quad input fast program 3/4 0 1-256 */ +#define M25P_Q_ENTER 0x35 /* 1 Enter Quad input/output 0 0 0 */ + +/* NOTE 1: All parts. + * NOTE 2: M25P632/M25P64 + * NOTE 3: EN25F80. In EN25F80 terminology, 0xd8 is a block erase and 0x20 + * is a sector erase. + */ + +enum +{ + /* SPI instructions */ + + READ_ID, + READ_STATUS_REG, + WRITE_STATUS_REG, + WRITE_ENABLE, + ERASE_SECTOR, + ERASE_CHIP, + ENTER_DDR, + READ_FAST, + + /* Quad SPI instructions */ + + READ_FAST_QUAD_OUTPUT, + PAGE_PROGRAM_QUAD_INPUT, + ENTER_QPI, +}; + +/* TODO: Re-define commands if using other than M25 NOR */ + +static const uint32_t g_flexspi_nor_lut[][4] = +{ + [READ_ID] = + { + FLEXSPI_LUT_SEQ(FLEXSPI_COMMAND_SDR, FLEXSPI_1PAD, M25P_RDID, + FLEXSPI_COMMAND_READ_SDR, FLEXSPI_1PAD, 0x04), + }, + + [READ_STATUS_REG] = + { + FLEXSPI_LUT_SEQ(FLEXSPI_COMMAND_SDR, FLEXSPI_1PAD, M25P_RDSR, + FLEXSPI_COMMAND_READ_SDR, FLEXSPI_1PAD, 0x04), + }, + + [WRITE_STATUS_REG] = + { + FLEXSPI_LUT_SEQ(FLEXSPI_COMMAND_SDR, FLEXSPI_1PAD, M25P_WRSR, + FLEXSPI_COMMAND_WRITE_SDR, FLEXSPI_1PAD, 0x04), + }, + + [WRITE_ENABLE] = + { + FLEXSPI_LUT_SEQ(FLEXSPI_COMMAND_SDR, FLEXSPI_1PAD, M25P_WREN, + FLEXSPI_COMMAND_STOP, FLEXSPI_1PAD, 0), + }, + + [ERASE_SECTOR] = + { + FLEXSPI_LUT_SEQ(FLEXSPI_COMMAND_SDR, FLEXSPI_1PAD, M25P_SSE, /* note: sub-sector erase */ + FLEXSPI_COMMAND_RADDR_SDR, FLEXSPI_1PAD, 0x18), + }, + + [ERASE_CHIP] = + { + FLEXSPI_LUT_SEQ(FLEXSPI_COMMAND_SDR, FLEXSPI_1PAD, M25P_BE, + FLEXSPI_COMMAND_STOP, FLEXSPI_1PAD, 0), + }, + + [READ_FAST_QUAD_OUTPUT] = + { + FLEXSPI_LUT_SEQ(FLEXSPI_COMMAND_SDR, FLEXSPI_1PAD, M25P_Q_FAST_RD, + FLEXSPI_COMMAND_RADDR_SDR, FLEXSPI_1PAD, 0x18), + FLEXSPI_LUT_SEQ(FLEXSPI_COMMAND_DUMMY_SDR, FLEXSPI_4PAD, 0x08, + FLEXSPI_COMMAND_READ_SDR, FLEXSPI_4PAD, 0x04), + }, + + [PAGE_PROGRAM_QUAD_INPUT] = + { + FLEXSPI_LUT_SEQ(FLEXSPI_COMMAND_SDR, FLEXSPI_1PAD, M25P_Q_FAST_PP, + FLEXSPI_COMMAND_RADDR_SDR, FLEXSPI_1PAD, 0x18), + FLEXSPI_LUT_SEQ(FLEXSPI_COMMAND_WRITE_SDR, FLEXSPI_4PAD, 0x04, + FLEXSPI_COMMAND_STOP, FLEXSPI_1PAD, 0), + }, + + [ENTER_QPI] = + { + FLEXSPI_LUT_SEQ(FLEXSPI_COMMAND_SDR, FLEXSPI_1PAD, M25P_Q_ENTER, + FLEXSPI_COMMAND_STOP, FLEXSPI_1PAD, 0), + }, + + [ENTER_DDR] = + { + FLEXSPI_LUT_SEQ(FLEXSPI_COMMAND_SDR, FLEXSPI_1PAD, M25P_WECR, + FLEXSPI_COMMAND_WRITE_SDR, FLEXSPI_1PAD, 0x8), + }, + + [READ_FAST] = + { + FLEXSPI_LUT_SEQ(FLEXSPI_COMMAND_SDR, FLEXSPI_1PAD, M25P_FAST_READ, + FLEXSPI_COMMAND_RADDR_SDR, FLEXSPI_1PAD, 0x18), + FLEXSPI_LUT_SEQ(FLEXSPI_COMMAND_DUMMY_SDR, FLEXSPI_1PAD, 0x08, + FLEXSPI_COMMAND_READ_SDR, FLEXSPI_1PAD, 0x04) + }, +}; + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* FlexSPI NOR device private data */ + +struct imx9_flexspi_nor_dev_s +{ + struct mtd_dev_s mtd; + struct flexspi_dev_s *flexspi; /* Saved FlexSPI interface instance */ + uint8_t *ahb_base; + enum flexspi_port_e port; + struct flexspi_device_config_s *config; + uint8_t sectorshift; /* 16 or 18 */ + uint8_t pageshift; /* 8 */ + uint16_t nsectors; /* 128 or 64 */ + uint32_t npages; /* 32,768 or 65,536 */ +#ifdef CONFIG_M25P_SUBSECTOR_ERASE + uint8_t subsectorshift; /* 0, 12 or 13 (4K or 8K) */ +#endif +}; + +/**************************************************************************** + * Private Functions Prototypes + ****************************************************************************/ + +/* MTD driver methods */ + +static int imx9_flexspi_nor_erase(struct mtd_dev_s *dev, + off_t startblock, + size_t nblocks); +static ssize_t imx9_flexspi_nor_read(struct mtd_dev_s *dev, + off_t offset, + size_t nbytes, + uint8_t *buffer); +static ssize_t imx9_flexspi_nor_bread(struct mtd_dev_s *dev, + off_t startblock, + size_t nblocks, + uint8_t *buffer); +static ssize_t imx9_flexspi_nor_bwrite(struct mtd_dev_s *dev, + off_t startblock, + size_t nblocks, + const uint8_t *buffer); +static int imx9_flexspi_nor_ioctl(struct mtd_dev_s *dev, + int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct flexspi_device_config_s g_flexspi_device_config = +{ + .flexspi_root_clk = 50000000, + .flash_size = 1024 * 64, /* size in kB */ + .cs_interval_unit = FLEXSPI_CS_INTERVAL_UNIT1_SCK_CYCLE, + .cs_interval = 0, + .cs_hold_time = 3, + .cs_setup_time = 3, + .data_valid_time = 0, + .columnspace = 0, + .enable_word_address = 0, + .awr_seq_index = 0, + .awr_seq_number = 0, + .ard_seq_index = READ_FAST_QUAD_OUTPUT, + .ard_seq_number = 1, + .ahb_write_wait_unit = FLEXSPI_AHB_WRITE_WAIT_UNIT2_AHB_CYCLE, + .ahb_write_wait_interval = 0 +}; + +static struct imx9_flexspi_nor_dev_s g_flexspi_nor = +{ + .mtd = + { + .erase = imx9_flexspi_nor_erase, + .bread = imx9_flexspi_nor_bread, + .bwrite = imx9_flexspi_nor_bwrite, + .read = imx9_flexspi_nor_read, + .ioctl = imx9_flexspi_nor_ioctl, +#ifdef CONFIG_MTD_BYTE_WRITE + .write = NULL, +#endif + .name = "imx9_flexspi_nor" + }, + .flexspi = (void *)0, + .ahb_base = (uint8_t *) CONFIG_FSPI_PER_BASEADDR, + .port = FLEXSPI_PORT_A1, + .config = &g_flexspi_device_config +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int imx9_flexspi_nor_get_id( + struct imx9_flexspi_nor_dev_s *priv) +{ + static uint32_t buffer = 0; + int stat; + + uint8_t manufacturer; + uint8_t memory; + uint8_t capacity; + + struct flexspi_transfer_s transfer = + { + .device_address = 0, + .port = priv->port, + .cmd_type = FLEXSPI_READ, + .seq_number = 1, + .seq_index = READ_ID, + .data = &buffer, + .data_size = 3, + }; + + stat = FLEXSPI_TRANSFER(priv->flexspi, &transfer); + if (stat != 0) + { + return -EIO; + } + + manufacturer = buffer; + memory = buffer >> 8; + capacity = buffer >> 16; + + /* Check for a valid manufacturer and memory type */ + + if (manufacturer == M25P_MANUFACTURER && memory == M25P_MEMORY_TYPE) + { + /* Okay.. is it a FLASH capacity that we understand? */ + +#ifdef CONFIG_M25P_SUBSECTOR_ERASE + priv->subsectorshift = 0; +#endif + + if (capacity == M25P_M25P1_CAPACITY) + { + /* Save the FLASH geometry */ + + priv->sectorshift = M25P_M25P1_SECTOR_SHIFT; + priv->nsectors = M25P_M25P1_NSECTORS; + priv->pageshift = M25P_M25P1_PAGE_SHIFT; + priv->npages = M25P_M25P1_NPAGES; + return OK; + } + else if (capacity == M25P_EN25F80_CAPACITY) + { + /* Save the FLASH geometry */ + + priv->pageshift = M25P_EN25F80_PAGE_SHIFT; + priv->npages = M25P_EN25F80_NPAGES; + priv->sectorshift = M25P_EN25F80_SECTOR_SHIFT; + priv->nsectors = M25P_EN25F80_NSECTORS; +#ifdef CONFIG_M25P_SUBSECTOR_ERASE + priv->subsectorshift = M25P_EN25F80_SUBSECT_SHIFT; +#endif + return OK; + } + else if (capacity == M25P_M25P16_CAPACITY) + { + /* Save the FLASH geometry */ + + priv->sectorshift = M25P_M25P16_SECTOR_SHIFT; + priv->nsectors = M25P_M25P16_NSECTORS; + priv->pageshift = M25P_M25P16_PAGE_SHIFT; + priv->npages = M25P_M25P16_NPAGES; +#ifdef CONFIG_M25P_SUBSECTOR_ERASE + priv->subsectorshift = M25P_M25PX16_SUBSECT_SHIFT; +#endif + return OK; + } + else if (capacity == M25P_M25P32_CAPACITY) + { + /* Save the FLASH geometry */ + + priv->sectorshift = M25P_M25P32_SECTOR_SHIFT; + priv->nsectors = M25P_M25P32_NSECTORS; + priv->pageshift = M25P_M25P32_PAGE_SHIFT; + priv->npages = M25P_M25P32_NPAGES; +#ifdef CONFIG_M25P_SUBSECTOR_ERASE + priv->subsectorshift = M25P_M25PX32_SUBSECT_SHIFT; +#endif + return OK; + } + else if (capacity == M25P_M25P64_CAPACITY) + { + /* Save the FLASH geometry */ + + priv->sectorshift = M25P_M25P64_SECTOR_SHIFT; + priv->nsectors = M25P_M25P64_NSECTORS; + priv->pageshift = M25P_M25P64_PAGE_SHIFT; + priv->npages = M25P_M25P64_NPAGES; + return OK; + } + else if (capacity == M25P_M25P128_CAPACITY) + { + /* Save the FLASH geometry */ + + priv->sectorshift = M25P_M25P128_SECTOR_SHIFT; + priv->nsectors = M25P_M25P128_NSECTORS; + priv->pageshift = M25P_M25P128_PAGE_SHIFT; + priv->npages = M25P_M25P128_NPAGES; + return OK; + } + } + else if (manufacturer == M25P_MANUFACTURER && + (memory == MT25Q_MEMORY_TYPE || memory == MT25QU_MEMORY_TYPE)) + { + /* Also okay.. is it a FLASH capacity that we understand? */ + +#ifdef CONFIG_M25P_SUBSECTOR_ERASE + priv->subsectorshift = 0; +#endif + if (capacity == M25P_MT25Q128_CAPACITY) + { + /* Save the FLASH geometry */ + + priv->sectorshift = M25P_MT25Q128_SECTOR_SHIFT; + priv->nsectors = M25P_MT25Q128_NSECTORS; + priv->pageshift = M25P_MT25Q128_PAGE_SHIFT; + priv->npages = M25P_MT25Q128_NPAGES; +#ifdef CONFIG_M25P_SUBSECTOR_ERASE + priv->subsectorshift = M25P_MT25Q128_SUBSECT_SHIFT; +#endif + return OK; + } + else if (capacity == M25P_MT25Q256_CAPACITY) + { + /* Save the FLASH geometry */ + + priv->sectorshift = M25P_MT25Q256_SECTOR_SHIFT; + priv->nsectors = M25P_MT25Q256_NSECTORS; + priv->pageshift = M25P_MT25Q256_PAGE_SHIFT; + priv->npages = M25P_MT25Q256_NPAGES; +#ifdef CONFIG_M25P_SUBSECTOR_ERASE + priv->subsectorshift = M25P_MT25Q256_SUBSECT_SHIFT; +#endif + return OK; + } + else if (capacity == M25P_MT25Q512_CAPACITY) + { + /* Save the FLASH geometry */ + + priv->sectorshift = M25P_MT25Q512_SECTOR_SHIFT; + priv->nsectors = M25P_MT25Q512_NSECTORS; + priv->pageshift = M25P_MT25Q512_PAGE_SHIFT; + priv->npages = M25P_MT25Q512_NPAGES; +#ifdef CONFIG_M25P_SUBSECTOR_ERASE + priv->subsectorshift = M25P_MT25Q512_SUBSECT_SHIFT; +#endif + return OK; + } + else if (capacity == M25P_MT25Q1G_CAPACITY) + { + /* Save the FLASH geometry */ + + priv->sectorshift = M25P_MT25Q1G_SECTOR_SHIFT; + priv->nsectors = M25P_MT25Q1G_NSECTORS; + priv->pageshift = M25P_MT25Q1G_PAGE_SHIFT; + priv->npages = M25P_MT25Q1G_NPAGES; +#ifdef CONFIG_M25P_SUBSECTOR_ERASE + priv->subsectorshift = M25P_MT25Q1G_SUBSECT_SHIFT; +#endif + return OK; + } + } + + return -ENODEV; +} + +static int imx9_flexspi_nor_read_status( + const struct imx9_flexspi_nor_dev_s *dev, + uint32_t *status) +{ + int stat; + + struct flexspi_transfer_s transfer = + { + .device_address = 0, + .port = dev->port, + .cmd_type = FLEXSPI_READ, + .seq_number = 1, + .seq_index = READ_STATUS_REG, + .data = status, + .data_size = 1, + }; + + stat = FLEXSPI_TRANSFER(dev->flexspi, &transfer); + if (stat != 0) + { + return -EIO; + } + + return 0; +} + +static int imx9_flexspi_nor_write_status( + const struct imx9_flexspi_nor_dev_s *dev, + uint32_t *status) +{ + int stat; + + struct flexspi_transfer_s transfer = + { + .device_address = 0, + .port = dev->port, + .cmd_type = FLEXSPI_WRITE, + .seq_number = 1, + .seq_index = WRITE_STATUS_REG, + .data = status, + .data_size = 1, + }; + + stat = FLEXSPI_TRANSFER(dev->flexspi, &transfer); + if (stat != 0) + { + return -EIO; + } + + return 0; +} + +static int imx9_flexspi_nor_write_enable( + const struct imx9_flexspi_nor_dev_s *dev) +{ + int stat; + + struct flexspi_transfer_s transfer = + { + .device_address = 0, + .port = dev->port, + .cmd_type = FLEXSPI_COMMAND, + .seq_number = 1, + .seq_index = WRITE_ENABLE, + .data = NULL, + .data_size = 0, + }; + + stat = FLEXSPI_TRANSFER(dev->flexspi, &transfer); + if (stat != 0) + { + return -EIO; + } + + return 0; +} + +static int imx9_flexspi_nor_erase_sector( + const struct imx9_flexspi_nor_dev_s *dev, + off_t offset) +{ + int stat; + + struct flexspi_transfer_s transfer = + { + .device_address = offset, + .port = dev->port, + .cmd_type = FLEXSPI_COMMAND, + .seq_number = 1, + .seq_index = ERASE_SECTOR, + .data = NULL, + .data_size = 0, + }; + + stat = FLEXSPI_TRANSFER(dev->flexspi, &transfer); + if (stat != 0) + { + return -EIO; + } + + return 0; +} + +static int imx9_flexspi_nor_erase_chip( + const struct imx9_flexspi_nor_dev_s *dev) +{ + int stat; + + struct flexspi_transfer_s transfer = + { + .device_address = 0, + .port = dev->port, + .cmd_type = FLEXSPI_COMMAND, + .seq_number = 1, + .seq_index = ERASE_CHIP, + .data = NULL, + .data_size = 0, + }; + + stat = FLEXSPI_TRANSFER(dev->flexspi, &transfer); + if (stat != 0) + { + return -EIO; + } + + return 0; +} + +static int imx9_flexspi_nor_page_program( + const struct imx9_flexspi_nor_dev_s *dev, + off_t offset, + const void *buffer, + size_t len) +{ + int stat; + + struct flexspi_transfer_s transfer = + { + .device_address = offset, + .port = dev->port, + .cmd_type = FLEXSPI_WRITE, + .seq_number = 1, + .seq_index = PAGE_PROGRAM_QUAD_INPUT, + .data = (uint32_t *) buffer, + .data_size = len, + }; + + up_clean_dcache((uintptr_t)buffer, (uintptr_t)buffer + len); + + stat = FLEXSPI_TRANSFER(dev->flexspi, &transfer); + if (stat != 0) + { + return -EIO; + } + + return 0; +} + +static int imx9_flexspi_nor_wait_bus_busy( + const struct imx9_flexspi_nor_dev_s *dev) +{ + uint32_t status = 0; + int ret; + + do + { + ret = imx9_flexspi_nor_read_status(dev, &status); + if (ret) + { + return ret; + } + } + while (status & 1); + + return 0; +} + +static int imx9_flexspi_nor_enable_quad_mode( + const struct imx9_flexspi_nor_dev_s *dev) +{ + uint32_t status = 0x40; + + imx9_flexspi_nor_write_status(dev, &status); + imx9_flexspi_nor_wait_bus_busy(dev); + FLEXSPI_SOFTWARE_RESET(dev->flexspi); + + return 0; +} + +static ssize_t imx9_flexspi_nor_read(struct mtd_dev_s *dev, + off_t offset, + size_t nbytes, + uint8_t *buffer) +{ + struct imx9_flexspi_nor_dev_s *priv = + (struct imx9_flexspi_nor_dev_s *)dev; + uint8_t *src; + + finfo("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes); + + if (priv->port >= FLEXSPI_PORT_COUNT) + { + return -EIO; + } + + src = priv->ahb_base + offset; + DEBUGASSERT(((uintptr_t)src & ALIGN_MASK) == 0); + + up_invalidate_dcache((uintptr_t)src, + (uintptr_t)src + ALIGN_UP(nbytes)); + + memcpy(buffer, src, nbytes); + + finfo("return nbytes: %d\n", (int)nbytes); + return (ssize_t)nbytes; +} + +static ssize_t imx9_flexspi_nor_bread(struct mtd_dev_s *dev, + off_t startblock, + size_t nblocks, + uint8_t *buffer) +{ + struct imx9_flexspi_nor_dev_s *priv = + (struct imx9_flexspi_nor_dev_s *)dev; + ssize_t nbytes; + + finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks); + + /* On this device, we can handle the block read just like the byte-oriented + * read + */ + + nbytes = imx9_flexspi_nor_read(dev, startblock << priv->pageshift, + nblocks << priv->pageshift, buffer); + if (nbytes > 0) + { + nbytes >>= priv->pageshift; + } + + return nbytes; +} + +static ssize_t imx9_flexspi_nor_bwrite(struct mtd_dev_s *dev, + off_t startblock, + size_t nblocks, + const uint8_t *src) +{ + struct imx9_flexspi_nor_dev_s *priv = + (struct imx9_flexspi_nor_dev_s *)dev; + size_t pgsize = 1 << priv->pageshift; + size_t len = nblocks << priv->pageshift; + off_t offset = startblock << priv->pageshift; + int i; + + finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks); + + while (len) + { + i = MIN(pgsize, len); + imx9_flexspi_nor_write_enable(priv); + imx9_flexspi_nor_page_program(priv, offset, src, i); + imx9_flexspi_nor_wait_bus_busy(priv); + FLEXSPI_SOFTWARE_RESET(priv->flexspi); + offset += i; + src += i; + len -= i; + } + + return nblocks; +} + +static int imx9_flexspi_nor_erase(struct mtd_dev_s *dev, + off_t startblock, + size_t nblocks) +{ + struct imx9_flexspi_nor_dev_s *priv = + (struct imx9_flexspi_nor_dev_s *)dev; + size_t blocksleft = nblocks; + uint8_t *dst = priv->ahb_base + (startblock << priv->subsectorshift); + + finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks); + + while (blocksleft-- > 0) + { + /* Erase each sector */ + + imx9_flexspi_nor_write_enable(priv); + imx9_flexspi_nor_erase_sector(priv, + startblock << priv->subsectorshift); + imx9_flexspi_nor_wait_bus_busy(priv); + FLEXSPI_SOFTWARE_RESET(priv->flexspi); + startblock++; + } + + up_invalidate_dcache((uintptr_t)dst, + (uintptr_t)dst + (nblocks << priv->subsectorshift)); + + return (int)nblocks; +} + +static int imx9_flexspi_nor_ioctl(struct mtd_dev_s *dev, + int cmd, + unsigned long arg) +{ + struct imx9_flexspi_nor_dev_s *priv = + (struct imx9_flexspi_nor_dev_s *)dev; + int ret = -EINVAL; /* Assume good command with bad parameters */ + + switch (cmd) + { + case MTDIOC_GEOMETRY: + { + struct mtd_geometry_s *geo = + (struct mtd_geometry_s *)((uintptr_t)arg); + if (geo) + { + memset(geo, 0, sizeof(*geo)); + + /* Populate the geometry structure with information need to + * know the capacity and how to access the device. + * + * NOTE: + * that the device is treated as though it where just an array + * of fixed size blocks. That is most likely not true, but the + * client will expect the device logic to do whatever is + * necessary to make it appear so. + */ + + geo->blocksize = (1 << priv->pageshift); +#ifdef CONFIG_M25P_SUBSECTOR_ERASE + if (priv->subsectorshift > 0) + { + geo->erasesize = (1 << priv->subsectorshift); + geo->neraseblocks = priv->nsectors * + (1 << (priv->sectorshift - + priv->subsectorshift)); + } + else +#endif + { + geo->erasesize = (1 << priv->sectorshift); + geo->neraseblocks = priv->nsectors; + } + + ret = OK; + + finfo("blocksize: %u erasesize: %u neraseblocks: %u\n", + geo->blocksize, geo->erasesize, geo->neraseblocks); + } + } + break; + + case BIOC_PARTINFO: + + break; + + case MTDIOC_BULKERASE: + { + /* Erase the entire device */ + + imx9_flexspi_nor_write_enable(priv); + ret = imx9_flexspi_nor_erase_chip(priv); + if (ret) + { + ferr("bulk erase failed\n"); + } + + imx9_flexspi_nor_wait_bus_busy(priv); + FLEXSPI_SOFTWARE_RESET(priv->flexspi); + } + break; + + case MTDIOC_PROTECT: + + /* TODO */ + + break; + + case MTDIOC_UNPROTECT: + + /* TODO */ + + break; + + default: + ret = -ENOTTY; /* Bad/unsupported command */ + break; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_flexspi_nor_initialize + * + * Description: + * Initialize a NOR FLASH on FlexSPI interface + * + * Input Parameters: + * None + * + * Returned Value: + * Pointer to an mtd device, Null on any error + * + ****************************************************************************/ + +struct mtd_dev_s *imx9_flexspi_nor_initialize(int intf) +{ + /* Configure multiplexed pins as connected on the board */ + + imx9_iomux_configure(MUX_FLEXSPI_IO0); + imx9_iomux_configure(MUX_FLEXSPI_IO1); + imx9_iomux_configure(MUX_FLEXSPI_IO2); + imx9_iomux_configure(MUX_FLEXSPI_IO3); + imx9_iomux_configure(MUX_FLEXSPI_CMD); + imx9_iomux_configure(MUX_FLEXSPI_CLK); + + g_flexspi_nor.flexspi = imx9_flexspi_initialize(intf); + if (!g_flexspi_nor.flexspi) + { + return NULL; + } + + FLEXSPI_SET_DEVICE_CONFIG(g_flexspi_nor.flexspi, + g_flexspi_nor.config, + g_flexspi_nor.port); + FLEXSPI_UPDATE_LUT(g_flexspi_nor.flexspi, + 0, + (const uint32_t *)g_flexspi_nor_lut, + sizeof(g_flexspi_nor_lut) / 4); + FLEXSPI_SOFTWARE_RESET(g_flexspi_nor.flexspi); + + if (imx9_flexspi_nor_get_id(&g_flexspi_nor)) + { + return NULL; + } + + if (imx9_flexspi_nor_enable_quad_mode(&g_flexspi_nor)) + { + return NULL; + } + + return &g_flexspi_nor.mtd; +} + +#endif /* CONFIG_IMX9_FLEXSPI_NOR */ diff --git a/arch/arm64/src/imx9/imx9_flexspi_nor.h b/arch/arm64/src/imx9/imx9_flexspi_nor.h new file mode 100644 index 0000000000000..71f8d31ac46cf --- /dev/null +++ b/arch/arm64/src/imx9/imx9_flexspi_nor.h @@ -0,0 +1,83 @@ +/**************************************************************************** + * arch/arm64/src/imx9/imx9_flexspi_nor.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM64_SRC_IMX9_IMX9_FLEXSPI_NOR_H +#define __ARCH_ARM64_SRC_IMX9_IMX9_FLEXSPI_NOR_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/mtd/mtd.h> + +#ifdef CONFIG_IMX9_FLEXSPI_NOR + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_flexspi_nor_initialize + * + * Description: + * Initialize a NOR FLASH on FlexSPI interface + * + * Input Parameters: + * intf: FlexSPI interface number + * + * Returned Value: + * Pointer to an mtd device, NULL on any error + * + ****************************************************************************/ + +struct mtd_dev_s *imx9_flexspi_nor_initialize(int intf); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_IMX9_FLEXSPI_NOR */ +#endif /* __ARCH_ARM_SRC_IMX9_IMX9_FLEXSPI_NOR_H */