Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * Copyright (C) 2005, Intec Automation Inc. |
| 4 | * Copyright (C) 2014, Freescale Semiconductor, Inc. |
| 5 | */ |
| 6 | |
| 7 | #include <linux/mtd/spi-nor.h> |
| 8 | |
| 9 | #include "core.h" |
| 10 | |
Michael Walle | 8b7a2e00d | 2022-02-23 14:43:48 +0100 | [diff] [blame] | 11 | #define XILINX_OP_SE 0x50 /* Sector erase */ |
| 12 | #define XILINX_OP_PP 0x82 /* Page program */ |
| 13 | #define XILINX_OP_RDSR 0xd7 /* Read status register */ |
Michael Walle | 8b4195c | 2022-02-23 14:43:47 +0100 | [diff] [blame] | 14 | |
| 15 | #define XSR_PAGESIZE BIT(0) /* Page size in Po2 or Linear */ |
| 16 | #define XSR_RDY BIT(7) /* Ready */ |
| 17 | |
Tudor Ambarus | c0abb86 | 2022-04-20 13:34:25 +0300 | [diff] [blame] | 18 | #define XILINX_RDSR_OP(buf) \ |
| 19 | SPI_MEM_OP(SPI_MEM_OP_CMD(XILINX_OP_RDSR, 0), \ |
| 20 | SPI_MEM_OP_NO_ADDR, \ |
| 21 | SPI_MEM_OP_NO_DUMMY, \ |
| 22 | SPI_MEM_OP_DATA_IN(1, buf, 0)) |
| 23 | |
Michael Walle | 8e52f54 | 2023-09-08 12:16:47 +0200 | [diff] [blame] | 24 | #define S3AN_FLASH(_id, _name, _n_sectors, _page_size) \ |
| 25 | .id = _id, \ |
| 26 | .name = _name, \ |
| 27 | .size = 8 * (_page_size) * (_n_sectors), \ |
| 28 | .sector_size = (8 * (_page_size)), \ |
| 29 | .page_size = (_page_size), \ |
| 30 | .flags = SPI_NOR_NO_FR |
Michael Walle | 8b4195c | 2022-02-23 14:43:47 +0100 | [diff] [blame] | 31 | |
| 32 | /* Xilinx S3AN share MFR with Atmel SPI NOR */ |
Michael Walle | 45acce2 | 2022-02-23 14:43:41 +0100 | [diff] [blame] | 33 | static const struct flash_info xilinx_nor_parts[] = { |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 34 | /* Xilinx S3AN Internal Flash */ |
Michael Walle | 8e52f54 | 2023-09-08 12:16:47 +0200 | [diff] [blame] | 35 | { S3AN_FLASH(SNOR_ID(0x1f, 0x22, 0x00), "3S50AN", 64, 264) }, |
| 36 | { S3AN_FLASH(SNOR_ID(0x1f, 0x24, 0x00), "3S200AN", 256, 264) }, |
| 37 | { S3AN_FLASH(SNOR_ID(0x1f, 0x24, 0x00), "3S400AN", 256, 264) }, |
| 38 | { S3AN_FLASH(SNOR_ID(0x1f, 0x25, 0x00), "3S700AN", 512, 264) }, |
| 39 | { S3AN_FLASH(SNOR_ID(0x1f, 0x26, 0x00), "3S1400AN", 512, 528) }, |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 40 | }; |
| 41 | |
| 42 | /* |
| 43 | * This code converts an address to the Default Address Mode, that has non |
| 44 | * power of two page sizes. We must support this mode because it is the default |
| 45 | * mode supported by Xilinx tools, it can access the whole flash area and |
| 46 | * changing over to the Power-of-two mode is irreversible and corrupts the |
| 47 | * original data. |
| 48 | * Addr can safely be unsigned int, the biggest S3AN device is smaller than |
| 49 | * 4 MiB. |
| 50 | */ |
Michael Walle | 45acce2 | 2022-02-23 14:43:41 +0100 | [diff] [blame] | 51 | static u32 s3an_nor_convert_addr(struct spi_nor *nor, u32 addr) |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 52 | { |
Tudor Ambarus | 5854d4a | 2021-10-29 20:26:12 +0300 | [diff] [blame] | 53 | u32 page_size = nor->params->page_size; |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 54 | u32 offset, page; |
| 55 | |
Tudor Ambarus | 5854d4a | 2021-10-29 20:26:12 +0300 | [diff] [blame] | 56 | offset = addr % page_size; |
| 57 | page = addr / page_size; |
| 58 | page <<= (page_size > 512) ? 10 : 9; |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 59 | |
| 60 | return page | offset; |
| 61 | } |
| 62 | |
Michael Walle | 8b4195c | 2022-02-23 14:43:47 +0100 | [diff] [blame] | 63 | /** |
Michael Walle | 8b7a2e00d | 2022-02-23 14:43:48 +0100 | [diff] [blame] | 64 | * xilinx_nor_read_sr() - Read the Status Register on S3AN flashes. |
Michael Walle | 8b4195c | 2022-02-23 14:43:47 +0100 | [diff] [blame] | 65 | * @nor: pointer to 'struct spi_nor'. |
| 66 | * @sr: pointer to a DMA-able buffer where the value of the |
| 67 | * Status Register will be written. |
| 68 | * |
| 69 | * Return: 0 on success, -errno otherwise. |
| 70 | */ |
Michael Walle | 8b7a2e00d | 2022-02-23 14:43:48 +0100 | [diff] [blame] | 71 | static int xilinx_nor_read_sr(struct spi_nor *nor, u8 *sr) |
Michael Walle | 8b4195c | 2022-02-23 14:43:47 +0100 | [diff] [blame] | 72 | { |
| 73 | int ret; |
| 74 | |
| 75 | if (nor->spimem) { |
Tudor Ambarus | c0abb86 | 2022-04-20 13:34:25 +0300 | [diff] [blame] | 76 | struct spi_mem_op op = XILINX_RDSR_OP(sr); |
Michael Walle | 8b4195c | 2022-02-23 14:43:47 +0100 | [diff] [blame] | 77 | |
| 78 | spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); |
| 79 | |
| 80 | ret = spi_mem_exec_op(nor->spimem, &op); |
| 81 | } else { |
Michael Walle | 8b7a2e00d | 2022-02-23 14:43:48 +0100 | [diff] [blame] | 82 | ret = spi_nor_controller_ops_read_reg(nor, XILINX_OP_RDSR, sr, |
Michael Walle | 8b4195c | 2022-02-23 14:43:47 +0100 | [diff] [blame] | 83 | 1); |
| 84 | } |
| 85 | |
| 86 | if (ret) |
Michael Walle | 56b852e | 2022-02-23 14:43:49 +0100 | [diff] [blame] | 87 | dev_dbg(nor->dev, "error %d reading SR\n", ret); |
Michael Walle | 8b4195c | 2022-02-23 14:43:47 +0100 | [diff] [blame] | 88 | |
| 89 | return ret; |
| 90 | } |
| 91 | |
| 92 | /** |
Michael Walle | 8b7a2e00d | 2022-02-23 14:43:48 +0100 | [diff] [blame] | 93 | * xilinx_nor_sr_ready() - Query the Status Register of the S3AN flash to see |
| 94 | * if the flash is ready for new commands. |
Michael Walle | 8b4195c | 2022-02-23 14:43:47 +0100 | [diff] [blame] | 95 | * @nor: pointer to 'struct spi_nor'. |
| 96 | * |
| 97 | * Return: 1 if ready, 0 if not ready, -errno on errors. |
| 98 | */ |
Michael Walle | 8b7a2e00d | 2022-02-23 14:43:48 +0100 | [diff] [blame] | 99 | static int xilinx_nor_sr_ready(struct spi_nor *nor) |
Michael Walle | 8b4195c | 2022-02-23 14:43:47 +0100 | [diff] [blame] | 100 | { |
| 101 | int ret; |
| 102 | |
Michael Walle | 8b7a2e00d | 2022-02-23 14:43:48 +0100 | [diff] [blame] | 103 | ret = xilinx_nor_read_sr(nor, nor->bouncebuf); |
Michael Walle | 8b4195c | 2022-02-23 14:43:47 +0100 | [diff] [blame] | 104 | if (ret) |
| 105 | return ret; |
| 106 | |
| 107 | return !!(nor->bouncebuf[0] & XSR_RDY); |
| 108 | } |
| 109 | |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 110 | static int xilinx_nor_setup(struct spi_nor *nor, |
| 111 | const struct spi_nor_hwcaps *hwcaps) |
| 112 | { |
Tudor Ambarus | 5854d4a | 2021-10-29 20:26:12 +0300 | [diff] [blame] | 113 | u32 page_size; |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 114 | int ret; |
| 115 | |
Michael Walle | 8b7a2e00d | 2022-02-23 14:43:48 +0100 | [diff] [blame] | 116 | ret = xilinx_nor_read_sr(nor, nor->bouncebuf); |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 117 | if (ret) |
| 118 | return ret; |
| 119 | |
Michael Walle | 8b7a2e00d | 2022-02-23 14:43:48 +0100 | [diff] [blame] | 120 | nor->erase_opcode = XILINX_OP_SE; |
| 121 | nor->program_opcode = XILINX_OP_PP; |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 122 | nor->read_opcode = SPINOR_OP_READ; |
| 123 | nor->flags |= SNOR_F_NO_OP_CHIP_ERASE; |
| 124 | |
| 125 | /* |
| 126 | * This flashes have a page size of 264 or 528 bytes (known as |
| 127 | * Default addressing mode). It can be changed to a more standard |
| 128 | * Power of two mode where the page size is 256/512. This comes |
| 129 | * with a price: there is 3% less of space, the data is corrupted |
| 130 | * and the page size cannot be changed back to default addressing |
| 131 | * mode. |
| 132 | * |
| 133 | * The current addressing mode can be read from the XRDSR register |
| 134 | * and should not be changed, because is a destructive operation. |
| 135 | */ |
| 136 | if (nor->bouncebuf[0] & XSR_PAGESIZE) { |
| 137 | /* Flash in Power of 2 mode */ |
Tudor Ambarus | 5854d4a | 2021-10-29 20:26:12 +0300 | [diff] [blame] | 138 | page_size = (nor->params->page_size == 264) ? 256 : 512; |
| 139 | nor->params->page_size = page_size; |
| 140 | nor->mtd.writebufsize = page_size; |
Michael Walle | 0554eff | 2023-09-08 12:16:23 +0200 | [diff] [blame] | 141 | nor->params->size = nor->info->size; |
Tudor Ambarus | 5854d4a | 2021-10-29 20:26:12 +0300 | [diff] [blame] | 142 | nor->mtd.erasesize = 8 * page_size; |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 143 | } else { |
| 144 | /* Flash in Default addressing mode */ |
Michael Walle | 45acce2 | 2022-02-23 14:43:41 +0100 | [diff] [blame] | 145 | nor->params->convert_addr = s3an_nor_convert_addr; |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 146 | nor->mtd.erasesize = nor->info->sector_size; |
| 147 | } |
| 148 | |
| 149 | return 0; |
| 150 | } |
| 151 | |
Takahiro Kuwano | d534fd9 | 2023-07-26 10:52:47 +0300 | [diff] [blame] | 152 | static int xilinx_nor_late_init(struct spi_nor *nor) |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 153 | { |
Tudor Ambarus | 829ec64 | 2020-03-13 19:42:53 +0000 | [diff] [blame] | 154 | nor->params->setup = xilinx_nor_setup; |
Michael Walle | 8b7a2e00d | 2022-02-23 14:43:48 +0100 | [diff] [blame] | 155 | nor->params->ready = xilinx_nor_sr_ready; |
Takahiro Kuwano | d534fd9 | 2023-07-26 10:52:47 +0300 | [diff] [blame] | 156 | |
| 157 | return 0; |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 158 | } |
| 159 | |
Michael Walle | 45acce2 | 2022-02-23 14:43:41 +0100 | [diff] [blame] | 160 | static const struct spi_nor_fixups xilinx_nor_fixups = { |
| 161 | .late_init = xilinx_nor_late_init, |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 162 | }; |
| 163 | |
| 164 | const struct spi_nor_manufacturer spi_nor_xilinx = { |
| 165 | .name = "xilinx", |
Michael Walle | 45acce2 | 2022-02-23 14:43:41 +0100 | [diff] [blame] | 166 | .parts = xilinx_nor_parts, |
| 167 | .nparts = ARRAY_SIZE(xilinx_nor_parts), |
| 168 | .fixups = &xilinx_nor_fixups, |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 169 | }; |