162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2005, Intec Automation Inc.
462306a36Sopenharmony_ci * Copyright (C) 2014, Freescale Semiconductor, Inc.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/mtd/spi-nor.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "core.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define XILINX_OP_SE		0x50	/* Sector erase */
1262306a36Sopenharmony_ci#define XILINX_OP_PP		0x82	/* Page program */
1362306a36Sopenharmony_ci#define XILINX_OP_RDSR		0xd7	/* Read status register */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define XSR_PAGESIZE		BIT(0)	/* Page size in Po2 or Linear */
1662306a36Sopenharmony_ci#define XSR_RDY			BIT(7)	/* Ready */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define XILINX_RDSR_OP(buf)						\
1962306a36Sopenharmony_ci	SPI_MEM_OP(SPI_MEM_OP_CMD(XILINX_OP_RDSR, 0),			\
2062306a36Sopenharmony_ci		   SPI_MEM_OP_NO_ADDR,					\
2162306a36Sopenharmony_ci		   SPI_MEM_OP_NO_DUMMY,					\
2262306a36Sopenharmony_ci		   SPI_MEM_OP_DATA_IN(1, buf, 0))
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define S3AN_INFO(_jedec_id, _n_sectors, _page_size)			\
2562306a36Sopenharmony_ci		.id = {							\
2662306a36Sopenharmony_ci			((_jedec_id) >> 16) & 0xff,			\
2762306a36Sopenharmony_ci			((_jedec_id) >> 8) & 0xff,			\
2862306a36Sopenharmony_ci			(_jedec_id) & 0xff				\
2962306a36Sopenharmony_ci			},						\
3062306a36Sopenharmony_ci		.id_len = 3,						\
3162306a36Sopenharmony_ci		.sector_size = (8 * (_page_size)),			\
3262306a36Sopenharmony_ci		.n_sectors = (_n_sectors),				\
3362306a36Sopenharmony_ci		.page_size = (_page_size),				\
3462306a36Sopenharmony_ci		.n_banks = 1,						\
3562306a36Sopenharmony_ci		.addr_nbytes = 3,					\
3662306a36Sopenharmony_ci		.flags = SPI_NOR_NO_FR
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* Xilinx S3AN share MFR with Atmel SPI NOR */
3962306a36Sopenharmony_cistatic const struct flash_info xilinx_nor_parts[] = {
4062306a36Sopenharmony_ci	/* Xilinx S3AN Internal Flash */
4162306a36Sopenharmony_ci	{ "3S50AN", S3AN_INFO(0x1f2200, 64, 264) },
4262306a36Sopenharmony_ci	{ "3S200AN", S3AN_INFO(0x1f2400, 256, 264) },
4362306a36Sopenharmony_ci	{ "3S400AN", S3AN_INFO(0x1f2400, 256, 264) },
4462306a36Sopenharmony_ci	{ "3S700AN", S3AN_INFO(0x1f2500, 512, 264) },
4562306a36Sopenharmony_ci	{ "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) },
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * This code converts an address to the Default Address Mode, that has non
5062306a36Sopenharmony_ci * power of two page sizes. We must support this mode because it is the default
5162306a36Sopenharmony_ci * mode supported by Xilinx tools, it can access the whole flash area and
5262306a36Sopenharmony_ci * changing over to the Power-of-two mode is irreversible and corrupts the
5362306a36Sopenharmony_ci * original data.
5462306a36Sopenharmony_ci * Addr can safely be unsigned int, the biggest S3AN device is smaller than
5562306a36Sopenharmony_ci * 4 MiB.
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_cistatic u32 s3an_nor_convert_addr(struct spi_nor *nor, u32 addr)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	u32 page_size = nor->params->page_size;
6062306a36Sopenharmony_ci	u32 offset, page;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	offset = addr % page_size;
6362306a36Sopenharmony_ci	page = addr / page_size;
6462306a36Sopenharmony_ci	page <<= (page_size > 512) ? 10 : 9;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return page | offset;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/**
7062306a36Sopenharmony_ci * xilinx_nor_read_sr() - Read the Status Register on S3AN flashes.
7162306a36Sopenharmony_ci * @nor:	pointer to 'struct spi_nor'.
7262306a36Sopenharmony_ci * @sr:		pointer to a DMA-able buffer where the value of the
7362306a36Sopenharmony_ci *              Status Register will be written.
7462306a36Sopenharmony_ci *
7562306a36Sopenharmony_ci * Return: 0 on success, -errno otherwise.
7662306a36Sopenharmony_ci */
7762306a36Sopenharmony_cistatic int xilinx_nor_read_sr(struct spi_nor *nor, u8 *sr)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	int ret;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (nor->spimem) {
8262306a36Sopenharmony_ci		struct spi_mem_op op = XILINX_RDSR_OP(sr);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci		spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		ret = spi_mem_exec_op(nor->spimem, &op);
8762306a36Sopenharmony_ci	} else {
8862306a36Sopenharmony_ci		ret = spi_nor_controller_ops_read_reg(nor, XILINX_OP_RDSR, sr,
8962306a36Sopenharmony_ci						      1);
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (ret)
9362306a36Sopenharmony_ci		dev_dbg(nor->dev, "error %d reading SR\n", ret);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return ret;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/**
9962306a36Sopenharmony_ci * xilinx_nor_sr_ready() - Query the Status Register of the S3AN flash to see
10062306a36Sopenharmony_ci * if the flash is ready for new commands.
10162306a36Sopenharmony_ci * @nor:	pointer to 'struct spi_nor'.
10262306a36Sopenharmony_ci *
10362306a36Sopenharmony_ci * Return: 1 if ready, 0 if not ready, -errno on errors.
10462306a36Sopenharmony_ci */
10562306a36Sopenharmony_cistatic int xilinx_nor_sr_ready(struct spi_nor *nor)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	int ret;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	ret = xilinx_nor_read_sr(nor, nor->bouncebuf);
11062306a36Sopenharmony_ci	if (ret)
11162306a36Sopenharmony_ci		return ret;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return !!(nor->bouncebuf[0] & XSR_RDY);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic int xilinx_nor_setup(struct spi_nor *nor,
11762306a36Sopenharmony_ci			    const struct spi_nor_hwcaps *hwcaps)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	u32 page_size;
12062306a36Sopenharmony_ci	int ret;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	ret = xilinx_nor_read_sr(nor, nor->bouncebuf);
12362306a36Sopenharmony_ci	if (ret)
12462306a36Sopenharmony_ci		return ret;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	nor->erase_opcode = XILINX_OP_SE;
12762306a36Sopenharmony_ci	nor->program_opcode = XILINX_OP_PP;
12862306a36Sopenharmony_ci	nor->read_opcode = SPINOR_OP_READ;
12962306a36Sopenharmony_ci	nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/*
13262306a36Sopenharmony_ci	 * This flashes have a page size of 264 or 528 bytes (known as
13362306a36Sopenharmony_ci	 * Default addressing mode). It can be changed to a more standard
13462306a36Sopenharmony_ci	 * Power of two mode where the page size is 256/512. This comes
13562306a36Sopenharmony_ci	 * with a price: there is 3% less of space, the data is corrupted
13662306a36Sopenharmony_ci	 * and the page size cannot be changed back to default addressing
13762306a36Sopenharmony_ci	 * mode.
13862306a36Sopenharmony_ci	 *
13962306a36Sopenharmony_ci	 * The current addressing mode can be read from the XRDSR register
14062306a36Sopenharmony_ci	 * and should not be changed, because is a destructive operation.
14162306a36Sopenharmony_ci	 */
14262306a36Sopenharmony_ci	if (nor->bouncebuf[0] & XSR_PAGESIZE) {
14362306a36Sopenharmony_ci		/* Flash in Power of 2 mode */
14462306a36Sopenharmony_ci		page_size = (nor->params->page_size == 264) ? 256 : 512;
14562306a36Sopenharmony_ci		nor->params->page_size = page_size;
14662306a36Sopenharmony_ci		nor->mtd.writebufsize = page_size;
14762306a36Sopenharmony_ci		nor->params->size = 8 * page_size * nor->info->n_sectors;
14862306a36Sopenharmony_ci		nor->mtd.erasesize = 8 * page_size;
14962306a36Sopenharmony_ci	} else {
15062306a36Sopenharmony_ci		/* Flash in Default addressing mode */
15162306a36Sopenharmony_ci		nor->params->convert_addr = s3an_nor_convert_addr;
15262306a36Sopenharmony_ci		nor->mtd.erasesize = nor->info->sector_size;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return 0;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic int xilinx_nor_late_init(struct spi_nor *nor)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	nor->params->setup = xilinx_nor_setup;
16162306a36Sopenharmony_ci	nor->params->ready = xilinx_nor_sr_ready;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return 0;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic const struct spi_nor_fixups xilinx_nor_fixups = {
16762306a36Sopenharmony_ci	.late_init = xilinx_nor_late_init,
16862306a36Sopenharmony_ci};
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ciconst struct spi_nor_manufacturer spi_nor_xilinx = {
17162306a36Sopenharmony_ci	.name = "xilinx",
17262306a36Sopenharmony_ci	.parts = xilinx_nor_parts,
17362306a36Sopenharmony_ci	.nparts = ARRAY_SIZE(xilinx_nor_parts),
17462306a36Sopenharmony_ci	.fixups = &xilinx_nor_fixups,
17562306a36Sopenharmony_ci};
176