18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SPI NOR driver for NXP SPI Flash Interface (SPIFI)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Based on Freescale QuadSPI driver:
88c2ecf20Sopenharmony_ci * Copyright (C) 2013 Freescale Semiconductor, Inc.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/clk.h>
128c2ecf20Sopenharmony_ci#include <linux/err.h>
138c2ecf20Sopenharmony_ci#include <linux/io.h>
148c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
178c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h>
188c2ecf20Sopenharmony_ci#include <linux/mtd/spi-nor.h>
198c2ecf20Sopenharmony_ci#include <linux/of.h>
208c2ecf20Sopenharmony_ci#include <linux/of_device.h>
218c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
228c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* NXP SPIFI registers, bits and macros */
258c2ecf20Sopenharmony_ci#define SPIFI_CTRL				0x000
268c2ecf20Sopenharmony_ci#define  SPIFI_CTRL_TIMEOUT(timeout)		(timeout)
278c2ecf20Sopenharmony_ci#define  SPIFI_CTRL_CSHIGH(cshigh)		((cshigh) << 16)
288c2ecf20Sopenharmony_ci#define  SPIFI_CTRL_MODE3			BIT(23)
298c2ecf20Sopenharmony_ci#define  SPIFI_CTRL_DUAL			BIT(28)
308c2ecf20Sopenharmony_ci#define  SPIFI_CTRL_FBCLK			BIT(30)
318c2ecf20Sopenharmony_ci#define SPIFI_CMD				0x004
328c2ecf20Sopenharmony_ci#define  SPIFI_CMD_DATALEN(dlen)		((dlen) & 0x3fff)
338c2ecf20Sopenharmony_ci#define  SPIFI_CMD_DOUT				BIT(15)
348c2ecf20Sopenharmony_ci#define  SPIFI_CMD_INTLEN(ilen)			((ilen) << 16)
358c2ecf20Sopenharmony_ci#define  SPIFI_CMD_FIELDFORM(field)		((field) << 19)
368c2ecf20Sopenharmony_ci#define  SPIFI_CMD_FIELDFORM_ALL_SERIAL		SPIFI_CMD_FIELDFORM(0x0)
378c2ecf20Sopenharmony_ci#define  SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA	SPIFI_CMD_FIELDFORM(0x1)
388c2ecf20Sopenharmony_ci#define  SPIFI_CMD_FRAMEFORM(frame)		((frame) << 21)
398c2ecf20Sopenharmony_ci#define  SPIFI_CMD_FRAMEFORM_OPCODE_ONLY	SPIFI_CMD_FRAMEFORM(0x1)
408c2ecf20Sopenharmony_ci#define  SPIFI_CMD_OPCODE(op)			((op) << 24)
418c2ecf20Sopenharmony_ci#define SPIFI_ADDR				0x008
428c2ecf20Sopenharmony_ci#define SPIFI_IDATA				0x00c
438c2ecf20Sopenharmony_ci#define SPIFI_CLIMIT				0x010
448c2ecf20Sopenharmony_ci#define SPIFI_DATA				0x014
458c2ecf20Sopenharmony_ci#define SPIFI_MCMD				0x018
468c2ecf20Sopenharmony_ci#define SPIFI_STAT				0x01c
478c2ecf20Sopenharmony_ci#define  SPIFI_STAT_MCINIT			BIT(0)
488c2ecf20Sopenharmony_ci#define  SPIFI_STAT_CMD				BIT(1)
498c2ecf20Sopenharmony_ci#define  SPIFI_STAT_RESET			BIT(4)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define SPI_NOR_MAX_ID_LEN	6
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistruct nxp_spifi {
548c2ecf20Sopenharmony_ci	struct device *dev;
558c2ecf20Sopenharmony_ci	struct clk *clk_spifi;
568c2ecf20Sopenharmony_ci	struct clk *clk_reg;
578c2ecf20Sopenharmony_ci	void __iomem *io_base;
588c2ecf20Sopenharmony_ci	void __iomem *flash_base;
598c2ecf20Sopenharmony_ci	struct spi_nor nor;
608c2ecf20Sopenharmony_ci	bool memory_mode;
618c2ecf20Sopenharmony_ci	u32 mcmd;
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic int nxp_spifi_wait_for_cmd(struct nxp_spifi *spifi)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	u8 stat;
678c2ecf20Sopenharmony_ci	int ret;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	ret = readb_poll_timeout(spifi->io_base + SPIFI_STAT, stat,
708c2ecf20Sopenharmony_ci				 !(stat & SPIFI_STAT_CMD), 10, 30);
718c2ecf20Sopenharmony_ci	if (ret)
728c2ecf20Sopenharmony_ci		dev_warn(spifi->dev, "command timed out\n");
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return ret;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int nxp_spifi_reset(struct nxp_spifi *spifi)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	u8 stat;
808c2ecf20Sopenharmony_ci	int ret;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	writel(SPIFI_STAT_RESET, spifi->io_base + SPIFI_STAT);
838c2ecf20Sopenharmony_ci	ret = readb_poll_timeout(spifi->io_base + SPIFI_STAT, stat,
848c2ecf20Sopenharmony_ci				 !(stat & SPIFI_STAT_RESET), 10, 30);
858c2ecf20Sopenharmony_ci	if (ret)
868c2ecf20Sopenharmony_ci		dev_warn(spifi->dev, "state reset timed out\n");
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	return ret;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int nxp_spifi_set_memory_mode_off(struct nxp_spifi *spifi)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	int ret;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (!spifi->memory_mode)
968c2ecf20Sopenharmony_ci		return 0;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	ret = nxp_spifi_reset(spifi);
998c2ecf20Sopenharmony_ci	if (ret)
1008c2ecf20Sopenharmony_ci		dev_err(spifi->dev, "unable to enter command mode\n");
1018c2ecf20Sopenharmony_ci	else
1028c2ecf20Sopenharmony_ci		spifi->memory_mode = false;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return ret;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int nxp_spifi_set_memory_mode_on(struct nxp_spifi *spifi)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	u8 stat;
1108c2ecf20Sopenharmony_ci	int ret;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (spifi->memory_mode)
1138c2ecf20Sopenharmony_ci		return 0;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	writel(spifi->mcmd, spifi->io_base + SPIFI_MCMD);
1168c2ecf20Sopenharmony_ci	ret = readb_poll_timeout(spifi->io_base + SPIFI_STAT, stat,
1178c2ecf20Sopenharmony_ci				 stat & SPIFI_STAT_MCINIT, 10, 30);
1188c2ecf20Sopenharmony_ci	if (ret)
1198c2ecf20Sopenharmony_ci		dev_err(spifi->dev, "unable to enter memory mode\n");
1208c2ecf20Sopenharmony_ci	else
1218c2ecf20Sopenharmony_ci		spifi->memory_mode = true;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return ret;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int nxp_spifi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
1278c2ecf20Sopenharmony_ci			      size_t len)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	struct nxp_spifi *spifi = nor->priv;
1308c2ecf20Sopenharmony_ci	u32 cmd;
1318c2ecf20Sopenharmony_ci	int ret;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	ret = nxp_spifi_set_memory_mode_off(spifi);
1348c2ecf20Sopenharmony_ci	if (ret)
1358c2ecf20Sopenharmony_ci		return ret;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	cmd = SPIFI_CMD_DATALEN(len) |
1388c2ecf20Sopenharmony_ci	      SPIFI_CMD_OPCODE(opcode) |
1398c2ecf20Sopenharmony_ci	      SPIFI_CMD_FIELDFORM_ALL_SERIAL |
1408c2ecf20Sopenharmony_ci	      SPIFI_CMD_FRAMEFORM_OPCODE_ONLY;
1418c2ecf20Sopenharmony_ci	writel(cmd, spifi->io_base + SPIFI_CMD);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	while (len--)
1448c2ecf20Sopenharmony_ci		*buf++ = readb(spifi->io_base + SPIFI_DATA);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	return nxp_spifi_wait_for_cmd(spifi);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic int nxp_spifi_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf,
1508c2ecf20Sopenharmony_ci			       size_t len)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	struct nxp_spifi *spifi = nor->priv;
1538c2ecf20Sopenharmony_ci	u32 cmd;
1548c2ecf20Sopenharmony_ci	int ret;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ret = nxp_spifi_set_memory_mode_off(spifi);
1578c2ecf20Sopenharmony_ci	if (ret)
1588c2ecf20Sopenharmony_ci		return ret;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	cmd = SPIFI_CMD_DOUT |
1618c2ecf20Sopenharmony_ci	      SPIFI_CMD_DATALEN(len) |
1628c2ecf20Sopenharmony_ci	      SPIFI_CMD_OPCODE(opcode) |
1638c2ecf20Sopenharmony_ci	      SPIFI_CMD_FIELDFORM_ALL_SERIAL |
1648c2ecf20Sopenharmony_ci	      SPIFI_CMD_FRAMEFORM_OPCODE_ONLY;
1658c2ecf20Sopenharmony_ci	writel(cmd, spifi->io_base + SPIFI_CMD);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	while (len--)
1688c2ecf20Sopenharmony_ci		writeb(*buf++, spifi->io_base + SPIFI_DATA);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return nxp_spifi_wait_for_cmd(spifi);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic ssize_t nxp_spifi_read(struct spi_nor *nor, loff_t from, size_t len,
1748c2ecf20Sopenharmony_ci			      u_char *buf)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	struct nxp_spifi *spifi = nor->priv;
1778c2ecf20Sopenharmony_ci	int ret;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	ret = nxp_spifi_set_memory_mode_on(spifi);
1808c2ecf20Sopenharmony_ci	if (ret)
1818c2ecf20Sopenharmony_ci		return ret;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	memcpy_fromio(buf, spifi->flash_base + from, len);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return len;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic ssize_t nxp_spifi_write(struct spi_nor *nor, loff_t to, size_t len,
1898c2ecf20Sopenharmony_ci			       const u_char *buf)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	struct nxp_spifi *spifi = nor->priv;
1928c2ecf20Sopenharmony_ci	u32 cmd;
1938c2ecf20Sopenharmony_ci	int ret;
1948c2ecf20Sopenharmony_ci	size_t i;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	ret = nxp_spifi_set_memory_mode_off(spifi);
1978c2ecf20Sopenharmony_ci	if (ret)
1988c2ecf20Sopenharmony_ci		return ret;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	writel(to, spifi->io_base + SPIFI_ADDR);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	cmd = SPIFI_CMD_DOUT |
2038c2ecf20Sopenharmony_ci	      SPIFI_CMD_DATALEN(len) |
2048c2ecf20Sopenharmony_ci	      SPIFI_CMD_FIELDFORM_ALL_SERIAL |
2058c2ecf20Sopenharmony_ci	      SPIFI_CMD_OPCODE(nor->program_opcode) |
2068c2ecf20Sopenharmony_ci	      SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1);
2078c2ecf20Sopenharmony_ci	writel(cmd, spifi->io_base + SPIFI_CMD);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++)
2108c2ecf20Sopenharmony_ci		writeb(buf[i], spifi->io_base + SPIFI_DATA);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	ret = nxp_spifi_wait_for_cmd(spifi);
2138c2ecf20Sopenharmony_ci	if (ret)
2148c2ecf20Sopenharmony_ci		return ret;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return len;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct nxp_spifi *spifi = nor->priv;
2228c2ecf20Sopenharmony_ci	u32 cmd;
2238c2ecf20Sopenharmony_ci	int ret;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	ret = nxp_spifi_set_memory_mode_off(spifi);
2268c2ecf20Sopenharmony_ci	if (ret)
2278c2ecf20Sopenharmony_ci		return ret;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	writel(offs, spifi->io_base + SPIFI_ADDR);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	cmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL |
2328c2ecf20Sopenharmony_ci	      SPIFI_CMD_OPCODE(nor->erase_opcode) |
2338c2ecf20Sopenharmony_ci	      SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1);
2348c2ecf20Sopenharmony_ci	writel(cmd, spifi->io_base + SPIFI_CMD);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return nxp_spifi_wait_for_cmd(spifi);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	switch (spifi->nor.read_proto) {
2428c2ecf20Sopenharmony_ci	case SNOR_PROTO_1_1_1:
2438c2ecf20Sopenharmony_ci		spifi->mcmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL;
2448c2ecf20Sopenharmony_ci		break;
2458c2ecf20Sopenharmony_ci	case SNOR_PROTO_1_1_2:
2468c2ecf20Sopenharmony_ci	case SNOR_PROTO_1_1_4:
2478c2ecf20Sopenharmony_ci		spifi->mcmd = SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA;
2488c2ecf20Sopenharmony_ci		break;
2498c2ecf20Sopenharmony_ci	default:
2508c2ecf20Sopenharmony_ci		dev_err(spifi->dev, "unsupported SPI read mode\n");
2518c2ecf20Sopenharmony_ci		return -EINVAL;
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/* Memory mode supports address length between 1 and 4 */
2558c2ecf20Sopenharmony_ci	if (spifi->nor.addr_width < 1 || spifi->nor.addr_width > 4)
2568c2ecf20Sopenharmony_ci		return -EINVAL;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	spifi->mcmd |= SPIFI_CMD_OPCODE(spifi->nor.read_opcode) |
2598c2ecf20Sopenharmony_ci		       SPIFI_CMD_INTLEN(spifi->nor.read_dummy / 8) |
2608c2ecf20Sopenharmony_ci		       SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return 0;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic void nxp_spifi_dummy_id_read(struct spi_nor *nor)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	u8 id[SPI_NOR_MAX_ID_LEN];
2688c2ecf20Sopenharmony_ci	nor->controller_ops->read_reg(nor, SPINOR_OP_RDID, id,
2698c2ecf20Sopenharmony_ci				      SPI_NOR_MAX_ID_LEN);
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic const struct spi_nor_controller_ops nxp_spifi_controller_ops = {
2738c2ecf20Sopenharmony_ci	.read_reg  = nxp_spifi_read_reg,
2748c2ecf20Sopenharmony_ci	.write_reg = nxp_spifi_write_reg,
2758c2ecf20Sopenharmony_ci	.read  = nxp_spifi_read,
2768c2ecf20Sopenharmony_ci	.write = nxp_spifi_write,
2778c2ecf20Sopenharmony_ci	.erase = nxp_spifi_erase,
2788c2ecf20Sopenharmony_ci};
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
2818c2ecf20Sopenharmony_ci				 struct device_node *np)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	struct spi_nor_hwcaps hwcaps = {
2848c2ecf20Sopenharmony_ci		.mask = SNOR_HWCAPS_READ |
2858c2ecf20Sopenharmony_ci			SNOR_HWCAPS_READ_FAST |
2868c2ecf20Sopenharmony_ci			SNOR_HWCAPS_PP,
2878c2ecf20Sopenharmony_ci	};
2888c2ecf20Sopenharmony_ci	u32 ctrl, property;
2898c2ecf20Sopenharmony_ci	u16 mode = 0;
2908c2ecf20Sopenharmony_ci	int ret;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (!of_property_read_u32(np, "spi-rx-bus-width", &property)) {
2938c2ecf20Sopenharmony_ci		switch (property) {
2948c2ecf20Sopenharmony_ci		case 1:
2958c2ecf20Sopenharmony_ci			break;
2968c2ecf20Sopenharmony_ci		case 2:
2978c2ecf20Sopenharmony_ci			mode |= SPI_RX_DUAL;
2988c2ecf20Sopenharmony_ci			break;
2998c2ecf20Sopenharmony_ci		case 4:
3008c2ecf20Sopenharmony_ci			mode |= SPI_RX_QUAD;
3018c2ecf20Sopenharmony_ci			break;
3028c2ecf20Sopenharmony_ci		default:
3038c2ecf20Sopenharmony_ci			dev_err(spifi->dev, "unsupported rx-bus-width\n");
3048c2ecf20Sopenharmony_ci			return -EINVAL;
3058c2ecf20Sopenharmony_ci		}
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	if (of_find_property(np, "spi-cpha", NULL))
3098c2ecf20Sopenharmony_ci		mode |= SPI_CPHA;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (of_find_property(np, "spi-cpol", NULL))
3128c2ecf20Sopenharmony_ci		mode |= SPI_CPOL;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* Setup control register defaults */
3158c2ecf20Sopenharmony_ci	ctrl = SPIFI_CTRL_TIMEOUT(1000) |
3168c2ecf20Sopenharmony_ci	       SPIFI_CTRL_CSHIGH(15) |
3178c2ecf20Sopenharmony_ci	       SPIFI_CTRL_FBCLK;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (mode & SPI_RX_DUAL) {
3208c2ecf20Sopenharmony_ci		ctrl |= SPIFI_CTRL_DUAL;
3218c2ecf20Sopenharmony_ci		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
3228c2ecf20Sopenharmony_ci	} else if (mode & SPI_RX_QUAD) {
3238c2ecf20Sopenharmony_ci		ctrl &= ~SPIFI_CTRL_DUAL;
3248c2ecf20Sopenharmony_ci		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
3258c2ecf20Sopenharmony_ci	} else {
3268c2ecf20Sopenharmony_ci		ctrl |= SPIFI_CTRL_DUAL;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	switch (mode & (SPI_CPHA | SPI_CPOL)) {
3308c2ecf20Sopenharmony_ci	case SPI_MODE_0:
3318c2ecf20Sopenharmony_ci		ctrl &= ~SPIFI_CTRL_MODE3;
3328c2ecf20Sopenharmony_ci		break;
3338c2ecf20Sopenharmony_ci	case SPI_MODE_3:
3348c2ecf20Sopenharmony_ci		ctrl |= SPIFI_CTRL_MODE3;
3358c2ecf20Sopenharmony_ci		break;
3368c2ecf20Sopenharmony_ci	default:
3378c2ecf20Sopenharmony_ci		dev_err(spifi->dev, "only mode 0 and 3 supported\n");
3388c2ecf20Sopenharmony_ci		return -EINVAL;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	writel(ctrl, spifi->io_base + SPIFI_CTRL);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	spifi->nor.dev   = spifi->dev;
3448c2ecf20Sopenharmony_ci	spi_nor_set_flash_node(&spifi->nor, np);
3458c2ecf20Sopenharmony_ci	spifi->nor.priv  = spifi;
3468c2ecf20Sopenharmony_ci	spifi->nor.controller_ops = &nxp_spifi_controller_ops;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	/*
3498c2ecf20Sopenharmony_ci	 * The first read on a hard reset isn't reliable so do a
3508c2ecf20Sopenharmony_ci	 * dummy read of the id before calling spi_nor_scan().
3518c2ecf20Sopenharmony_ci	 * The reason for this problem is unknown.
3528c2ecf20Sopenharmony_ci	 *
3538c2ecf20Sopenharmony_ci	 * The official NXP spifilib uses more or less the same
3548c2ecf20Sopenharmony_ci	 * workaround that is applied here by reading the device
3558c2ecf20Sopenharmony_ci	 * id multiple times.
3568c2ecf20Sopenharmony_ci	 */
3578c2ecf20Sopenharmony_ci	nxp_spifi_dummy_id_read(&spifi->nor);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	ret = spi_nor_scan(&spifi->nor, NULL, &hwcaps);
3608c2ecf20Sopenharmony_ci	if (ret) {
3618c2ecf20Sopenharmony_ci		dev_err(spifi->dev, "device scan failed\n");
3628c2ecf20Sopenharmony_ci		return ret;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	ret = nxp_spifi_setup_memory_cmd(spifi);
3668c2ecf20Sopenharmony_ci	if (ret) {
3678c2ecf20Sopenharmony_ci		dev_err(spifi->dev, "memory command setup failed\n");
3688c2ecf20Sopenharmony_ci		return ret;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	ret = mtd_device_register(&spifi->nor.mtd, NULL, 0);
3728c2ecf20Sopenharmony_ci	if (ret) {
3738c2ecf20Sopenharmony_ci		dev_err(spifi->dev, "mtd device parse failed\n");
3748c2ecf20Sopenharmony_ci		return ret;
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	return 0;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int nxp_spifi_probe(struct platform_device *pdev)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct device_node *flash_np;
3838c2ecf20Sopenharmony_ci	struct nxp_spifi *spifi;
3848c2ecf20Sopenharmony_ci	struct resource *res;
3858c2ecf20Sopenharmony_ci	int ret;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	spifi = devm_kzalloc(&pdev->dev, sizeof(*spifi), GFP_KERNEL);
3888c2ecf20Sopenharmony_ci	if (!spifi)
3898c2ecf20Sopenharmony_ci		return -ENOMEM;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spifi");
3928c2ecf20Sopenharmony_ci	spifi->io_base = devm_ioremap_resource(&pdev->dev, res);
3938c2ecf20Sopenharmony_ci	if (IS_ERR(spifi->io_base))
3948c2ecf20Sopenharmony_ci		return PTR_ERR(spifi->io_base);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash");
3978c2ecf20Sopenharmony_ci	spifi->flash_base = devm_ioremap_resource(&pdev->dev, res);
3988c2ecf20Sopenharmony_ci	if (IS_ERR(spifi->flash_base))
3998c2ecf20Sopenharmony_ci		return PTR_ERR(spifi->flash_base);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	spifi->clk_spifi = devm_clk_get(&pdev->dev, "spifi");
4028c2ecf20Sopenharmony_ci	if (IS_ERR(spifi->clk_spifi)) {
4038c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "spifi clock not found\n");
4048c2ecf20Sopenharmony_ci		return PTR_ERR(spifi->clk_spifi);
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	spifi->clk_reg = devm_clk_get(&pdev->dev, "reg");
4088c2ecf20Sopenharmony_ci	if (IS_ERR(spifi->clk_reg)) {
4098c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "reg clock not found\n");
4108c2ecf20Sopenharmony_ci		return PTR_ERR(spifi->clk_reg);
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(spifi->clk_reg);
4148c2ecf20Sopenharmony_ci	if (ret) {
4158c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "unable to enable reg clock\n");
4168c2ecf20Sopenharmony_ci		return ret;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(spifi->clk_spifi);
4208c2ecf20Sopenharmony_ci	if (ret) {
4218c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "unable to enable spifi clock\n");
4228c2ecf20Sopenharmony_ci		goto dis_clk_reg;
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	spifi->dev = &pdev->dev;
4268c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, spifi);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	/* Initialize and reset device */
4298c2ecf20Sopenharmony_ci	nxp_spifi_reset(spifi);
4308c2ecf20Sopenharmony_ci	writel(0, spifi->io_base + SPIFI_IDATA);
4318c2ecf20Sopenharmony_ci	writel(0, spifi->io_base + SPIFI_MCMD);
4328c2ecf20Sopenharmony_ci	nxp_spifi_reset(spifi);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	flash_np = of_get_next_available_child(pdev->dev.of_node, NULL);
4358c2ecf20Sopenharmony_ci	if (!flash_np) {
4368c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no SPI flash device to configure\n");
4378c2ecf20Sopenharmony_ci		ret = -ENODEV;
4388c2ecf20Sopenharmony_ci		goto dis_clks;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	ret = nxp_spifi_setup_flash(spifi, flash_np);
4428c2ecf20Sopenharmony_ci	of_node_put(flash_np);
4438c2ecf20Sopenharmony_ci	if (ret) {
4448c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "unable to setup flash chip\n");
4458c2ecf20Sopenharmony_ci		goto dis_clks;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	return 0;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cidis_clks:
4518c2ecf20Sopenharmony_ci	clk_disable_unprepare(spifi->clk_spifi);
4528c2ecf20Sopenharmony_cidis_clk_reg:
4538c2ecf20Sopenharmony_ci	clk_disable_unprepare(spifi->clk_reg);
4548c2ecf20Sopenharmony_ci	return ret;
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic int nxp_spifi_remove(struct platform_device *pdev)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	struct nxp_spifi *spifi = platform_get_drvdata(pdev);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	mtd_device_unregister(&spifi->nor.mtd);
4628c2ecf20Sopenharmony_ci	clk_disable_unprepare(spifi->clk_spifi);
4638c2ecf20Sopenharmony_ci	clk_disable_unprepare(spifi->clk_reg);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	return 0;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic const struct of_device_id nxp_spifi_match[] = {
4698c2ecf20Sopenharmony_ci	{.compatible = "nxp,lpc1773-spifi"},
4708c2ecf20Sopenharmony_ci	{ /* sentinel */ }
4718c2ecf20Sopenharmony_ci};
4728c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, nxp_spifi_match);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic struct platform_driver nxp_spifi_driver = {
4758c2ecf20Sopenharmony_ci	.probe	= nxp_spifi_probe,
4768c2ecf20Sopenharmony_ci	.remove	= nxp_spifi_remove,
4778c2ecf20Sopenharmony_ci	.driver	= {
4788c2ecf20Sopenharmony_ci		.name = "nxp-spifi",
4798c2ecf20Sopenharmony_ci		.of_match_table = nxp_spifi_match,
4808c2ecf20Sopenharmony_ci	},
4818c2ecf20Sopenharmony_ci};
4828c2ecf20Sopenharmony_cimodule_platform_driver(nxp_spifi_driver);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NXP SPI Flash Interface driver");
4858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
4868c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
487