xref: /freebsd-src/sys/dev/flash/flexspi/flex_spi.c (revision d1a89bd9b6eb1524902b619fa092c7d6de63e623)
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, &reg, 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(&reg, 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