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 | |
| 11 | static const struct flash_info xilinx_parts[] = { |
| 12 | /* Xilinx S3AN Internal Flash */ |
| 13 | { "3S50AN", S3AN_INFO(0x1f2200, 64, 264) }, |
| 14 | { "3S200AN", S3AN_INFO(0x1f2400, 256, 264) }, |
| 15 | { "3S400AN", S3AN_INFO(0x1f2400, 256, 264) }, |
| 16 | { "3S700AN", S3AN_INFO(0x1f2500, 512, 264) }, |
| 17 | { "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) }, |
| 18 | }; |
| 19 | |
| 20 | /* |
| 21 | * This code converts an address to the Default Address Mode, that has non |
| 22 | * power of two page sizes. We must support this mode because it is the default |
| 23 | * mode supported by Xilinx tools, it can access the whole flash area and |
| 24 | * changing over to the Power-of-two mode is irreversible and corrupts the |
| 25 | * original data. |
| 26 | * Addr can safely be unsigned int, the biggest S3AN device is smaller than |
| 27 | * 4 MiB. |
| 28 | */ |
| 29 | static u32 s3an_convert_addr(struct spi_nor *nor, u32 addr) |
| 30 | { |
Tudor Ambarus | 5854d4a | 2021-10-29 20:26:12 +0300 | [diff] [blame] | 31 | u32 page_size = nor->params->page_size; |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 32 | u32 offset, page; |
| 33 | |
Tudor Ambarus | 5854d4a | 2021-10-29 20:26:12 +0300 | [diff] [blame] | 34 | offset = addr % page_size; |
| 35 | page = addr / page_size; |
| 36 | page <<= (page_size > 512) ? 10 : 9; |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 37 | |
| 38 | return page | offset; |
| 39 | } |
| 40 | |
| 41 | static int xilinx_nor_setup(struct spi_nor *nor, |
| 42 | const struct spi_nor_hwcaps *hwcaps) |
| 43 | { |
Tudor Ambarus | 5854d4a | 2021-10-29 20:26:12 +0300 | [diff] [blame] | 44 | u32 page_size; |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 45 | int ret; |
| 46 | |
| 47 | ret = spi_nor_xread_sr(nor, nor->bouncebuf); |
| 48 | if (ret) |
| 49 | return ret; |
| 50 | |
| 51 | nor->erase_opcode = SPINOR_OP_XSE; |
| 52 | nor->program_opcode = SPINOR_OP_XPP; |
| 53 | nor->read_opcode = SPINOR_OP_READ; |
| 54 | nor->flags |= SNOR_F_NO_OP_CHIP_ERASE; |
| 55 | |
| 56 | /* |
| 57 | * This flashes have a page size of 264 or 528 bytes (known as |
| 58 | * Default addressing mode). It can be changed to a more standard |
| 59 | * Power of two mode where the page size is 256/512. This comes |
| 60 | * with a price: there is 3% less of space, the data is corrupted |
| 61 | * and the page size cannot be changed back to default addressing |
| 62 | * mode. |
| 63 | * |
| 64 | * The current addressing mode can be read from the XRDSR register |
| 65 | * and should not be changed, because is a destructive operation. |
| 66 | */ |
| 67 | if (nor->bouncebuf[0] & XSR_PAGESIZE) { |
| 68 | /* Flash in Power of 2 mode */ |
Tudor Ambarus | 5854d4a | 2021-10-29 20:26:12 +0300 | [diff] [blame] | 69 | page_size = (nor->params->page_size == 264) ? 256 : 512; |
| 70 | nor->params->page_size = page_size; |
| 71 | nor->mtd.writebufsize = page_size; |
Tudor Ambarus | f656b41 | 2021-12-07 16:02:41 +0200 | [diff] [blame] | 72 | nor->params->size = 8 * page_size * nor->info->n_sectors; |
Tudor Ambarus | 5854d4a | 2021-10-29 20:26:12 +0300 | [diff] [blame] | 73 | nor->mtd.erasesize = 8 * page_size; |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 74 | } else { |
| 75 | /* Flash in Default addressing mode */ |
Tudor Ambarus | 829ec64 | 2020-03-13 19:42:53 +0000 | [diff] [blame] | 76 | nor->params->convert_addr = s3an_convert_addr; |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 77 | nor->mtd.erasesize = nor->info->sector_size; |
| 78 | } |
| 79 | |
| 80 | return 0; |
| 81 | } |
| 82 | |
Tudor Ambarus | 3fdad69e | 2021-10-29 20:26:17 +0300 | [diff] [blame] | 83 | static void xilinx_late_init(struct spi_nor *nor) |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 84 | { |
Tudor Ambarus | 829ec64 | 2020-03-13 19:42:53 +0000 | [diff] [blame] | 85 | nor->params->setup = xilinx_nor_setup; |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 86 | } |
| 87 | |
| 88 | static const struct spi_nor_fixups xilinx_fixups = { |
Tudor Ambarus | 3fdad69e | 2021-10-29 20:26:17 +0300 | [diff] [blame] | 89 | .late_init = xilinx_late_init, |
Boris Brezillon | 2d47cac | 2020-03-13 19:42:50 +0000 | [diff] [blame] | 90 | }; |
| 91 | |
| 92 | const struct spi_nor_manufacturer spi_nor_xilinx = { |
| 93 | .name = "xilinx", |
| 94 | .parts = xilinx_parts, |
| 95 | .nparts = ARRAY_SIZE(xilinx_parts), |
| 96 | .fixups = &xilinx_fixups, |
| 97 | }; |