194e25b7aSWojciech Macek /*- 294e25b7aSWojciech Macek * Copyright (c) 2021 Alstom Group. 394e25b7aSWojciech Macek * Copyright (c) 2021 Semihalf. 494e25b7aSWojciech Macek * 594e25b7aSWojciech Macek * Redistribution and use in source and binary forms, with or without 694e25b7aSWojciech Macek * modification, are permitted provided that the following conditions 794e25b7aSWojciech Macek * are met: 894e25b7aSWojciech Macek * 1. Redistributions of source code must retain the above copyright 994e25b7aSWojciech Macek * notice, this list of conditions and the following disclaimer. 1094e25b7aSWojciech Macek * 2. Redistributions in binary form must reproduce the above copyright 1194e25b7aSWojciech Macek * notice, this list of conditions and the following disclaimer in the 1294e25b7aSWojciech Macek * documentation and/or other materials provided with the distribution. 1394e25b7aSWojciech Macek * 1494e25b7aSWojciech Macek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1594e25b7aSWojciech Macek * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1694e25b7aSWojciech Macek * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1794e25b7aSWojciech Macek * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1894e25b7aSWojciech Macek * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1994e25b7aSWojciech Macek * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2094e25b7aSWojciech Macek * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2194e25b7aSWojciech Macek * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2294e25b7aSWojciech Macek * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2394e25b7aSWojciech Macek * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2494e25b7aSWojciech Macek */ 2594e25b7aSWojciech Macek 2694e25b7aSWojciech Macek #include <sys/cdefs.h> 2794e25b7aSWojciech Macek #include "opt_platform.h" 2894e25b7aSWojciech Macek 2994e25b7aSWojciech Macek #include <sys/param.h> 3094e25b7aSWojciech Macek #include <sys/systm.h> 3194e25b7aSWojciech Macek #include <sys/bio.h> 3294e25b7aSWojciech Macek #include <sys/endian.h> 3394e25b7aSWojciech Macek #include <sys/kernel.h> 3494e25b7aSWojciech Macek #include <sys/kthread.h> 3594e25b7aSWojciech Macek #include <sys/lock.h> 3694e25b7aSWojciech Macek #include <sys/malloc.h> 3794e25b7aSWojciech Macek #include <sys/module.h> 3894e25b7aSWojciech Macek #include <sys/mutex.h> 3994e25b7aSWojciech Macek #include <sys/rman.h> 4094e25b7aSWojciech Macek 4194e25b7aSWojciech Macek #include <geom/geom_disk.h> 4294e25b7aSWojciech Macek 4394e25b7aSWojciech Macek #include <machine/bus.h> 4494e25b7aSWojciech Macek 45*be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h> 4694e25b7aSWojciech Macek #include <dev/fdt/fdt_common.h> 4794e25b7aSWojciech Macek #include <dev/ofw/ofw_bus_subr.h> 4894e25b7aSWojciech Macek 4994e25b7aSWojciech Macek #include <vm/pmap.h> 5094e25b7aSWojciech Macek 5194e25b7aSWojciech Macek #include "flex_spi.h" 5294e25b7aSWojciech Macek 5394e25b7aSWojciech Macek static MALLOC_DEFINE(SECTOR_BUFFER, "flex_spi", "FSL QSPI sector buffer memory"); 5494e25b7aSWojciech Macek 5594e25b7aSWojciech Macek #define AHB_LUT_ID 31 5694e25b7aSWojciech Macek #define MHZ(x) ((x)*1000*1000) 5794e25b7aSWojciech Macek #define SPI_DEFAULT_CLK_RATE (MHZ(10)) 5894e25b7aSWojciech Macek 5994e25b7aSWojciech Macek static int driver_flags = 0; 6094e25b7aSWojciech Macek SYSCTL_NODE(_hw, OID_AUTO, flex_spi, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 6194e25b7aSWojciech Macek "FlexSPI driver parameters"); 6294e25b7aSWojciech Macek SYSCTL_INT(_hw_flex_spi, OID_AUTO, driver_flags, CTLFLAG_RDTUN, &driver_flags, 0, 6394e25b7aSWojciech Macek "Configuration flags and quirks"); 6494e25b7aSWojciech Macek 6594e25b7aSWojciech Macek static struct ofw_compat_data flex_spi_compat_data[] = { 6694e25b7aSWojciech Macek {"nxp,lx2160a-fspi", true}, 6794e25b7aSWojciech Macek {NULL, false} 6894e25b7aSWojciech Macek }; 6994e25b7aSWojciech Macek 7094e25b7aSWojciech Macek struct flex_spi_flash_info { 7194e25b7aSWojciech Macek char* name; 7294e25b7aSWojciech Macek uint32_t jedecid; 7394e25b7aSWojciech Macek uint32_t sectorsize; 7494e25b7aSWojciech Macek uint32_t sectorcount; 7594e25b7aSWojciech Macek uint32_t erasesize; 7694e25b7aSWojciech Macek uint32_t maxclk; 7794e25b7aSWojciech Macek }; 7894e25b7aSWojciech Macek 7994e25b7aSWojciech Macek /* Add information about supported Flashes. TODO: use SFDP instead */ 8094e25b7aSWojciech Macek static struct flex_spi_flash_info flex_spi_flash_info[] = { 8194e25b7aSWojciech Macek {"W25Q128JW", 0x001860ef, 64*1024, 256, 4096, MHZ(100)}, 8294e25b7aSWojciech Macek {NULL, 0, 0, 0, 0, 0} 8394e25b7aSWojciech Macek }; 8494e25b7aSWojciech Macek 8594e25b7aSWojciech Macek struct flex_spi_softc 8694e25b7aSWojciech Macek { 8794e25b7aSWojciech Macek device_t dev; 8894e25b7aSWojciech Macek unsigned int flags; 8994e25b7aSWojciech Macek 9094e25b7aSWojciech Macek struct bio_queue_head bio_queue; 9194e25b7aSWojciech Macek struct mtx disk_mtx; 9294e25b7aSWojciech Macek struct disk *disk; 9394e25b7aSWojciech Macek struct proc *p; 9494e25b7aSWojciech Macek unsigned int taskstate; 9594e25b7aSWojciech Macek uint8_t *buf; 9694e25b7aSWojciech Macek 9794e25b7aSWojciech Macek struct resource *ahb_mem_res; 9894e25b7aSWojciech Macek struct resource *mem_res; 9994e25b7aSWojciech Macek 10094e25b7aSWojciech Macek clk_t fspi_clk_en; 10194e25b7aSWojciech Macek clk_t fspi_clk; 10294e25b7aSWojciech Macek uint64_t fspi_clk_en_hz; 10394e25b7aSWojciech Macek uint64_t fspi_clk_hz; 10494e25b7aSWojciech Macek 10594e25b7aSWojciech Macek /* TODO: support more than one Flash per bus */ 10694e25b7aSWojciech Macek uint64_t fspi_max_clk; 10794e25b7aSWojciech Macek uint32_t quirks; 10894e25b7aSWojciech Macek 10994e25b7aSWojciech Macek /* Flash parameters */ 11094e25b7aSWojciech Macek uint32_t sectorsize; 11194e25b7aSWojciech Macek uint32_t sectorcount; 11294e25b7aSWojciech Macek uint32_t erasesize; 11394e25b7aSWojciech Macek }; 11494e25b7aSWojciech Macek 11594e25b7aSWojciech Macek static int flex_spi_read(struct flex_spi_softc *sc, off_t offset, caddr_t data, 11694e25b7aSWojciech Macek size_t count); 11794e25b7aSWojciech Macek static int flex_spi_write(struct flex_spi_softc *sc, off_t offset, 11894e25b7aSWojciech Macek uint8_t *data, size_t size); 11994e25b7aSWojciech Macek 12094e25b7aSWojciech Macek static int flex_spi_attach(device_t dev); 12194e25b7aSWojciech Macek static int flex_spi_probe(device_t dev); 12294e25b7aSWojciech Macek static int flex_spi_detach(device_t dev); 12394e25b7aSWojciech Macek 12494e25b7aSWojciech Macek /* disk routines */ 12594e25b7aSWojciech Macek static int flex_spi_open(struct disk *dp); 12694e25b7aSWojciech Macek static int flex_spi_close(struct disk *dp); 12794e25b7aSWojciech Macek static int flex_spi_ioctl(struct disk *, u_long, void *, int, struct thread *); 12894e25b7aSWojciech Macek static void flex_spi_strategy(struct bio *bp); 12994e25b7aSWojciech Macek static int flex_spi_getattr(struct bio *bp); 13094e25b7aSWojciech Macek static void flex_spi_task(void *arg); 13194e25b7aSWojciech Macek 13294e25b7aSWojciech Macek static uint32_t 13394e25b7aSWojciech Macek read_reg(struct flex_spi_softc *sc, uint32_t offset) 13494e25b7aSWojciech Macek { 13594e25b7aSWojciech Macek 13694e25b7aSWojciech Macek return ((bus_read_4(sc->mem_res, offset))); 13794e25b7aSWojciech Macek } 13894e25b7aSWojciech Macek 13994e25b7aSWojciech Macek static void 14094e25b7aSWojciech Macek write_reg(struct flex_spi_softc *sc, uint32_t offset, uint32_t value) 14194e25b7aSWojciech Macek { 14294e25b7aSWojciech Macek 14394e25b7aSWojciech Macek bus_write_4(sc->mem_res, offset, (value)); 14494e25b7aSWojciech Macek } 14594e25b7aSWojciech Macek 14694e25b7aSWojciech Macek static int 14794e25b7aSWojciech Macek reg_read_poll_tout(struct flex_spi_softc *sc, uint32_t offset, uint32_t mask, 14894e25b7aSWojciech Macek uint32_t delay_us, uint32_t iterations, bool positive) 14994e25b7aSWojciech Macek { 15094e25b7aSWojciech Macek uint32_t reg; 15194e25b7aSWojciech Macek uint32_t condition = 0; 15294e25b7aSWojciech Macek 15394e25b7aSWojciech Macek do { 15494e25b7aSWojciech Macek reg = read_reg(sc, offset); 15594e25b7aSWojciech Macek if (positive) 15694e25b7aSWojciech Macek condition = ((reg & mask) == 0); 15794e25b7aSWojciech Macek else 15894e25b7aSWojciech Macek condition = ((reg & mask) != 0); 15994e25b7aSWojciech Macek 16094e25b7aSWojciech Macek if (condition == 0) 16194e25b7aSWojciech Macek break; 16294e25b7aSWojciech Macek 16394e25b7aSWojciech Macek DELAY(delay_us); 16494e25b7aSWojciech Macek } while (condition && (--iterations > 0)); 16594e25b7aSWojciech Macek 16694e25b7aSWojciech Macek return (condition != 0); 16794e25b7aSWojciech Macek } 16894e25b7aSWojciech Macek 16994e25b7aSWojciech Macek static int 17094e25b7aSWojciech Macek flex_spi_clk_setup(struct flex_spi_softc *sc, uint32_t rate) 17194e25b7aSWojciech Macek { 17294e25b7aSWojciech Macek int ret = 0; 17394e25b7aSWojciech Macek 17494e25b7aSWojciech Macek /* disable to avoid glitching */ 17594e25b7aSWojciech Macek ret |= clk_disable(sc->fspi_clk_en); 17694e25b7aSWojciech Macek ret |= clk_disable(sc->fspi_clk); 17794e25b7aSWojciech Macek 17894e25b7aSWojciech Macek ret |= clk_set_freq(sc->fspi_clk, rate, 0); 17994e25b7aSWojciech Macek sc->fspi_clk_hz = rate; 18094e25b7aSWojciech Macek 18194e25b7aSWojciech Macek /* enable clocks back */ 18294e25b7aSWojciech Macek ret |= clk_enable(sc->fspi_clk_en); 18394e25b7aSWojciech Macek ret |= clk_enable(sc->fspi_clk); 18494e25b7aSWojciech Macek 18594e25b7aSWojciech Macek if (ret) 18694e25b7aSWojciech Macek return (EINVAL); 18794e25b7aSWojciech Macek 18894e25b7aSWojciech Macek return (0); 18994e25b7aSWojciech Macek } 19094e25b7aSWojciech Macek 19194e25b7aSWojciech Macek static void 19294e25b7aSWojciech Macek flex_spi_prepare_lut(struct flex_spi_softc *sc, uint8_t op) 19394e25b7aSWojciech Macek { 19494e25b7aSWojciech Macek uint32_t lut_id; 19594e25b7aSWojciech Macek uint32_t lut; 19694e25b7aSWojciech Macek 19794e25b7aSWojciech Macek /* unlock LUT */ 19894e25b7aSWojciech Macek write_reg(sc, FSPI_LUTKEY, FSPI_LUTKEY_VALUE); 19994e25b7aSWojciech Macek write_reg(sc, FSPI_LCKCR, FSPI_LCKER_UNLOCK); 20094e25b7aSWojciech Macek 20194e25b7aSWojciech Macek /* Read JEDEC ID */ 20294e25b7aSWojciech Macek lut_id = 0; 20394e25b7aSWojciech Macek 20494e25b7aSWojciech Macek switch (op) { 20594e25b7aSWojciech Macek case LUT_FLASH_CMD_JEDECID: 20694e25b7aSWojciech Macek lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_READ_IDENT); 20794e25b7aSWojciech Macek lut |= LUT_DEF(1, LUT_NXP_READ, LUT_PAD(1), 0); 20894e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id), lut); 20994e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id) + 4, 0); 21094e25b7aSWojciech Macek break; 21194e25b7aSWojciech Macek case LUT_FLASH_CMD_READ: 21294e25b7aSWojciech Macek lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_FAST_READ); 21394e25b7aSWojciech Macek lut |= LUT_DEF(1, LUT_ADDR, LUT_PAD(1), 3*8); 21494e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id), lut); 21594e25b7aSWojciech Macek lut = LUT_DEF(0, LUT_DUMMY, LUT_PAD(1), 1*8); 21694e25b7aSWojciech Macek lut |= LUT_DEF(1, LUT_NXP_READ, LUT_PAD(1), 0); 21794e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id) + 4, lut); 21894e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id) + 8, 0); 21994e25b7aSWojciech Macek break; 22094e25b7aSWojciech Macek case LUT_FLASH_CMD_STATUS_READ: 22194e25b7aSWojciech Macek lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_READ_STATUS); 22294e25b7aSWojciech Macek lut |= LUT_DEF(1, LUT_NXP_READ, LUT_PAD(1), 0); 22394e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id), lut); 22494e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id) + 4, 0); 22594e25b7aSWojciech Macek break; 22694e25b7aSWojciech Macek case LUT_FLASH_CMD_PAGE_PROGRAM: 22794e25b7aSWojciech Macek lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_PAGE_PROGRAM); 22894e25b7aSWojciech Macek lut |= LUT_DEF(1, LUT_ADDR, LUT_PAD(1), 3*8); 22994e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id), lut); 23094e25b7aSWojciech Macek lut = LUT_DEF(0, LUT_NXP_WRITE, LUT_PAD(1), 0); 23194e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id) + 4, lut); 23294e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id) + 8, 0); 23394e25b7aSWojciech Macek break; 23494e25b7aSWojciech Macek case LUT_FLASH_CMD_WRITE_ENABLE: 23594e25b7aSWojciech Macek lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_WRITE_ENABLE); 23694e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id), lut); 23794e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id) + 4, 0); 23894e25b7aSWojciech Macek break; 23994e25b7aSWojciech Macek case LUT_FLASH_CMD_WRITE_DISABLE: 24094e25b7aSWojciech Macek lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_WRITE_DISABLE); 24194e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id), lut); 24294e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id) + 4, 0); 24394e25b7aSWojciech Macek break; 24494e25b7aSWojciech Macek case LUT_FLASH_CMD_SECTOR_ERASE: 24594e25b7aSWojciech Macek lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_SECTOR_ERASE); 24694e25b7aSWojciech Macek lut |= LUT_DEF(1, LUT_ADDR, LUT_PAD(1), 3*8); 24794e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id), lut); 24894e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id) + 4, 0); 24994e25b7aSWojciech Macek break; 25094e25b7aSWojciech Macek default: 25194e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id), 0); 25294e25b7aSWojciech Macek } 25394e25b7aSWojciech Macek 25494e25b7aSWojciech Macek /* lock LUT */ 25594e25b7aSWojciech Macek write_reg(sc, FSPI_LUTKEY, FSPI_LUTKEY_VALUE); 25694e25b7aSWojciech Macek write_reg(sc, FSPI_LCKCR, FSPI_LCKER_LOCK); 25794e25b7aSWojciech Macek } 25894e25b7aSWojciech Macek 25994e25b7aSWojciech Macek static void 26094e25b7aSWojciech Macek flex_spi_prepare_ahb_lut(struct flex_spi_softc *sc) 26194e25b7aSWojciech Macek { 26294e25b7aSWojciech Macek uint32_t lut_id; 26394e25b7aSWojciech Macek uint32_t lut; 26494e25b7aSWojciech Macek 26594e25b7aSWojciech Macek /* unlock LUT */ 26694e25b7aSWojciech Macek write_reg(sc, FSPI_LUTKEY, FSPI_LUTKEY_VALUE); 26794e25b7aSWojciech Macek write_reg(sc, FSPI_LCKCR, FSPI_LCKER_UNLOCK); 26894e25b7aSWojciech Macek 26994e25b7aSWojciech Macek lut_id = AHB_LUT_ID; 27094e25b7aSWojciech Macek lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_FAST_READ); 27194e25b7aSWojciech Macek lut |= LUT_DEF(1, LUT_ADDR, LUT_PAD(1), 3*8); 27294e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id), lut); 27394e25b7aSWojciech Macek lut = LUT_DEF(0, LUT_DUMMY, LUT_PAD(1), 1*8); 27494e25b7aSWojciech Macek lut |= LUT_DEF(1, LUT_NXP_READ, LUT_PAD(1), 0); 27594e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id) + 4, lut); 27694e25b7aSWojciech Macek write_reg(sc, FSPI_LUT_REG(lut_id) + 8, 0); 27794e25b7aSWojciech Macek 27894e25b7aSWojciech Macek /* lock LUT */ 27994e25b7aSWojciech Macek write_reg(sc, FSPI_LUTKEY, FSPI_LUTKEY_VALUE); 28094e25b7aSWojciech Macek write_reg(sc, FSPI_LCKCR, FSPI_LCKER_LOCK); 28194e25b7aSWojciech Macek } 28294e25b7aSWojciech Macek 28394e25b7aSWojciech Macek #define DIR_READ 0 28494e25b7aSWojciech Macek #define DIR_WRITE 1 28594e25b7aSWojciech Macek 28694e25b7aSWojciech Macek static void 28794e25b7aSWojciech Macek flex_spi_read_rxfifo(struct flex_spi_softc *sc, uint8_t *buf, uint8_t size) 28894e25b7aSWojciech Macek { 28994e25b7aSWojciech Macek int i, ret, reg; 29094e25b7aSWojciech Macek 29194e25b7aSWojciech Macek /* 29294e25b7aSWojciech Macek * Default value of water mark level is 8 bytes, hence in single 29394e25b7aSWojciech Macek * read request controller can read max 8 bytes of data. 29494e25b7aSWojciech Macek */ 29594e25b7aSWojciech Macek for (i = 0; i < size; i += 4) { 29694e25b7aSWojciech Macek /* Wait for RXFIFO available */ 29794e25b7aSWojciech Macek if (i % 8 == 0) { 29894e25b7aSWojciech Macek ret = reg_read_poll_tout(sc, FSPI_INTR, FSPI_INTR_IPRXWA, 29994e25b7aSWojciech Macek 1, 50000, 1); 30094e25b7aSWojciech Macek if (ret) 30194e25b7aSWojciech Macek device_printf(sc->dev, 30294e25b7aSWojciech Macek "timed out waiting for FSPI_INTR_IPRXWA\n"); 30394e25b7aSWojciech Macek } 30494e25b7aSWojciech Macek 30594e25b7aSWojciech Macek if (i % 8 == 0) 30694e25b7aSWojciech Macek reg = read_reg(sc, FSPI_RFDR); 30794e25b7aSWojciech Macek else 30894e25b7aSWojciech Macek reg = read_reg(sc, FSPI_RFDR + 4); 30994e25b7aSWojciech Macek 31094e25b7aSWojciech Macek if (size >= (i + 4)) 31194e25b7aSWojciech Macek *(uint32_t *)(buf + i) = reg; 31294e25b7aSWojciech Macek else 31394e25b7aSWojciech Macek memcpy(buf + i, ®, size - i); 31494e25b7aSWojciech Macek 31594e25b7aSWojciech Macek /* move the FIFO pointer */ 31694e25b7aSWojciech Macek if (i % 8 != 0) 31794e25b7aSWojciech Macek write_reg(sc, FSPI_INTR, FSPI_INTR_IPRXWA); 31894e25b7aSWojciech Macek } 31994e25b7aSWojciech Macek 32094e25b7aSWojciech Macek /* invalid the RXFIFO */ 32194e25b7aSWojciech Macek write_reg(sc, FSPI_IPRXFCR, FSPI_IPRXFCR_CLR); 32294e25b7aSWojciech Macek /* move the FIFO pointer */ 32394e25b7aSWojciech Macek write_reg(sc, FSPI_INTR, FSPI_INTR_IPRXWA); 32494e25b7aSWojciech Macek } 32594e25b7aSWojciech Macek 32694e25b7aSWojciech Macek static void 32794e25b7aSWojciech Macek flex_spi_write_txfifo(struct flex_spi_softc *sc, uint8_t *buf, uint8_t size) 32894e25b7aSWojciech Macek { 32994e25b7aSWojciech Macek int i, ret, reg; 33094e25b7aSWojciech Macek 33194e25b7aSWojciech Macek /* invalid the TXFIFO */ 33294e25b7aSWojciech Macek write_reg(sc, FSPI_IPRXFCR, FSPI_IPTXFCR_CLR); 33394e25b7aSWojciech Macek 33494e25b7aSWojciech Macek /* 33594e25b7aSWojciech Macek * Default value of water mark level is 8 bytes, hence in single 33694e25b7aSWojciech Macek * read request controller can read max 8 bytes of data. 33794e25b7aSWojciech Macek */ 33894e25b7aSWojciech Macek for (i = 0; i < size; i += 4) { 33994e25b7aSWojciech Macek /* Wait for RXFIFO available */ 34094e25b7aSWojciech Macek if (i % 8 == 0) { 34194e25b7aSWojciech Macek ret = reg_read_poll_tout(sc, FSPI_INTR, FSPI_INTR_IPTXWE, 34294e25b7aSWojciech Macek 1, 50000, 1); 34394e25b7aSWojciech Macek if (ret) 34494e25b7aSWojciech Macek device_printf(sc->dev, 34594e25b7aSWojciech Macek "timed out waiting for FSPI_INTR_IPRXWA\n"); 34694e25b7aSWojciech Macek } 34794e25b7aSWojciech Macek 34894e25b7aSWojciech Macek if (size >= (i + 4)) 34994e25b7aSWojciech Macek reg = *(uint32_t *)(buf + i); 35094e25b7aSWojciech Macek else { 35194e25b7aSWojciech Macek reg = 0; 35294e25b7aSWojciech Macek memcpy(®, buf + i, size - i); 35394e25b7aSWojciech Macek } 35494e25b7aSWojciech Macek 35594e25b7aSWojciech Macek if (i % 8 == 0) 35694e25b7aSWojciech Macek write_reg(sc, FSPI_TFDR, reg); 35794e25b7aSWojciech Macek else 35894e25b7aSWojciech Macek write_reg(sc, FSPI_TFDR + 4, reg); 35994e25b7aSWojciech Macek 36094e25b7aSWojciech Macek /* move the FIFO pointer */ 36194e25b7aSWojciech Macek if (i % 8 != 0) 36294e25b7aSWojciech Macek write_reg(sc, FSPI_INTR, FSPI_INTR_IPTXWE); 36394e25b7aSWojciech Macek } 36494e25b7aSWojciech Macek 36594e25b7aSWojciech Macek /* move the FIFO pointer */ 36694e25b7aSWojciech Macek write_reg(sc, FSPI_INTR, FSPI_INTR_IPTXWE); 36794e25b7aSWojciech Macek } 36894e25b7aSWojciech Macek 36994e25b7aSWojciech Macek static int 37094e25b7aSWojciech Macek flex_spi_do_op(struct flex_spi_softc *sc, uint32_t op, uint32_t addr, 37194e25b7aSWojciech Macek uint8_t *buf, uint8_t size, uint8_t dir) 37294e25b7aSWojciech Macek { 37394e25b7aSWojciech Macek 37494e25b7aSWojciech Macek uint32_t cnt = 1000, reg; 37594e25b7aSWojciech Macek 37694e25b7aSWojciech Macek reg = read_reg(sc, FSPI_IPRXFCR); 37794e25b7aSWojciech Macek /* invalidate RXFIFO first */ 37894e25b7aSWojciech Macek reg &= ~FSPI_IPRXFCR_DMA_EN; 37994e25b7aSWojciech Macek reg |= FSPI_IPRXFCR_CLR; 38094e25b7aSWojciech Macek write_reg(sc, FSPI_IPRXFCR, reg); 38194e25b7aSWojciech Macek 38294e25b7aSWojciech Macek /* Prepare LUT */ 38394e25b7aSWojciech Macek flex_spi_prepare_lut(sc, op); 38494e25b7aSWojciech Macek 38594e25b7aSWojciech Macek write_reg(sc, FSPI_IPCR0, addr); 38694e25b7aSWojciech Macek /* 38794e25b7aSWojciech Macek * Always start the sequence at the same index since we update 38894e25b7aSWojciech Macek * the LUT at each BIO operation. And also specify the DATA 38994e25b7aSWojciech Macek * length, since it's has not been specified in the LUT. 39094e25b7aSWojciech Macek */ 39194e25b7aSWojciech Macek write_reg(sc, FSPI_IPCR1, size | 39294e25b7aSWojciech Macek (0 << FSPI_IPCR1_SEQID_SHIFT) | (0 << FSPI_IPCR1_SEQNUM_SHIFT)); 39394e25b7aSWojciech Macek 39494e25b7aSWojciech Macek if ((size != 0) && (dir == DIR_WRITE)) 39594e25b7aSWojciech Macek flex_spi_write_txfifo(sc, buf, size); 39694e25b7aSWojciech Macek 39794e25b7aSWojciech Macek /* Trigger the LUT now. */ 39894e25b7aSWojciech Macek write_reg(sc, FSPI_IPCMD, FSPI_IPCMD_TRG); 39994e25b7aSWojciech Macek 40094e25b7aSWojciech Macek 40194e25b7aSWojciech Macek /* Wait for completion. */ 40294e25b7aSWojciech Macek do { 40394e25b7aSWojciech Macek reg = read_reg(sc, FSPI_INTR); 40494e25b7aSWojciech Macek if (reg & FSPI_INTR_IPCMDDONE) { 40594e25b7aSWojciech Macek write_reg(sc, FSPI_INTR, FSPI_INTR_IPCMDDONE); 40694e25b7aSWojciech Macek break; 40794e25b7aSWojciech Macek } 40894e25b7aSWojciech Macek DELAY(1); 40994e25b7aSWojciech Macek } while (--cnt); 41094e25b7aSWojciech Macek if (cnt == 0) { 41194e25b7aSWojciech Macek device_printf(sc->dev, "timed out waiting for command completion\n"); 41294e25b7aSWojciech Macek return (ETIMEDOUT); 41394e25b7aSWojciech Macek } 41494e25b7aSWojciech Macek 41594e25b7aSWojciech Macek /* Invoke IP data read, if request is of data read. */ 41694e25b7aSWojciech Macek if ((size != 0) && (dir == DIR_READ)) 41794e25b7aSWojciech Macek flex_spi_read_rxfifo(sc, buf, size); 41894e25b7aSWojciech Macek 41994e25b7aSWojciech Macek return (0); 42094e25b7aSWojciech Macek } 42194e25b7aSWojciech Macek 42294e25b7aSWojciech Macek static int 42394e25b7aSWojciech Macek flex_spi_wait_for_controller(struct flex_spi_softc *sc) 42494e25b7aSWojciech Macek { 42594e25b7aSWojciech Macek int err; 42694e25b7aSWojciech Macek 42794e25b7aSWojciech Macek /* Wait for controller being ready. */ 42894e25b7aSWojciech Macek err = reg_read_poll_tout(sc, FSPI_STS0, 42994e25b7aSWojciech Macek FSPI_STS0_ARB_IDLE, 1, POLL_TOUT, 1); 43094e25b7aSWojciech Macek 43194e25b7aSWojciech Macek return (err); 43294e25b7aSWojciech Macek } 43394e25b7aSWojciech Macek 43494e25b7aSWojciech Macek static int 43594e25b7aSWojciech Macek flex_spi_wait_for_flash(struct flex_spi_softc *sc) 43694e25b7aSWojciech Macek { 43794e25b7aSWojciech Macek int ret; 43894e25b7aSWojciech Macek uint32_t status = 0; 43994e25b7aSWojciech Macek 44094e25b7aSWojciech Macek ret = flex_spi_wait_for_controller(sc); 44194e25b7aSWojciech Macek if (ret != 0) { 44294e25b7aSWojciech Macek device_printf(sc->dev, "%s: timed out waiting for controller", __func__); 44394e25b7aSWojciech Macek return (ret); 44494e25b7aSWojciech Macek } 44594e25b7aSWojciech Macek 44694e25b7aSWojciech Macek do { 44794e25b7aSWojciech Macek ret = flex_spi_do_op(sc, LUT_FLASH_CMD_STATUS_READ, 0, (void*)&status, 44894e25b7aSWojciech Macek 1, DIR_READ); 44994e25b7aSWojciech Macek if (ret != 0) { 45094e25b7aSWojciech Macek device_printf(sc->dev, "ERROR: failed to get flash status\n"); 45194e25b7aSWojciech Macek return (ret); 45294e25b7aSWojciech Macek } 45394e25b7aSWojciech Macek 45494e25b7aSWojciech Macek } while (status & STATUS_WIP); 45594e25b7aSWojciech Macek 45694e25b7aSWojciech Macek return (0); 45794e25b7aSWojciech Macek } 45894e25b7aSWojciech Macek 45994e25b7aSWojciech Macek static int 46094e25b7aSWojciech Macek flex_spi_identify(struct flex_spi_softc *sc) 46194e25b7aSWojciech Macek { 46294e25b7aSWojciech Macek int ret; 46394e25b7aSWojciech Macek uint32_t id = 0; 46494e25b7aSWojciech Macek struct flex_spi_flash_info *finfo = flex_spi_flash_info; 46594e25b7aSWojciech Macek 46694e25b7aSWojciech Macek ret = flex_spi_do_op(sc, LUT_FLASH_CMD_JEDECID, 0, (void*)&id, sizeof(id), DIR_READ); 46794e25b7aSWojciech Macek if (ret != 0) { 46894e25b7aSWojciech Macek device_printf(sc->dev, "ERROR: failed to identify device\n"); 46994e25b7aSWojciech Macek return (ret); 47094e25b7aSWojciech Macek } 47194e25b7aSWojciech Macek 47294e25b7aSWojciech Macek /* XXX TODO: SFDP to be implemented */ 47394e25b7aSWojciech Macek while (finfo->jedecid != 0) { 47494e25b7aSWojciech Macek if (id == finfo->jedecid) { 47594e25b7aSWojciech Macek device_printf(sc->dev, "found %s Flash\n", finfo->name); 47694e25b7aSWojciech Macek sc->sectorsize = finfo->sectorsize; 47794e25b7aSWojciech Macek sc->sectorcount = finfo->sectorcount; 47894e25b7aSWojciech Macek sc->erasesize = finfo->erasesize; 47994e25b7aSWojciech Macek sc->fspi_max_clk = finfo->maxclk; 48094e25b7aSWojciech Macek return (0); 48194e25b7aSWojciech Macek } 48294e25b7aSWojciech Macek finfo++; 48394e25b7aSWojciech Macek } 48494e25b7aSWojciech Macek 48594e25b7aSWojciech Macek return (EINVAL); 48694e25b7aSWojciech Macek } 48794e25b7aSWojciech Macek 48894e25b7aSWojciech Macek static inline int 48994e25b7aSWojciech Macek flex_spi_force_ip_mode(struct flex_spi_softc *sc) 49094e25b7aSWojciech Macek { 49194e25b7aSWojciech Macek 49294e25b7aSWojciech Macek if (sc->quirks & FSPI_QUIRK_USE_IP_ONLY) 49394e25b7aSWojciech Macek return (1); 49494e25b7aSWojciech Macek if (driver_flags & FSPI_QUIRK_USE_IP_ONLY) 49594e25b7aSWojciech Macek return (1); 49694e25b7aSWojciech Macek 49794e25b7aSWojciech Macek return (0); 49894e25b7aSWojciech Macek } 49994e25b7aSWojciech Macek 50094e25b7aSWojciech Macek static int 50194e25b7aSWojciech Macek flex_spi_read(struct flex_spi_softc *sc, off_t offset, caddr_t data, 50294e25b7aSWojciech Macek size_t count) 50394e25b7aSWojciech Macek { 50494e25b7aSWojciech Macek int err; 50594e25b7aSWojciech Macek size_t len; 50694e25b7aSWojciech Macek 50794e25b7aSWojciech Macek /* Wait for controller being ready. */ 50894e25b7aSWojciech Macek err = flex_spi_wait_for_controller(sc); 50994e25b7aSWojciech Macek if (err) 51094e25b7aSWojciech Macek device_printf(sc->dev, 51194e25b7aSWojciech Macek "warning: spi_read, timed out waiting for controller"); 51294e25b7aSWojciech Macek 51394e25b7aSWojciech Macek /* Use AHB access whenever we can */ 51494e25b7aSWojciech Macek if (flex_spi_force_ip_mode(sc) != 0) { 51594e25b7aSWojciech Macek do { 51694e25b7aSWojciech Macek if (((offset % 4) != 0) || (count < 4)) { 51794e25b7aSWojciech Macek *(uint8_t*)data = bus_read_1(sc->ahb_mem_res, offset); 51894e25b7aSWojciech Macek data++; 51994e25b7aSWojciech Macek count--; 52094e25b7aSWojciech Macek offset++; 52194e25b7aSWojciech Macek } else { 52294e25b7aSWojciech Macek *(uint32_t*)data = bus_read_4(sc->ahb_mem_res, offset); 52394e25b7aSWojciech Macek data += 4; 52494e25b7aSWojciech Macek count -= 4; 52594e25b7aSWojciech Macek offset += 4; 52694e25b7aSWojciech Macek } 52794e25b7aSWojciech Macek } while (count); 52894e25b7aSWojciech Macek 52994e25b7aSWojciech Macek return (0); 53094e25b7aSWojciech Macek } 53194e25b7aSWojciech Macek 53294e25b7aSWojciech Macek do { 53394e25b7aSWojciech Macek len = min(64, count); 53494e25b7aSWojciech Macek err = flex_spi_do_op(sc, LUT_FLASH_CMD_READ, offset, (void*)data, 53594e25b7aSWojciech Macek len, DIR_READ); 53694e25b7aSWojciech Macek if (err) 53794e25b7aSWojciech Macek return (err); 53894e25b7aSWojciech Macek offset += len; 53994e25b7aSWojciech Macek data += len; 54094e25b7aSWojciech Macek count -= len; 54194e25b7aSWojciech Macek } while (count); 54294e25b7aSWojciech Macek 54394e25b7aSWojciech Macek return (0); 54494e25b7aSWojciech Macek } 54594e25b7aSWojciech Macek 54694e25b7aSWojciech Macek static int 54794e25b7aSWojciech Macek flex_spi_write(struct flex_spi_softc *sc, off_t offset, uint8_t *data, 54894e25b7aSWojciech Macek size_t size) 54994e25b7aSWojciech Macek { 55094e25b7aSWojciech Macek int ret = 0; 55194e25b7aSWojciech Macek size_t ptr; 55294e25b7aSWojciech Macek 55394e25b7aSWojciech Macek flex_spi_wait_for_flash(sc); 55494e25b7aSWojciech Macek ret = flex_spi_do_op(sc, LUT_FLASH_CMD_WRITE_ENABLE, offset, NULL, 55594e25b7aSWojciech Macek 0, DIR_READ); 55694e25b7aSWojciech Macek if (ret != 0) { 55794e25b7aSWojciech Macek device_printf(sc->dev, "ERROR: failed to enable writes\n"); 55894e25b7aSWojciech Macek return (ret); 55994e25b7aSWojciech Macek } 56094e25b7aSWojciech Macek flex_spi_wait_for_flash(sc); 56194e25b7aSWojciech Macek 56294e25b7aSWojciech Macek /* per-sector write */ 56394e25b7aSWojciech Macek while (size > 0) { 56494e25b7aSWojciech Macek uint32_t sector_base = rounddown2(offset, sc->erasesize); 56594e25b7aSWojciech Macek size_t size_in_sector = size; 56694e25b7aSWojciech Macek 56794e25b7aSWojciech Macek if (size_in_sector + offset > sector_base + sc->erasesize) 56894e25b7aSWojciech Macek size_in_sector = sector_base + sc->erasesize - offset; 56994e25b7aSWojciech Macek 57094e25b7aSWojciech Macek /* Read sector */ 57194e25b7aSWojciech Macek ret = flex_spi_read(sc, sector_base, sc->buf, sc->erasesize); 57294e25b7aSWojciech Macek if (ret != 0) { 57394e25b7aSWojciech Macek device_printf(sc->dev, "ERROR: failed to read sector %d\n", 57494e25b7aSWojciech Macek sector_base); 57594e25b7aSWojciech Macek goto exit; 57694e25b7aSWojciech Macek } 57794e25b7aSWojciech Macek 57894e25b7aSWojciech Macek /* Erase sector */ 57994e25b7aSWojciech Macek flex_spi_wait_for_flash(sc); 58094e25b7aSWojciech Macek ret = flex_spi_do_op(sc, LUT_FLASH_CMD_SECTOR_ERASE, offset, NULL, 58194e25b7aSWojciech Macek 0, DIR_READ); 58294e25b7aSWojciech Macek if (ret != 0) { 58394e25b7aSWojciech Macek device_printf(sc->dev, "ERROR: failed to erase sector %d\n", 58494e25b7aSWojciech Macek sector_base); 58594e25b7aSWojciech Macek goto exit; 58694e25b7aSWojciech Macek } 58794e25b7aSWojciech Macek 58894e25b7aSWojciech Macek /* Update buffer with input data */ 58994e25b7aSWojciech Macek memcpy(sc->buf + (offset - sector_base), data, size_in_sector); 59094e25b7aSWojciech Macek 59194e25b7aSWojciech Macek /* Write buffer back to the flash 59294e25b7aSWojciech Macek * Up to 32 bytes per single request, request cannot spread 59394e25b7aSWojciech Macek * across 256-byte page boundary 59494e25b7aSWojciech Macek */ 59594e25b7aSWojciech Macek for (ptr = 0; ptr < sc->erasesize; ptr += 32) { 59694e25b7aSWojciech Macek flex_spi_wait_for_flash(sc); 59794e25b7aSWojciech Macek ret = flex_spi_do_op(sc, LUT_FLASH_CMD_PAGE_PROGRAM, 59894e25b7aSWojciech Macek sector_base + ptr, (void*)(sc->buf + ptr), 32, DIR_WRITE); 59994e25b7aSWojciech Macek if (ret != 0) { 60094e25b7aSWojciech Macek device_printf(sc->dev, "ERROR: failed to write address %ld\n", 60194e25b7aSWojciech Macek sector_base + ptr); 60294e25b7aSWojciech Macek goto exit; 60394e25b7aSWojciech Macek } 60494e25b7aSWojciech Macek } 60594e25b7aSWojciech Macek 60694e25b7aSWojciech Macek /* update pointers */ 60794e25b7aSWojciech Macek size = size - size_in_sector; 60894e25b7aSWojciech Macek offset = offset + size; 60994e25b7aSWojciech Macek } 61094e25b7aSWojciech Macek 61194e25b7aSWojciech Macek flex_spi_wait_for_flash(sc); 61294e25b7aSWojciech Macek ret = flex_spi_do_op(sc, LUT_FLASH_CMD_WRITE_DISABLE, offset, (void*)sc->buf, 61394e25b7aSWojciech Macek 0, DIR_READ); 61494e25b7aSWojciech Macek if (ret != 0) { 61594e25b7aSWojciech Macek device_printf(sc->dev, "ERROR: failed to disable writes\n"); 61694e25b7aSWojciech Macek goto exit; 61794e25b7aSWojciech Macek } 61894e25b7aSWojciech Macek flex_spi_wait_for_flash(sc); 61994e25b7aSWojciech Macek 62094e25b7aSWojciech Macek exit: 62194e25b7aSWojciech Macek 62294e25b7aSWojciech Macek return (ret); 62394e25b7aSWojciech Macek } 62494e25b7aSWojciech Macek 62594e25b7aSWojciech Macek static int 62694e25b7aSWojciech Macek flex_spi_default_setup(struct flex_spi_softc *sc) 62794e25b7aSWojciech Macek { 62894e25b7aSWojciech Macek int ret, i; 62994e25b7aSWojciech Macek uint32_t reg; 63094e25b7aSWojciech Macek 63194e25b7aSWojciech Macek /* Default clock speed */ 63294e25b7aSWojciech Macek ret = flex_spi_clk_setup(sc, SPI_DEFAULT_CLK_RATE); 63394e25b7aSWojciech Macek if (ret) 63494e25b7aSWojciech Macek return (ret); 63594e25b7aSWojciech Macek 63694e25b7aSWojciech Macek /* Reset the module */ 63794e25b7aSWojciech Macek /* w1c register, wait unit clear */ 63894e25b7aSWojciech Macek reg = read_reg(sc, FSPI_MCR0); 63994e25b7aSWojciech Macek reg |= FSPI_MCR0_SWRST; 64094e25b7aSWojciech Macek write_reg(sc, FSPI_MCR0, reg); 64194e25b7aSWojciech Macek ret = reg_read_poll_tout(sc, FSPI_MCR0, FSPI_MCR0_SWRST, 1000, POLL_TOUT, 0); 64294e25b7aSWojciech Macek if (ret != 0) { 64394e25b7aSWojciech Macek device_printf(sc->dev, "time out waiting for reset"); 64494e25b7aSWojciech Macek return (ret); 64594e25b7aSWojciech Macek } 64694e25b7aSWojciech Macek 64794e25b7aSWojciech Macek /* Disable the module */ 64894e25b7aSWojciech Macek write_reg(sc, FSPI_MCR0, FSPI_MCR0_MDIS); 64994e25b7aSWojciech Macek 65094e25b7aSWojciech Macek /* Reset the DLL register to default value */ 65194e25b7aSWojciech Macek write_reg(sc, FSPI_DLLACR, FSPI_DLLACR_OVRDEN); 65294e25b7aSWojciech Macek write_reg(sc, FSPI_DLLBCR, FSPI_DLLBCR_OVRDEN); 65394e25b7aSWojciech Macek 65494e25b7aSWojciech Macek /* enable module */ 65594e25b7aSWojciech Macek write_reg(sc, FSPI_MCR0, FSPI_MCR0_AHB_TIMEOUT(0xFF) | 65694e25b7aSWojciech Macek FSPI_MCR0_IP_TIMEOUT(0xFF) | (uint32_t) FSPI_MCR0_OCTCOMB_EN); 65794e25b7aSWojciech Macek 65894e25b7aSWojciech Macek /* 65994e25b7aSWojciech Macek * Disable same device enable bit and configure all slave devices 66094e25b7aSWojciech Macek * independently. 66194e25b7aSWojciech Macek */ 66294e25b7aSWojciech Macek reg = read_reg(sc, FSPI_MCR2); 66394e25b7aSWojciech Macek reg = reg & ~(FSPI_MCR2_SAMEDEVICEEN); 66494e25b7aSWojciech Macek write_reg(sc, FSPI_MCR2, reg); 66594e25b7aSWojciech Macek 66694e25b7aSWojciech Macek /* AHB configuration for access buffer 0~7. */ 66794e25b7aSWojciech Macek for (i = 0; i < 7; i++) 66894e25b7aSWojciech Macek write_reg(sc, FSPI_AHBRX_BUF0CR0 + 4 * i, 0); 66994e25b7aSWojciech Macek 67094e25b7aSWojciech Macek /* 67194e25b7aSWojciech Macek * Set ADATSZ with the maximum AHB buffer size to improve the read 67294e25b7aSWojciech Macek * performance. 67394e25b7aSWojciech Macek */ 67494e25b7aSWojciech Macek write_reg(sc, FSPI_AHBRX_BUF7CR0, (2048 / 8 | 67594e25b7aSWojciech Macek FSPI_AHBRXBUF0CR7_PREF)); 67694e25b7aSWojciech Macek 67794e25b7aSWojciech Macek /* prefetch and no start address alignment limitation */ 67894e25b7aSWojciech Macek write_reg(sc, FSPI_AHBCR, FSPI_AHBCR_PREF_EN | FSPI_AHBCR_RDADDROPT); 67994e25b7aSWojciech Macek 68094e25b7aSWojciech Macek /* AHB Read - Set lut sequence ID for all CS. */ 68194e25b7aSWojciech Macek flex_spi_prepare_ahb_lut(sc); 68294e25b7aSWojciech Macek write_reg(sc, FSPI_FLSHA1CR2, AHB_LUT_ID); 68394e25b7aSWojciech Macek write_reg(sc, FSPI_FLSHA2CR2, AHB_LUT_ID); 68494e25b7aSWojciech Macek write_reg(sc, FSPI_FLSHB1CR2, AHB_LUT_ID); 68594e25b7aSWojciech Macek write_reg(sc, FSPI_FLSHB2CR2, AHB_LUT_ID); 68694e25b7aSWojciech Macek 68794e25b7aSWojciech Macek /* disable interrupts */ 68894e25b7aSWojciech Macek write_reg(sc, FSPI_INTEN, 0); 68994e25b7aSWojciech Macek 69094e25b7aSWojciech Macek return (0); 69194e25b7aSWojciech Macek } 69294e25b7aSWojciech Macek 69394e25b7aSWojciech Macek static int 69494e25b7aSWojciech Macek flex_spi_probe(device_t dev) 69594e25b7aSWojciech Macek { 69694e25b7aSWojciech Macek 69794e25b7aSWojciech Macek if (!ofw_bus_status_okay(dev)) 69894e25b7aSWojciech Macek return (ENXIO); 69994e25b7aSWojciech Macek 70094e25b7aSWojciech Macek if (!ofw_bus_search_compatible(dev, flex_spi_compat_data)->ocd_data) 70194e25b7aSWojciech Macek return (ENXIO); 70294e25b7aSWojciech Macek 70394e25b7aSWojciech Macek device_set_desc(dev, "NXP FlexSPI Flash"); 70494e25b7aSWojciech Macek return (BUS_PROBE_SPECIFIC); 70594e25b7aSWojciech Macek } 70694e25b7aSWojciech Macek 70794e25b7aSWojciech Macek static int 70894e25b7aSWojciech Macek flex_spi_attach(device_t dev) 70994e25b7aSWojciech Macek { 71094e25b7aSWojciech Macek struct flex_spi_softc *sc; 71194e25b7aSWojciech Macek phandle_t node; 71294e25b7aSWojciech Macek int rid; 71394e25b7aSWojciech Macek uint32_t reg; 71494e25b7aSWojciech Macek 71594e25b7aSWojciech Macek node = ofw_bus_get_node(dev); 71694e25b7aSWojciech Macek sc = device_get_softc(dev); 71794e25b7aSWojciech Macek sc->dev = dev; 71894e25b7aSWojciech Macek 71994e25b7aSWojciech Macek mtx_init(&sc->disk_mtx, "flex_spi_DISK", "QSPI disk mtx", MTX_DEF); 72094e25b7aSWojciech Macek 72194e25b7aSWojciech Macek /* Get memory resources. */ 72294e25b7aSWojciech Macek rid = 0; 72394e25b7aSWojciech Macek sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 72494e25b7aSWojciech Macek RF_ACTIVE); 72594e25b7aSWojciech Macek 72694e25b7aSWojciech Macek rid = 1; 72794e25b7aSWojciech Macek sc->ahb_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 72894e25b7aSWojciech Macek RF_ACTIVE | RF_SHAREABLE); 72994e25b7aSWojciech Macek 73094e25b7aSWojciech Macek if (sc->mem_res == NULL || sc->ahb_mem_res == NULL) { 73194e25b7aSWojciech Macek device_printf(dev, "could not allocate resources\n"); 73294e25b7aSWojciech Macek flex_spi_detach(dev); 73394e25b7aSWojciech Macek return (ENOMEM); 73494e25b7aSWojciech Macek } 73594e25b7aSWojciech Macek 73694e25b7aSWojciech Macek /* Get clocks */ 73794e25b7aSWojciech Macek if ((clk_get_by_ofw_name(dev, node, "fspi_en", &sc->fspi_clk_en) != 0) 73894e25b7aSWojciech Macek || (clk_get_freq(sc->fspi_clk_en, &sc->fspi_clk_en_hz) != 0)) { 73994e25b7aSWojciech Macek device_printf(dev, "could not get fspi_en clock\n"); 74094e25b7aSWojciech Macek flex_spi_detach(dev); 74194e25b7aSWojciech Macek return (EINVAL); 74294e25b7aSWojciech Macek } 74394e25b7aSWojciech Macek if ((clk_get_by_ofw_name(dev, node, "fspi", &sc->fspi_clk) != 0) 74494e25b7aSWojciech Macek || (clk_get_freq(sc->fspi_clk, &sc->fspi_clk_hz) != 0)) { 74594e25b7aSWojciech Macek device_printf(dev, "could not get fspi clock\n"); 74694e25b7aSWojciech Macek flex_spi_detach(dev); 74794e25b7aSWojciech Macek return (EINVAL); 74894e25b7aSWojciech Macek } 74994e25b7aSWojciech Macek 75094e25b7aSWojciech Macek /* Enable clocks */ 75194e25b7aSWojciech Macek if (clk_enable(sc->fspi_clk_en) != 0 || 75294e25b7aSWojciech Macek clk_enable(sc->fspi_clk) != 0) { 75394e25b7aSWojciech Macek device_printf(dev, "could not enable clocks\n"); 75494e25b7aSWojciech Macek flex_spi_detach(dev); 75594e25b7aSWojciech Macek return (EINVAL); 75694e25b7aSWojciech Macek } 75794e25b7aSWojciech Macek 75894e25b7aSWojciech Macek /* Clear potential interrupts */ 75994e25b7aSWojciech Macek reg = read_reg(sc, FSPI_INTR); 76094e25b7aSWojciech Macek if (reg) 76194e25b7aSWojciech Macek write_reg(sc, FSPI_INTR, reg); 76294e25b7aSWojciech Macek 76394e25b7aSWojciech Macek /* Default setup */ 76494e25b7aSWojciech Macek if (flex_spi_default_setup(sc) != 0) { 76594e25b7aSWojciech Macek device_printf(sc->dev, "Unable to initialize defaults\n"); 76694e25b7aSWojciech Macek flex_spi_detach(dev); 76794e25b7aSWojciech Macek return (ENXIO); 76894e25b7aSWojciech Macek } 76994e25b7aSWojciech Macek 77094e25b7aSWojciech Macek /* Identify attached Flash */ 77194e25b7aSWojciech Macek if(flex_spi_identify(sc) != 0) { 77294e25b7aSWojciech Macek device_printf(sc->dev, "Unable to identify Flash\n"); 77394e25b7aSWojciech Macek flex_spi_detach(dev); 77494e25b7aSWojciech Macek return (ENXIO); 77594e25b7aSWojciech Macek } 77694e25b7aSWojciech Macek 77794e25b7aSWojciech Macek if (flex_spi_clk_setup(sc, sc->fspi_max_clk) != 0) { 77894e25b7aSWojciech Macek device_printf(sc->dev, "Unable to set up SPI max clock\n"); 77994e25b7aSWojciech Macek flex_spi_detach(dev); 78094e25b7aSWojciech Macek return (ENXIO); 78194e25b7aSWojciech Macek } 78294e25b7aSWojciech Macek 78394e25b7aSWojciech Macek sc->buf = malloc(sc->erasesize, SECTOR_BUFFER, M_WAITOK); 78494e25b7aSWojciech Macek /* Move it to per-flash */ 78594e25b7aSWojciech Macek sc->disk = disk_alloc(); 78694e25b7aSWojciech Macek sc->disk->d_open = flex_spi_open; 78794e25b7aSWojciech Macek sc->disk->d_close = flex_spi_close; 78894e25b7aSWojciech Macek sc->disk->d_strategy = flex_spi_strategy; 78994e25b7aSWojciech Macek sc->disk->d_getattr = flex_spi_getattr; 79094e25b7aSWojciech Macek sc->disk->d_ioctl = flex_spi_ioctl; 79194e25b7aSWojciech Macek sc->disk->d_name = "flash/qspi"; 79294e25b7aSWojciech Macek sc->disk->d_drv1 = sc; 79394e25b7aSWojciech Macek /* the most that can fit in a single spi transaction */ 79494e25b7aSWojciech Macek sc->disk->d_maxsize = DFLTPHYS; 79594e25b7aSWojciech Macek sc->disk->d_sectorsize = FLASH_SECTORSIZE; 79694e25b7aSWojciech Macek sc->disk->d_unit = device_get_unit(sc->dev); 79794e25b7aSWojciech Macek sc->disk->d_dump = NULL; 79894e25b7aSWojciech Macek 79994e25b7aSWojciech Macek sc->disk->d_mediasize = sc->sectorsize * sc->sectorcount; 80094e25b7aSWojciech Macek sc->disk->d_stripesize = sc->erasesize; 80194e25b7aSWojciech Macek 80294e25b7aSWojciech Macek bioq_init(&sc->bio_queue); 80394e25b7aSWojciech Macek sc->taskstate = TSTATE_RUNNING; 80494e25b7aSWojciech Macek kproc_create(&flex_spi_task, sc, &sc->p, 0, 0, "task: qspi flash"); 80594e25b7aSWojciech Macek disk_create(sc->disk, DISK_VERSION); 80694e25b7aSWojciech Macek 80794e25b7aSWojciech Macek return (0); 80894e25b7aSWojciech Macek } 80994e25b7aSWojciech Macek 81094e25b7aSWojciech Macek static int 81194e25b7aSWojciech Macek flex_spi_detach(device_t dev) 81294e25b7aSWojciech Macek { 81394e25b7aSWojciech Macek struct flex_spi_softc *sc; 81494e25b7aSWojciech Macek int err; 81594e25b7aSWojciech Macek 81694e25b7aSWojciech Macek sc = device_get_softc(dev); 81794e25b7aSWojciech Macek err = 0; 81894e25b7aSWojciech Macek 81943c4b47bSKornel Duleba if (!device_is_attached(dev)) 82043c4b47bSKornel Duleba goto free_resources; 82143c4b47bSKornel Duleba 82294e25b7aSWojciech Macek mtx_lock(&sc->disk_mtx); 82394e25b7aSWojciech Macek if (sc->taskstate == TSTATE_RUNNING) { 82494e25b7aSWojciech Macek sc->taskstate = TSTATE_STOPPING; 82594e25b7aSWojciech Macek wakeup(sc->disk); 82694e25b7aSWojciech Macek while (err == 0 && sc->taskstate != TSTATE_STOPPED) { 82794e25b7aSWojciech Macek err = mtx_sleep(sc->disk, &sc->disk_mtx, 0, "flex_spi", 82894e25b7aSWojciech Macek hz * 3); 82994e25b7aSWojciech Macek if (err != 0) { 83094e25b7aSWojciech Macek sc->taskstate = TSTATE_RUNNING; 83194e25b7aSWojciech Macek device_printf(sc->dev, 83294e25b7aSWojciech Macek "Failed to stop queue task\n"); 83394e25b7aSWojciech Macek } 83494e25b7aSWojciech Macek } 83594e25b7aSWojciech Macek } 83694e25b7aSWojciech Macek 83794e25b7aSWojciech Macek mtx_unlock(&sc->disk_mtx); 83894e25b7aSWojciech Macek mtx_destroy(&sc->disk_mtx); 83994e25b7aSWojciech Macek 84094e25b7aSWojciech Macek if (err == 0 && sc->taskstate == TSTATE_STOPPED) { 84194e25b7aSWojciech Macek disk_destroy(sc->disk); 84294e25b7aSWojciech Macek bioq_flush(&sc->bio_queue, NULL, ENXIO); 84394e25b7aSWojciech Macek } 84494e25b7aSWojciech Macek 84594e25b7aSWojciech Macek /* Disable hardware. */ 84643c4b47bSKornel Duleba free_resources: 84794e25b7aSWojciech Macek /* Release memory resource. */ 84894e25b7aSWojciech Macek if (sc->mem_res != NULL) 84994e25b7aSWojciech Macek bus_release_resource(dev, SYS_RES_MEMORY, 85094e25b7aSWojciech Macek rman_get_rid(sc->mem_res), sc->mem_res); 85194e25b7aSWojciech Macek 85294e25b7aSWojciech Macek if (sc->ahb_mem_res != NULL) 85394e25b7aSWojciech Macek bus_release_resource(dev, SYS_RES_MEMORY, 85494e25b7aSWojciech Macek rman_get_rid(sc->ahb_mem_res), sc->ahb_mem_res); 85594e25b7aSWojciech Macek 85694e25b7aSWojciech Macek /* Disable clocks */ 85794e25b7aSWojciech Macek if (sc->fspi_clk_en_hz) 85894e25b7aSWojciech Macek clk_disable(sc->fspi_clk_en); 85994e25b7aSWojciech Macek if (sc->fspi_clk_hz) 86094e25b7aSWojciech Macek clk_disable(sc->fspi_clk); 86194e25b7aSWojciech Macek 86294e25b7aSWojciech Macek free(sc->buf, SECTOR_BUFFER); 86394e25b7aSWojciech Macek 86494e25b7aSWojciech Macek return (err); 86594e25b7aSWojciech Macek } 86694e25b7aSWojciech Macek 86794e25b7aSWojciech Macek static int 86894e25b7aSWojciech Macek flex_spi_open(struct disk *dp) 86994e25b7aSWojciech Macek { 87094e25b7aSWojciech Macek 87194e25b7aSWojciech Macek return (0); 87294e25b7aSWojciech Macek } 87394e25b7aSWojciech Macek 87494e25b7aSWojciech Macek static int 87594e25b7aSWojciech Macek flex_spi_close(struct disk *dp) 87694e25b7aSWojciech Macek { 87794e25b7aSWojciech Macek 87894e25b7aSWojciech Macek return (0); 87994e25b7aSWojciech Macek } 88094e25b7aSWojciech Macek 88194e25b7aSWojciech Macek static int 88294e25b7aSWojciech Macek flex_spi_ioctl(struct disk *dp, u_long cmd, void *data, int fflag, 88394e25b7aSWojciech Macek struct thread *td) 88494e25b7aSWojciech Macek { 88594e25b7aSWojciech Macek 88694e25b7aSWojciech Macek return (ENOTSUP); 88794e25b7aSWojciech Macek } 88894e25b7aSWojciech Macek 88994e25b7aSWojciech Macek static void 89094e25b7aSWojciech Macek flex_spi_strategy(struct bio *bp) 89194e25b7aSWojciech Macek { 89294e25b7aSWojciech Macek struct flex_spi_softc *sc; 89394e25b7aSWojciech Macek 89494e25b7aSWojciech Macek sc = (struct flex_spi_softc *)bp->bio_disk->d_drv1; 89594e25b7aSWojciech Macek mtx_lock(&sc->disk_mtx); 89694e25b7aSWojciech Macek bioq_disksort(&sc->bio_queue, bp); 89794e25b7aSWojciech Macek mtx_unlock(&sc->disk_mtx); 89894e25b7aSWojciech Macek wakeup(sc->disk); 89994e25b7aSWojciech Macek } 90094e25b7aSWojciech Macek 90194e25b7aSWojciech Macek static int 90294e25b7aSWojciech Macek flex_spi_getattr(struct bio *bp) 90394e25b7aSWojciech Macek { 90494e25b7aSWojciech Macek struct flex_spi_softc *sc; 90594e25b7aSWojciech Macek device_t dev; 90694e25b7aSWojciech Macek 90794e25b7aSWojciech Macek if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL) { 90894e25b7aSWojciech Macek return (ENXIO); 90994e25b7aSWojciech Macek } 91094e25b7aSWojciech Macek 91194e25b7aSWojciech Macek sc = bp->bio_disk->d_drv1; 91294e25b7aSWojciech Macek dev = sc->dev; 91394e25b7aSWojciech Macek 91494e25b7aSWojciech Macek if (strcmp(bp->bio_attribute, "SPI::device") != 0) { 91594e25b7aSWojciech Macek return (-1); 91694e25b7aSWojciech Macek } 91794e25b7aSWojciech Macek 91894e25b7aSWojciech Macek if (bp->bio_length != sizeof(dev)) { 91994e25b7aSWojciech Macek return (EFAULT); 92094e25b7aSWojciech Macek } 92194e25b7aSWojciech Macek 92294e25b7aSWojciech Macek bcopy(&dev, bp->bio_data, sizeof(dev)); 92394e25b7aSWojciech Macek 92494e25b7aSWojciech Macek return (0); 92594e25b7aSWojciech Macek } 92694e25b7aSWojciech Macek 92794e25b7aSWojciech Macek static void 92894e25b7aSWojciech Macek flex_spi_task(void *arg) 92994e25b7aSWojciech Macek { 93094e25b7aSWojciech Macek struct flex_spi_softc *sc; 93194e25b7aSWojciech Macek struct bio *bp; 93294e25b7aSWojciech Macek 93394e25b7aSWojciech Macek sc = (struct flex_spi_softc *)arg; 93494e25b7aSWojciech Macek for (;;) { 93594e25b7aSWojciech Macek mtx_lock(&sc->disk_mtx); 93694e25b7aSWojciech Macek do { 93794e25b7aSWojciech Macek if (sc->taskstate == TSTATE_STOPPING) { 93894e25b7aSWojciech Macek sc->taskstate = TSTATE_STOPPED; 93994e25b7aSWojciech Macek mtx_unlock(&sc->disk_mtx); 94094e25b7aSWojciech Macek wakeup(sc->disk); 94194e25b7aSWojciech Macek kproc_exit(0); 94294e25b7aSWojciech Macek } 94394e25b7aSWojciech Macek bp = bioq_first(&sc->bio_queue); 94494e25b7aSWojciech Macek if (bp == NULL) 94594e25b7aSWojciech Macek mtx_sleep(sc->disk, &sc->disk_mtx, PRIBIO, 94694e25b7aSWojciech Macek "flex_spi", 0); 94794e25b7aSWojciech Macek } while (bp == NULL); 94894e25b7aSWojciech Macek bioq_remove(&sc->bio_queue, bp); 94994e25b7aSWojciech Macek mtx_unlock(&sc->disk_mtx); 95094e25b7aSWojciech Macek 95194e25b7aSWojciech Macek switch (bp->bio_cmd) { 95294e25b7aSWojciech Macek case BIO_READ: 95394e25b7aSWojciech Macek bp->bio_error = flex_spi_read(sc, bp->bio_offset, 95494e25b7aSWojciech Macek bp->bio_data, bp->bio_bcount); 95594e25b7aSWojciech Macek break; 95694e25b7aSWojciech Macek case BIO_WRITE: 95794e25b7aSWojciech Macek bp->bio_error = flex_spi_write(sc, bp->bio_offset, 95894e25b7aSWojciech Macek bp->bio_data, bp->bio_bcount); 95994e25b7aSWojciech Macek break; 96094e25b7aSWojciech Macek default: 96194e25b7aSWojciech Macek bp->bio_error = EINVAL; 96294e25b7aSWojciech Macek } 96394e25b7aSWojciech Macek biodone(bp); 96494e25b7aSWojciech Macek } 96594e25b7aSWojciech Macek } 96694e25b7aSWojciech Macek 96794e25b7aSWojciech Macek static device_method_t flex_spi_methods[] = { 96894e25b7aSWojciech Macek /* Device interface */ 96994e25b7aSWojciech Macek DEVMETHOD(device_probe, flex_spi_probe), 97094e25b7aSWojciech Macek DEVMETHOD(device_attach, flex_spi_attach), 97194e25b7aSWojciech Macek DEVMETHOD(device_detach, flex_spi_detach), 97294e25b7aSWojciech Macek 97394e25b7aSWojciech Macek { 0, 0 } 97494e25b7aSWojciech Macek }; 97594e25b7aSWojciech Macek 97694e25b7aSWojciech Macek static driver_t flex_spi_driver = { 97794e25b7aSWojciech Macek "flex_spi", 97894e25b7aSWojciech Macek flex_spi_methods, 97994e25b7aSWojciech Macek sizeof(struct flex_spi_softc), 98094e25b7aSWojciech Macek }; 98194e25b7aSWojciech Macek 9820d95fe04SJohn Baldwin DRIVER_MODULE(flex_spi, simplebus, flex_spi_driver, 0, 0); 98394e25b7aSWojciech Macek SIMPLEBUS_PNP_INFO(flex_spi_compat_data); 984