162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * SPI NOR driver for NXP SPI Flash Interface (SPIFI)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Based on Freescale QuadSPI driver:
862306a36Sopenharmony_ci * Copyright (C) 2013 Freescale Semiconductor, Inc.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/clk.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/iopoll.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
1762306a36Sopenharmony_ci#include <linux/mtd/partitions.h>
1862306a36Sopenharmony_ci#include <linux/mtd/spi-nor.h>
1962306a36Sopenharmony_ci#include <linux/of.h>
2062306a36Sopenharmony_ci#include <linux/platform_device.h>
2162306a36Sopenharmony_ci#include <linux/spi/spi.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* NXP SPIFI registers, bits and macros */
2462306a36Sopenharmony_ci#define SPIFI_CTRL				0x000
2562306a36Sopenharmony_ci#define  SPIFI_CTRL_TIMEOUT(timeout)		(timeout)
2662306a36Sopenharmony_ci#define  SPIFI_CTRL_CSHIGH(cshigh)		((cshigh) << 16)
2762306a36Sopenharmony_ci#define  SPIFI_CTRL_MODE3			BIT(23)
2862306a36Sopenharmony_ci#define  SPIFI_CTRL_DUAL			BIT(28)
2962306a36Sopenharmony_ci#define  SPIFI_CTRL_FBCLK			BIT(30)
3062306a36Sopenharmony_ci#define SPIFI_CMD				0x004
3162306a36Sopenharmony_ci#define  SPIFI_CMD_DATALEN(dlen)		((dlen) & 0x3fff)
3262306a36Sopenharmony_ci#define  SPIFI_CMD_DOUT				BIT(15)
3362306a36Sopenharmony_ci#define  SPIFI_CMD_INTLEN(ilen)			((ilen) << 16)
3462306a36Sopenharmony_ci#define  SPIFI_CMD_FIELDFORM(field)		((field) << 19)
3562306a36Sopenharmony_ci#define  SPIFI_CMD_FIELDFORM_ALL_SERIAL		SPIFI_CMD_FIELDFORM(0x0)
3662306a36Sopenharmony_ci#define  SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA	SPIFI_CMD_FIELDFORM(0x1)
3762306a36Sopenharmony_ci#define  SPIFI_CMD_FRAMEFORM(frame)		((frame) << 21)
3862306a36Sopenharmony_ci#define  SPIFI_CMD_FRAMEFORM_OPCODE_ONLY	SPIFI_CMD_FRAMEFORM(0x1)
3962306a36Sopenharmony_ci#define  SPIFI_CMD_OPCODE(op)			((op) << 24)
4062306a36Sopenharmony_ci#define SPIFI_ADDR				0x008
4162306a36Sopenharmony_ci#define SPIFI_IDATA				0x00c
4262306a36Sopenharmony_ci#define SPIFI_CLIMIT				0x010
4362306a36Sopenharmony_ci#define SPIFI_DATA				0x014
4462306a36Sopenharmony_ci#define SPIFI_MCMD				0x018
4562306a36Sopenharmony_ci#define SPIFI_STAT				0x01c
4662306a36Sopenharmony_ci#define  SPIFI_STAT_MCINIT			BIT(0)
4762306a36Sopenharmony_ci#define  SPIFI_STAT_CMD				BIT(1)
4862306a36Sopenharmony_ci#define  SPIFI_STAT_RESET			BIT(4)
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define SPI_NOR_MAX_ID_LEN	6
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistruct nxp_spifi {
5362306a36Sopenharmony_ci	struct device *dev;
5462306a36Sopenharmony_ci	struct clk *clk_spifi;
5562306a36Sopenharmony_ci	struct clk *clk_reg;
5662306a36Sopenharmony_ci	void __iomem *io_base;
5762306a36Sopenharmony_ci	void __iomem *flash_base;
5862306a36Sopenharmony_ci	struct spi_nor nor;
5962306a36Sopenharmony_ci	bool memory_mode;
6062306a36Sopenharmony_ci	u32 mcmd;
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic int nxp_spifi_wait_for_cmd(struct nxp_spifi *spifi)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	u8 stat;
6662306a36Sopenharmony_ci	int ret;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	ret = readb_poll_timeout(spifi->io_base + SPIFI_STAT, stat,
6962306a36Sopenharmony_ci				 !(stat & SPIFI_STAT_CMD), 10, 30);
7062306a36Sopenharmony_ci	if (ret)
7162306a36Sopenharmony_ci		dev_warn(spifi->dev, "command timed out\n");
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return ret;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int nxp_spifi_reset(struct nxp_spifi *spifi)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	u8 stat;
7962306a36Sopenharmony_ci	int ret;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	writel(SPIFI_STAT_RESET, spifi->io_base + SPIFI_STAT);
8262306a36Sopenharmony_ci	ret = readb_poll_timeout(spifi->io_base + SPIFI_STAT, stat,
8362306a36Sopenharmony_ci				 !(stat & SPIFI_STAT_RESET), 10, 30);
8462306a36Sopenharmony_ci	if (ret)
8562306a36Sopenharmony_ci		dev_warn(spifi->dev, "state reset timed out\n");
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return ret;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic int nxp_spifi_set_memory_mode_off(struct nxp_spifi *spifi)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	int ret;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (!spifi->memory_mode)
9562306a36Sopenharmony_ci		return 0;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	ret = nxp_spifi_reset(spifi);
9862306a36Sopenharmony_ci	if (ret)
9962306a36Sopenharmony_ci		dev_err(spifi->dev, "unable to enter command mode\n");
10062306a36Sopenharmony_ci	else
10162306a36Sopenharmony_ci		spifi->memory_mode = false;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return ret;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int nxp_spifi_set_memory_mode_on(struct nxp_spifi *spifi)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	u8 stat;
10962306a36Sopenharmony_ci	int ret;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (spifi->memory_mode)
11262306a36Sopenharmony_ci		return 0;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	writel(spifi->mcmd, spifi->io_base + SPIFI_MCMD);
11562306a36Sopenharmony_ci	ret = readb_poll_timeout(spifi->io_base + SPIFI_STAT, stat,
11662306a36Sopenharmony_ci				 stat & SPIFI_STAT_MCINIT, 10, 30);
11762306a36Sopenharmony_ci	if (ret)
11862306a36Sopenharmony_ci		dev_err(spifi->dev, "unable to enter memory mode\n");
11962306a36Sopenharmony_ci	else
12062306a36Sopenharmony_ci		spifi->memory_mode = true;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return ret;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic int nxp_spifi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
12662306a36Sopenharmony_ci			      size_t len)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct nxp_spifi *spifi = nor->priv;
12962306a36Sopenharmony_ci	u32 cmd;
13062306a36Sopenharmony_ci	int ret;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	ret = nxp_spifi_set_memory_mode_off(spifi);
13362306a36Sopenharmony_ci	if (ret)
13462306a36Sopenharmony_ci		return ret;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	cmd = SPIFI_CMD_DATALEN(len) |
13762306a36Sopenharmony_ci	      SPIFI_CMD_OPCODE(opcode) |
13862306a36Sopenharmony_ci	      SPIFI_CMD_FIELDFORM_ALL_SERIAL |
13962306a36Sopenharmony_ci	      SPIFI_CMD_FRAMEFORM_OPCODE_ONLY;
14062306a36Sopenharmony_ci	writel(cmd, spifi->io_base + SPIFI_CMD);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	while (len--)
14362306a36Sopenharmony_ci		*buf++ = readb(spifi->io_base + SPIFI_DATA);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return nxp_spifi_wait_for_cmd(spifi);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic int nxp_spifi_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf,
14962306a36Sopenharmony_ci			       size_t len)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct nxp_spifi *spifi = nor->priv;
15262306a36Sopenharmony_ci	u32 cmd;
15362306a36Sopenharmony_ci	int ret;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	ret = nxp_spifi_set_memory_mode_off(spifi);
15662306a36Sopenharmony_ci	if (ret)
15762306a36Sopenharmony_ci		return ret;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	cmd = SPIFI_CMD_DOUT |
16062306a36Sopenharmony_ci	      SPIFI_CMD_DATALEN(len) |
16162306a36Sopenharmony_ci	      SPIFI_CMD_OPCODE(opcode) |
16262306a36Sopenharmony_ci	      SPIFI_CMD_FIELDFORM_ALL_SERIAL |
16362306a36Sopenharmony_ci	      SPIFI_CMD_FRAMEFORM_OPCODE_ONLY;
16462306a36Sopenharmony_ci	writel(cmd, spifi->io_base + SPIFI_CMD);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	while (len--)
16762306a36Sopenharmony_ci		writeb(*buf++, spifi->io_base + SPIFI_DATA);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return nxp_spifi_wait_for_cmd(spifi);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic ssize_t nxp_spifi_read(struct spi_nor *nor, loff_t from, size_t len,
17362306a36Sopenharmony_ci			      u_char *buf)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct nxp_spifi *spifi = nor->priv;
17662306a36Sopenharmony_ci	int ret;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	ret = nxp_spifi_set_memory_mode_on(spifi);
17962306a36Sopenharmony_ci	if (ret)
18062306a36Sopenharmony_ci		return ret;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	memcpy_fromio(buf, spifi->flash_base + from, len);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return len;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic ssize_t nxp_spifi_write(struct spi_nor *nor, loff_t to, size_t len,
18862306a36Sopenharmony_ci			       const u_char *buf)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct nxp_spifi *spifi = nor->priv;
19162306a36Sopenharmony_ci	u32 cmd;
19262306a36Sopenharmony_ci	int ret;
19362306a36Sopenharmony_ci	size_t i;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	ret = nxp_spifi_set_memory_mode_off(spifi);
19662306a36Sopenharmony_ci	if (ret)
19762306a36Sopenharmony_ci		return ret;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	writel(to, spifi->io_base + SPIFI_ADDR);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	cmd = SPIFI_CMD_DOUT |
20262306a36Sopenharmony_ci	      SPIFI_CMD_DATALEN(len) |
20362306a36Sopenharmony_ci	      SPIFI_CMD_FIELDFORM_ALL_SERIAL |
20462306a36Sopenharmony_ci	      SPIFI_CMD_OPCODE(nor->program_opcode) |
20562306a36Sopenharmony_ci	      SPIFI_CMD_FRAMEFORM(spifi->nor.addr_nbytes + 1);
20662306a36Sopenharmony_ci	writel(cmd, spifi->io_base + SPIFI_CMD);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	for (i = 0; i < len; i++)
20962306a36Sopenharmony_ci		writeb(buf[i], spifi->io_base + SPIFI_DATA);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ret = nxp_spifi_wait_for_cmd(spifi);
21262306a36Sopenharmony_ci	if (ret)
21362306a36Sopenharmony_ci		return ret;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return len;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct nxp_spifi *spifi = nor->priv;
22162306a36Sopenharmony_ci	u32 cmd;
22262306a36Sopenharmony_ci	int ret;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	ret = nxp_spifi_set_memory_mode_off(spifi);
22562306a36Sopenharmony_ci	if (ret)
22662306a36Sopenharmony_ci		return ret;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	writel(offs, spifi->io_base + SPIFI_ADDR);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	cmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL |
23162306a36Sopenharmony_ci	      SPIFI_CMD_OPCODE(nor->erase_opcode) |
23262306a36Sopenharmony_ci	      SPIFI_CMD_FRAMEFORM(spifi->nor.addr_nbytes + 1);
23362306a36Sopenharmony_ci	writel(cmd, spifi->io_base + SPIFI_CMD);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return nxp_spifi_wait_for_cmd(spifi);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	switch (spifi->nor.read_proto) {
24162306a36Sopenharmony_ci	case SNOR_PROTO_1_1_1:
24262306a36Sopenharmony_ci		spifi->mcmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL;
24362306a36Sopenharmony_ci		break;
24462306a36Sopenharmony_ci	case SNOR_PROTO_1_1_2:
24562306a36Sopenharmony_ci	case SNOR_PROTO_1_1_4:
24662306a36Sopenharmony_ci		spifi->mcmd = SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA;
24762306a36Sopenharmony_ci		break;
24862306a36Sopenharmony_ci	default:
24962306a36Sopenharmony_ci		dev_err(spifi->dev, "unsupported SPI read mode\n");
25062306a36Sopenharmony_ci		return -EINVAL;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* Memory mode supports address length between 1 and 4 */
25462306a36Sopenharmony_ci	if (spifi->nor.addr_nbytes < 1 || spifi->nor.addr_nbytes > 4)
25562306a36Sopenharmony_ci		return -EINVAL;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	spifi->mcmd |= SPIFI_CMD_OPCODE(spifi->nor.read_opcode) |
25862306a36Sopenharmony_ci		       SPIFI_CMD_INTLEN(spifi->nor.read_dummy / 8) |
25962306a36Sopenharmony_ci		       SPIFI_CMD_FRAMEFORM(spifi->nor.addr_nbytes + 1);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return 0;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic void nxp_spifi_dummy_id_read(struct spi_nor *nor)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	u8 id[SPI_NOR_MAX_ID_LEN];
26762306a36Sopenharmony_ci	nor->controller_ops->read_reg(nor, SPINOR_OP_RDID, id,
26862306a36Sopenharmony_ci				      SPI_NOR_MAX_ID_LEN);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic const struct spi_nor_controller_ops nxp_spifi_controller_ops = {
27262306a36Sopenharmony_ci	.read_reg  = nxp_spifi_read_reg,
27362306a36Sopenharmony_ci	.write_reg = nxp_spifi_write_reg,
27462306a36Sopenharmony_ci	.read  = nxp_spifi_read,
27562306a36Sopenharmony_ci	.write = nxp_spifi_write,
27662306a36Sopenharmony_ci	.erase = nxp_spifi_erase,
27762306a36Sopenharmony_ci};
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
28062306a36Sopenharmony_ci				 struct device_node *np)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct spi_nor_hwcaps hwcaps = {
28362306a36Sopenharmony_ci		.mask = SNOR_HWCAPS_READ |
28462306a36Sopenharmony_ci			SNOR_HWCAPS_READ_FAST |
28562306a36Sopenharmony_ci			SNOR_HWCAPS_PP,
28662306a36Sopenharmony_ci	};
28762306a36Sopenharmony_ci	u32 ctrl, property;
28862306a36Sopenharmony_ci	u16 mode = 0;
28962306a36Sopenharmony_ci	int ret;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (!of_property_read_u32(np, "spi-rx-bus-width", &property)) {
29262306a36Sopenharmony_ci		switch (property) {
29362306a36Sopenharmony_ci		case 1:
29462306a36Sopenharmony_ci			break;
29562306a36Sopenharmony_ci		case 2:
29662306a36Sopenharmony_ci			mode |= SPI_RX_DUAL;
29762306a36Sopenharmony_ci			break;
29862306a36Sopenharmony_ci		case 4:
29962306a36Sopenharmony_ci			mode |= SPI_RX_QUAD;
30062306a36Sopenharmony_ci			break;
30162306a36Sopenharmony_ci		default:
30262306a36Sopenharmony_ci			dev_err(spifi->dev, "unsupported rx-bus-width\n");
30362306a36Sopenharmony_ci			return -EINVAL;
30462306a36Sopenharmony_ci		}
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (of_property_read_bool(np, "spi-cpha"))
30862306a36Sopenharmony_ci		mode |= SPI_CPHA;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (of_property_read_bool(np, "spi-cpol"))
31162306a36Sopenharmony_ci		mode |= SPI_CPOL;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/* Setup control register defaults */
31462306a36Sopenharmony_ci	ctrl = SPIFI_CTRL_TIMEOUT(1000) |
31562306a36Sopenharmony_ci	       SPIFI_CTRL_CSHIGH(15) |
31662306a36Sopenharmony_ci	       SPIFI_CTRL_FBCLK;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (mode & SPI_RX_DUAL) {
31962306a36Sopenharmony_ci		ctrl |= SPIFI_CTRL_DUAL;
32062306a36Sopenharmony_ci		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
32162306a36Sopenharmony_ci	} else if (mode & SPI_RX_QUAD) {
32262306a36Sopenharmony_ci		ctrl &= ~SPIFI_CTRL_DUAL;
32362306a36Sopenharmony_ci		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
32462306a36Sopenharmony_ci	} else {
32562306a36Sopenharmony_ci		ctrl |= SPIFI_CTRL_DUAL;
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	switch (mode & SPI_MODE_X_MASK) {
32962306a36Sopenharmony_ci	case SPI_MODE_0:
33062306a36Sopenharmony_ci		ctrl &= ~SPIFI_CTRL_MODE3;
33162306a36Sopenharmony_ci		break;
33262306a36Sopenharmony_ci	case SPI_MODE_3:
33362306a36Sopenharmony_ci		ctrl |= SPIFI_CTRL_MODE3;
33462306a36Sopenharmony_ci		break;
33562306a36Sopenharmony_ci	default:
33662306a36Sopenharmony_ci		dev_err(spifi->dev, "only mode 0 and 3 supported\n");
33762306a36Sopenharmony_ci		return -EINVAL;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	writel(ctrl, spifi->io_base + SPIFI_CTRL);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	spifi->nor.dev   = spifi->dev;
34362306a36Sopenharmony_ci	spi_nor_set_flash_node(&spifi->nor, np);
34462306a36Sopenharmony_ci	spifi->nor.priv  = spifi;
34562306a36Sopenharmony_ci	spifi->nor.controller_ops = &nxp_spifi_controller_ops;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	/*
34862306a36Sopenharmony_ci	 * The first read on a hard reset isn't reliable so do a
34962306a36Sopenharmony_ci	 * dummy read of the id before calling spi_nor_scan().
35062306a36Sopenharmony_ci	 * The reason for this problem is unknown.
35162306a36Sopenharmony_ci	 *
35262306a36Sopenharmony_ci	 * The official NXP spifilib uses more or less the same
35362306a36Sopenharmony_ci	 * workaround that is applied here by reading the device
35462306a36Sopenharmony_ci	 * id multiple times.
35562306a36Sopenharmony_ci	 */
35662306a36Sopenharmony_ci	nxp_spifi_dummy_id_read(&spifi->nor);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	ret = spi_nor_scan(&spifi->nor, NULL, &hwcaps);
35962306a36Sopenharmony_ci	if (ret) {
36062306a36Sopenharmony_ci		dev_err(spifi->dev, "device scan failed\n");
36162306a36Sopenharmony_ci		return ret;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	ret = nxp_spifi_setup_memory_cmd(spifi);
36562306a36Sopenharmony_ci	if (ret) {
36662306a36Sopenharmony_ci		dev_err(spifi->dev, "memory command setup failed\n");
36762306a36Sopenharmony_ci		return ret;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	ret = mtd_device_register(&spifi->nor.mtd, NULL, 0);
37162306a36Sopenharmony_ci	if (ret) {
37262306a36Sopenharmony_ci		dev_err(spifi->dev, "mtd device parse failed\n");
37362306a36Sopenharmony_ci		return ret;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	return 0;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int nxp_spifi_probe(struct platform_device *pdev)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct device_node *flash_np;
38262306a36Sopenharmony_ci	struct nxp_spifi *spifi;
38362306a36Sopenharmony_ci	int ret;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	spifi = devm_kzalloc(&pdev->dev, sizeof(*spifi), GFP_KERNEL);
38662306a36Sopenharmony_ci	if (!spifi)
38762306a36Sopenharmony_ci		return -ENOMEM;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	spifi->io_base = devm_platform_ioremap_resource_byname(pdev, "spifi");
39062306a36Sopenharmony_ci	if (IS_ERR(spifi->io_base))
39162306a36Sopenharmony_ci		return PTR_ERR(spifi->io_base);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	spifi->flash_base = devm_platform_ioremap_resource_byname(pdev, "flash");
39462306a36Sopenharmony_ci	if (IS_ERR(spifi->flash_base))
39562306a36Sopenharmony_ci		return PTR_ERR(spifi->flash_base);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	spifi->clk_spifi = devm_clk_get_enabled(&pdev->dev, "spifi");
39862306a36Sopenharmony_ci	if (IS_ERR(spifi->clk_spifi)) {
39962306a36Sopenharmony_ci		dev_err(&pdev->dev, "spifi clock not found or unable to enable\n");
40062306a36Sopenharmony_ci		return PTR_ERR(spifi->clk_spifi);
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	spifi->clk_reg = devm_clk_get_enabled(&pdev->dev, "reg");
40462306a36Sopenharmony_ci	if (IS_ERR(spifi->clk_reg)) {
40562306a36Sopenharmony_ci		dev_err(&pdev->dev, "reg clock not found or unable to enable\n");
40662306a36Sopenharmony_ci		return PTR_ERR(spifi->clk_reg);
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	spifi->dev = &pdev->dev;
41062306a36Sopenharmony_ci	platform_set_drvdata(pdev, spifi);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/* Initialize and reset device */
41362306a36Sopenharmony_ci	nxp_spifi_reset(spifi);
41462306a36Sopenharmony_ci	writel(0, spifi->io_base + SPIFI_IDATA);
41562306a36Sopenharmony_ci	writel(0, spifi->io_base + SPIFI_MCMD);
41662306a36Sopenharmony_ci	nxp_spifi_reset(spifi);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	flash_np = of_get_next_available_child(pdev->dev.of_node, NULL);
41962306a36Sopenharmony_ci	if (!flash_np) {
42062306a36Sopenharmony_ci		dev_err(&pdev->dev, "no SPI flash device to configure\n");
42162306a36Sopenharmony_ci		return -ENODEV;
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	ret = nxp_spifi_setup_flash(spifi, flash_np);
42562306a36Sopenharmony_ci	of_node_put(flash_np);
42662306a36Sopenharmony_ci	if (ret) {
42762306a36Sopenharmony_ci		dev_err(&pdev->dev, "unable to setup flash chip\n");
42862306a36Sopenharmony_ci		return ret;
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return 0;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic int nxp_spifi_remove(struct platform_device *pdev)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	struct nxp_spifi *spifi = platform_get_drvdata(pdev);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	mtd_device_unregister(&spifi->nor.mtd);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	return 0;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic const struct of_device_id nxp_spifi_match[] = {
44462306a36Sopenharmony_ci	{.compatible = "nxp,lpc1773-spifi"},
44562306a36Sopenharmony_ci	{ /* sentinel */ }
44662306a36Sopenharmony_ci};
44762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, nxp_spifi_match);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic struct platform_driver nxp_spifi_driver = {
45062306a36Sopenharmony_ci	.probe	= nxp_spifi_probe,
45162306a36Sopenharmony_ci	.remove	= nxp_spifi_remove,
45262306a36Sopenharmony_ci	.driver	= {
45362306a36Sopenharmony_ci		.name = "nxp-spifi",
45462306a36Sopenharmony_ci		.of_match_table = nxp_spifi_match,
45562306a36Sopenharmony_ci	},
45662306a36Sopenharmony_ci};
45762306a36Sopenharmony_cimodule_platform_driver(nxp_spifi_driver);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ciMODULE_DESCRIPTION("NXP SPI Flash Interface driver");
46062306a36Sopenharmony_ciMODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
46162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
462