18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2005, Intec Automation Inc. 48c2ecf20Sopenharmony_ci * Copyright (C) 2014, Freescale Semiconductor, Inc. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/slab.h> 88c2ecf20Sopenharmony_ci#include <linux/sort.h> 98c2ecf20Sopenharmony_ci#include <linux/mtd/spi-nor.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "core.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define SFDP_PARAM_HEADER_ID(p) (((p)->id_msb << 8) | (p)->id_lsb) 148c2ecf20Sopenharmony_ci#define SFDP_PARAM_HEADER_PTP(p) \ 158c2ecf20Sopenharmony_ci (((p)->parameter_table_pointer[2] << 16) | \ 168c2ecf20Sopenharmony_ci ((p)->parameter_table_pointer[1] << 8) | \ 178c2ecf20Sopenharmony_ci ((p)->parameter_table_pointer[0] << 0)) 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */ 208c2ecf20Sopenharmony_ci#define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */ 218c2ecf20Sopenharmony_ci#define SFDP_4BAIT_ID 0xff84 /* 4-byte Address Instruction Table */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define SFDP_SIGNATURE 0x50444653U 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct sfdp_header { 268c2ecf20Sopenharmony_ci u32 signature; /* Ox50444653U <=> "SFDP" */ 278c2ecf20Sopenharmony_ci u8 minor; 288c2ecf20Sopenharmony_ci u8 major; 298c2ecf20Sopenharmony_ci u8 nph; /* 0-base number of parameter headers */ 308c2ecf20Sopenharmony_ci u8 unused; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci /* Basic Flash Parameter Table. */ 338c2ecf20Sopenharmony_ci struct sfdp_parameter_header bfpt_header; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* Fast Read settings. */ 378c2ecf20Sopenharmony_cistruct sfdp_bfpt_read { 388c2ecf20Sopenharmony_ci /* The Fast Read x-y-z hardware capability in params->hwcaps.mask. */ 398c2ecf20Sopenharmony_ci u32 hwcaps; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* 428c2ecf20Sopenharmony_ci * The <supported_bit> bit in <supported_dword> BFPT DWORD tells us 438c2ecf20Sopenharmony_ci * whether the Fast Read x-y-z command is supported. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci u32 supported_dword; 468c2ecf20Sopenharmony_ci u32 supported_bit; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* 498c2ecf20Sopenharmony_ci * The half-word at offset <setting_shift> in <setting_dword> BFPT DWORD 508c2ecf20Sopenharmony_ci * encodes the op code, the number of mode clocks and the number of wait 518c2ecf20Sopenharmony_ci * states to be used by Fast Read x-y-z command. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci u32 settings_dword; 548c2ecf20Sopenharmony_ci u32 settings_shift; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* The SPI protocol for this Fast Read x-y-z command. */ 578c2ecf20Sopenharmony_ci enum spi_nor_protocol proto; 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct sfdp_bfpt_erase { 618c2ecf20Sopenharmony_ci /* 628c2ecf20Sopenharmony_ci * The half-word at offset <shift> in DWORD <dwoard> encodes the 638c2ecf20Sopenharmony_ci * op code and erase sector size to be used by Sector Erase commands. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci u32 dword; 668c2ecf20Sopenharmony_ci u32 shift; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define SMPT_CMD_ADDRESS_LEN_MASK GENMASK(23, 22) 708c2ecf20Sopenharmony_ci#define SMPT_CMD_ADDRESS_LEN_0 (0x0UL << 22) 718c2ecf20Sopenharmony_ci#define SMPT_CMD_ADDRESS_LEN_3 (0x1UL << 22) 728c2ecf20Sopenharmony_ci#define SMPT_CMD_ADDRESS_LEN_4 (0x2UL << 22) 738c2ecf20Sopenharmony_ci#define SMPT_CMD_ADDRESS_LEN_USE_CURRENT (0x3UL << 22) 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define SMPT_CMD_READ_DUMMY_MASK GENMASK(19, 16) 768c2ecf20Sopenharmony_ci#define SMPT_CMD_READ_DUMMY_SHIFT 16 778c2ecf20Sopenharmony_ci#define SMPT_CMD_READ_DUMMY(_cmd) \ 788c2ecf20Sopenharmony_ci (((_cmd) & SMPT_CMD_READ_DUMMY_MASK) >> SMPT_CMD_READ_DUMMY_SHIFT) 798c2ecf20Sopenharmony_ci#define SMPT_CMD_READ_DUMMY_IS_VARIABLE 0xfUL 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define SMPT_CMD_READ_DATA_MASK GENMASK(31, 24) 828c2ecf20Sopenharmony_ci#define SMPT_CMD_READ_DATA_SHIFT 24 838c2ecf20Sopenharmony_ci#define SMPT_CMD_READ_DATA(_cmd) \ 848c2ecf20Sopenharmony_ci (((_cmd) & SMPT_CMD_READ_DATA_MASK) >> SMPT_CMD_READ_DATA_SHIFT) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define SMPT_CMD_OPCODE_MASK GENMASK(15, 8) 878c2ecf20Sopenharmony_ci#define SMPT_CMD_OPCODE_SHIFT 8 888c2ecf20Sopenharmony_ci#define SMPT_CMD_OPCODE(_cmd) \ 898c2ecf20Sopenharmony_ci (((_cmd) & SMPT_CMD_OPCODE_MASK) >> SMPT_CMD_OPCODE_SHIFT) 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define SMPT_MAP_REGION_COUNT_MASK GENMASK(23, 16) 928c2ecf20Sopenharmony_ci#define SMPT_MAP_REGION_COUNT_SHIFT 16 938c2ecf20Sopenharmony_ci#define SMPT_MAP_REGION_COUNT(_header) \ 948c2ecf20Sopenharmony_ci ((((_header) & SMPT_MAP_REGION_COUNT_MASK) >> \ 958c2ecf20Sopenharmony_ci SMPT_MAP_REGION_COUNT_SHIFT) + 1) 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define SMPT_MAP_ID_MASK GENMASK(15, 8) 988c2ecf20Sopenharmony_ci#define SMPT_MAP_ID_SHIFT 8 998c2ecf20Sopenharmony_ci#define SMPT_MAP_ID(_header) \ 1008c2ecf20Sopenharmony_ci (((_header) & SMPT_MAP_ID_MASK) >> SMPT_MAP_ID_SHIFT) 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#define SMPT_MAP_REGION_SIZE_MASK GENMASK(31, 8) 1038c2ecf20Sopenharmony_ci#define SMPT_MAP_REGION_SIZE_SHIFT 8 1048c2ecf20Sopenharmony_ci#define SMPT_MAP_REGION_SIZE(_region) \ 1058c2ecf20Sopenharmony_ci (((((_region) & SMPT_MAP_REGION_SIZE_MASK) >> \ 1068c2ecf20Sopenharmony_ci SMPT_MAP_REGION_SIZE_SHIFT) + 1) * 256) 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define SMPT_MAP_REGION_ERASE_TYPE_MASK GENMASK(3, 0) 1098c2ecf20Sopenharmony_ci#define SMPT_MAP_REGION_ERASE_TYPE(_region) \ 1108c2ecf20Sopenharmony_ci ((_region) & SMPT_MAP_REGION_ERASE_TYPE_MASK) 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#define SMPT_DESC_TYPE_MAP BIT(1) 1138c2ecf20Sopenharmony_ci#define SMPT_DESC_END BIT(0) 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#define SFDP_4BAIT_DWORD_MAX 2 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistruct sfdp_4bait { 1188c2ecf20Sopenharmony_ci /* The hardware capability. */ 1198c2ecf20Sopenharmony_ci u32 hwcaps; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* 1228c2ecf20Sopenharmony_ci * The <supported_bit> bit in DWORD1 of the 4BAIT tells us whether 1238c2ecf20Sopenharmony_ci * the associated 4-byte address op code is supported. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ci u32 supported_bit; 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/** 1298c2ecf20Sopenharmony_ci * spi_nor_read_raw() - raw read of serial flash memory. read_opcode, 1308c2ecf20Sopenharmony_ci * addr_width and read_dummy members of the struct spi_nor 1318c2ecf20Sopenharmony_ci * should be previously 1328c2ecf20Sopenharmony_ci * set. 1338c2ecf20Sopenharmony_ci * @nor: pointer to a 'struct spi_nor' 1348c2ecf20Sopenharmony_ci * @addr: offset in the serial flash memory 1358c2ecf20Sopenharmony_ci * @len: number of bytes to read 1368c2ecf20Sopenharmony_ci * @buf: buffer where the data is copied into (dma-safe memory) 1378c2ecf20Sopenharmony_ci * 1388c2ecf20Sopenharmony_ci * Return: 0 on success, -errno otherwise. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_cistatic int spi_nor_read_raw(struct spi_nor *nor, u32 addr, size_t len, u8 *buf) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci ssize_t ret; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci while (len) { 1458c2ecf20Sopenharmony_ci ret = spi_nor_read_data(nor, addr, len, buf); 1468c2ecf20Sopenharmony_ci if (ret < 0) 1478c2ecf20Sopenharmony_ci return ret; 1488c2ecf20Sopenharmony_ci if (!ret || ret > len) 1498c2ecf20Sopenharmony_ci return -EIO; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci buf += ret; 1528c2ecf20Sopenharmony_ci addr += ret; 1538c2ecf20Sopenharmony_ci len -= ret; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/** 1598c2ecf20Sopenharmony_ci * spi_nor_read_sfdp() - read Serial Flash Discoverable Parameters. 1608c2ecf20Sopenharmony_ci * @nor: pointer to a 'struct spi_nor' 1618c2ecf20Sopenharmony_ci * @addr: offset in the SFDP area to start reading data from 1628c2ecf20Sopenharmony_ci * @len: number of bytes to read 1638c2ecf20Sopenharmony_ci * @buf: buffer where the SFDP data are copied into (dma-safe memory) 1648c2ecf20Sopenharmony_ci * 1658c2ecf20Sopenharmony_ci * Whatever the actual numbers of bytes for address and dummy cycles are 1668c2ecf20Sopenharmony_ci * for (Fast) Read commands, the Read SFDP (5Ah) instruction is always 1678c2ecf20Sopenharmony_ci * followed by a 3-byte address and 8 dummy clock cycles. 1688c2ecf20Sopenharmony_ci * 1698c2ecf20Sopenharmony_ci * Return: 0 on success, -errno otherwise. 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_cistatic int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr, 1728c2ecf20Sopenharmony_ci size_t len, void *buf) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci u8 addr_width, read_opcode, read_dummy; 1758c2ecf20Sopenharmony_ci int ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci read_opcode = nor->read_opcode; 1788c2ecf20Sopenharmony_ci addr_width = nor->addr_width; 1798c2ecf20Sopenharmony_ci read_dummy = nor->read_dummy; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci nor->read_opcode = SPINOR_OP_RDSFDP; 1828c2ecf20Sopenharmony_ci nor->addr_width = 3; 1838c2ecf20Sopenharmony_ci nor->read_dummy = 8; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci ret = spi_nor_read_raw(nor, addr, len, buf); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci nor->read_opcode = read_opcode; 1888c2ecf20Sopenharmony_ci nor->addr_width = addr_width; 1898c2ecf20Sopenharmony_ci nor->read_dummy = read_dummy; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return ret; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/** 1958c2ecf20Sopenharmony_ci * spi_nor_read_sfdp_dma_unsafe() - read Serial Flash Discoverable Parameters. 1968c2ecf20Sopenharmony_ci * @nor: pointer to a 'struct spi_nor' 1978c2ecf20Sopenharmony_ci * @addr: offset in the SFDP area to start reading data from 1988c2ecf20Sopenharmony_ci * @len: number of bytes to read 1998c2ecf20Sopenharmony_ci * @buf: buffer where the SFDP data are copied into 2008c2ecf20Sopenharmony_ci * 2018c2ecf20Sopenharmony_ci * Wrap spi_nor_read_sfdp() using a kmalloc'ed bounce buffer as @buf is now not 2028c2ecf20Sopenharmony_ci * guaranteed to be dma-safe. 2038c2ecf20Sopenharmony_ci * 2048c2ecf20Sopenharmony_ci * Return: -ENOMEM if kmalloc() fails, the return code of spi_nor_read_sfdp() 2058c2ecf20Sopenharmony_ci * otherwise. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_cistatic int spi_nor_read_sfdp_dma_unsafe(struct spi_nor *nor, u32 addr, 2088c2ecf20Sopenharmony_ci size_t len, void *buf) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci void *dma_safe_buf; 2118c2ecf20Sopenharmony_ci int ret; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci dma_safe_buf = kmalloc(len, GFP_KERNEL); 2148c2ecf20Sopenharmony_ci if (!dma_safe_buf) 2158c2ecf20Sopenharmony_ci return -ENOMEM; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci ret = spi_nor_read_sfdp(nor, addr, len, dma_safe_buf); 2188c2ecf20Sopenharmony_ci memcpy(buf, dma_safe_buf, len); 2198c2ecf20Sopenharmony_ci kfree(dma_safe_buf); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return ret; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void 2258c2ecf20Sopenharmony_cispi_nor_set_read_settings_from_bfpt(struct spi_nor_read_command *read, 2268c2ecf20Sopenharmony_ci u16 half, 2278c2ecf20Sopenharmony_ci enum spi_nor_protocol proto) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci read->num_mode_clocks = (half >> 5) & 0x07; 2308c2ecf20Sopenharmony_ci read->num_wait_states = (half >> 0) & 0x1f; 2318c2ecf20Sopenharmony_ci read->opcode = (half >> 8) & 0xff; 2328c2ecf20Sopenharmony_ci read->proto = proto; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic const struct sfdp_bfpt_read sfdp_bfpt_reads[] = { 2368c2ecf20Sopenharmony_ci /* Fast Read 1-1-2 */ 2378c2ecf20Sopenharmony_ci { 2388c2ecf20Sopenharmony_ci SNOR_HWCAPS_READ_1_1_2, 2398c2ecf20Sopenharmony_ci BFPT_DWORD(1), BIT(16), /* Supported bit */ 2408c2ecf20Sopenharmony_ci BFPT_DWORD(4), 0, /* Settings */ 2418c2ecf20Sopenharmony_ci SNOR_PROTO_1_1_2, 2428c2ecf20Sopenharmony_ci }, 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* Fast Read 1-2-2 */ 2458c2ecf20Sopenharmony_ci { 2468c2ecf20Sopenharmony_ci SNOR_HWCAPS_READ_1_2_2, 2478c2ecf20Sopenharmony_ci BFPT_DWORD(1), BIT(20), /* Supported bit */ 2488c2ecf20Sopenharmony_ci BFPT_DWORD(4), 16, /* Settings */ 2498c2ecf20Sopenharmony_ci SNOR_PROTO_1_2_2, 2508c2ecf20Sopenharmony_ci }, 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* Fast Read 2-2-2 */ 2538c2ecf20Sopenharmony_ci { 2548c2ecf20Sopenharmony_ci SNOR_HWCAPS_READ_2_2_2, 2558c2ecf20Sopenharmony_ci BFPT_DWORD(5), BIT(0), /* Supported bit */ 2568c2ecf20Sopenharmony_ci BFPT_DWORD(6), 16, /* Settings */ 2578c2ecf20Sopenharmony_ci SNOR_PROTO_2_2_2, 2588c2ecf20Sopenharmony_ci }, 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* Fast Read 1-1-4 */ 2618c2ecf20Sopenharmony_ci { 2628c2ecf20Sopenharmony_ci SNOR_HWCAPS_READ_1_1_4, 2638c2ecf20Sopenharmony_ci BFPT_DWORD(1), BIT(22), /* Supported bit */ 2648c2ecf20Sopenharmony_ci BFPT_DWORD(3), 16, /* Settings */ 2658c2ecf20Sopenharmony_ci SNOR_PROTO_1_1_4, 2668c2ecf20Sopenharmony_ci }, 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* Fast Read 1-4-4 */ 2698c2ecf20Sopenharmony_ci { 2708c2ecf20Sopenharmony_ci SNOR_HWCAPS_READ_1_4_4, 2718c2ecf20Sopenharmony_ci BFPT_DWORD(1), BIT(21), /* Supported bit */ 2728c2ecf20Sopenharmony_ci BFPT_DWORD(3), 0, /* Settings */ 2738c2ecf20Sopenharmony_ci SNOR_PROTO_1_4_4, 2748c2ecf20Sopenharmony_ci }, 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Fast Read 4-4-4 */ 2778c2ecf20Sopenharmony_ci { 2788c2ecf20Sopenharmony_ci SNOR_HWCAPS_READ_4_4_4, 2798c2ecf20Sopenharmony_ci BFPT_DWORD(5), BIT(4), /* Supported bit */ 2808c2ecf20Sopenharmony_ci BFPT_DWORD(7), 16, /* Settings */ 2818c2ecf20Sopenharmony_ci SNOR_PROTO_4_4_4, 2828c2ecf20Sopenharmony_ci }, 2838c2ecf20Sopenharmony_ci}; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic const struct sfdp_bfpt_erase sfdp_bfpt_erases[] = { 2868c2ecf20Sopenharmony_ci /* Erase Type 1 in DWORD8 bits[15:0] */ 2878c2ecf20Sopenharmony_ci {BFPT_DWORD(8), 0}, 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* Erase Type 2 in DWORD8 bits[31:16] */ 2908c2ecf20Sopenharmony_ci {BFPT_DWORD(8), 16}, 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* Erase Type 3 in DWORD9 bits[15:0] */ 2938c2ecf20Sopenharmony_ci {BFPT_DWORD(9), 0}, 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* Erase Type 4 in DWORD9 bits[31:16] */ 2968c2ecf20Sopenharmony_ci {BFPT_DWORD(9), 16}, 2978c2ecf20Sopenharmony_ci}; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/** 3008c2ecf20Sopenharmony_ci * spi_nor_set_erase_settings_from_bfpt() - set erase type settings from BFPT 3018c2ecf20Sopenharmony_ci * @erase: pointer to a structure that describes a SPI NOR erase type 3028c2ecf20Sopenharmony_ci * @size: the size of the sector/block erased by the erase type 3038c2ecf20Sopenharmony_ci * @opcode: the SPI command op code to erase the sector/block 3048c2ecf20Sopenharmony_ci * @i: erase type index as sorted in the Basic Flash Parameter Table 3058c2ecf20Sopenharmony_ci * 3068c2ecf20Sopenharmony_ci * The supported Erase Types will be sorted at init in ascending order, with 3078c2ecf20Sopenharmony_ci * the smallest Erase Type size being the first member in the erase_type array 3088c2ecf20Sopenharmony_ci * of the spi_nor_erase_map structure. Save the Erase Type index as sorted in 3098c2ecf20Sopenharmony_ci * the Basic Flash Parameter Table since it will be used later on to 3108c2ecf20Sopenharmony_ci * synchronize with the supported Erase Types defined in SFDP optional tables. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_cistatic void 3138c2ecf20Sopenharmony_cispi_nor_set_erase_settings_from_bfpt(struct spi_nor_erase_type *erase, 3148c2ecf20Sopenharmony_ci u32 size, u8 opcode, u8 i) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci erase->idx = i; 3178c2ecf20Sopenharmony_ci spi_nor_set_erase_type(erase, size, opcode); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci/** 3218c2ecf20Sopenharmony_ci * spi_nor_map_cmp_erase_type() - compare the map's erase types by size 3228c2ecf20Sopenharmony_ci * @l: member in the left half of the map's erase_type array 3238c2ecf20Sopenharmony_ci * @r: member in the right half of the map's erase_type array 3248c2ecf20Sopenharmony_ci * 3258c2ecf20Sopenharmony_ci * Comparison function used in the sort() call to sort in ascending order the 3268c2ecf20Sopenharmony_ci * map's erase types, the smallest erase type size being the first member in the 3278c2ecf20Sopenharmony_ci * sorted erase_type array. 3288c2ecf20Sopenharmony_ci * 3298c2ecf20Sopenharmony_ci * Return: the result of @l->size - @r->size 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_cistatic int spi_nor_map_cmp_erase_type(const void *l, const void *r) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci const struct spi_nor_erase_type *left = l, *right = r; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return left->size - right->size; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci/** 3398c2ecf20Sopenharmony_ci * spi_nor_sort_erase_mask() - sort erase mask 3408c2ecf20Sopenharmony_ci * @map: the erase map of the SPI NOR 3418c2ecf20Sopenharmony_ci * @erase_mask: the erase type mask to be sorted 3428c2ecf20Sopenharmony_ci * 3438c2ecf20Sopenharmony_ci * Replicate the sort done for the map's erase types in BFPT: sort the erase 3448c2ecf20Sopenharmony_ci * mask in ascending order with the smallest erase type size starting from 3458c2ecf20Sopenharmony_ci * BIT(0) in the sorted erase mask. 3468c2ecf20Sopenharmony_ci * 3478c2ecf20Sopenharmony_ci * Return: sorted erase mask. 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_cistatic u8 spi_nor_sort_erase_mask(struct spi_nor_erase_map *map, u8 erase_mask) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct spi_nor_erase_type *erase_type = map->erase_type; 3528c2ecf20Sopenharmony_ci int i; 3538c2ecf20Sopenharmony_ci u8 sorted_erase_mask = 0; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (!erase_mask) 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* Replicate the sort done for the map's erase types. */ 3598c2ecf20Sopenharmony_ci for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) 3608c2ecf20Sopenharmony_ci if (erase_type[i].size && erase_mask & BIT(erase_type[i].idx)) 3618c2ecf20Sopenharmony_ci sorted_erase_mask |= BIT(i); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return sorted_erase_mask; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci/** 3678c2ecf20Sopenharmony_ci * spi_nor_regions_sort_erase_types() - sort erase types in each region 3688c2ecf20Sopenharmony_ci * @map: the erase map of the SPI NOR 3698c2ecf20Sopenharmony_ci * 3708c2ecf20Sopenharmony_ci * Function assumes that the erase types defined in the erase map are already 3718c2ecf20Sopenharmony_ci * sorted in ascending order, with the smallest erase type size being the first 3728c2ecf20Sopenharmony_ci * member in the erase_type array. It replicates the sort done for the map's 3738c2ecf20Sopenharmony_ci * erase types. Each region's erase bitmask will indicate which erase types are 3748c2ecf20Sopenharmony_ci * supported from the sorted erase types defined in the erase map. 3758c2ecf20Sopenharmony_ci * Sort the all region's erase type at init in order to speed up the process of 3768c2ecf20Sopenharmony_ci * finding the best erase command at runtime. 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_cistatic void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct spi_nor_erase_region *region = map->regions; 3818c2ecf20Sopenharmony_ci u8 region_erase_mask, sorted_erase_mask; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci while (region) { 3848c2ecf20Sopenharmony_ci region_erase_mask = region->offset & SNOR_ERASE_TYPE_MASK; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci sorted_erase_mask = spi_nor_sort_erase_mask(map, 3878c2ecf20Sopenharmony_ci region_erase_mask); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* Overwrite erase mask. */ 3908c2ecf20Sopenharmony_ci region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK) | 3918c2ecf20Sopenharmony_ci sorted_erase_mask; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci region = spi_nor_region_next(region); 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci/** 3988c2ecf20Sopenharmony_ci * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table. 3998c2ecf20Sopenharmony_ci * @nor: pointer to a 'struct spi_nor' 4008c2ecf20Sopenharmony_ci * @bfpt_header: pointer to the 'struct sfdp_parameter_header' describing 4018c2ecf20Sopenharmony_ci * the Basic Flash Parameter Table length and version 4028c2ecf20Sopenharmony_ci * @params: pointer to the 'struct spi_nor_flash_parameter' to be 4038c2ecf20Sopenharmony_ci * filled 4048c2ecf20Sopenharmony_ci * 4058c2ecf20Sopenharmony_ci * The Basic Flash Parameter Table is the main and only mandatory table as 4068c2ecf20Sopenharmony_ci * defined by the SFDP (JESD216) specification. 4078c2ecf20Sopenharmony_ci * It provides us with the total size (memory density) of the data array and 4088c2ecf20Sopenharmony_ci * the number of address bytes for Fast Read, Page Program and Sector Erase 4098c2ecf20Sopenharmony_ci * commands. 4108c2ecf20Sopenharmony_ci * For Fast READ commands, it also gives the number of mode clock cycles and 4118c2ecf20Sopenharmony_ci * wait states (regrouped in the number of dummy clock cycles) for each 4128c2ecf20Sopenharmony_ci * supported instruction op code. 4138c2ecf20Sopenharmony_ci * For Page Program, the page size is now available since JESD216 rev A, however 4148c2ecf20Sopenharmony_ci * the supported instruction op codes are still not provided. 4158c2ecf20Sopenharmony_ci * For Sector Erase commands, this table stores the supported instruction op 4168c2ecf20Sopenharmony_ci * codes and the associated sector sizes. 4178c2ecf20Sopenharmony_ci * Finally, the Quad Enable Requirements (QER) are also available since JESD216 4188c2ecf20Sopenharmony_ci * rev A. The QER bits encode the manufacturer dependent procedure to be 4198c2ecf20Sopenharmony_ci * executed to set the Quad Enable (QE) bit in some internal register of the 4208c2ecf20Sopenharmony_ci * Quad SPI memory. Indeed the QE bit, when it exists, must be set before 4218c2ecf20Sopenharmony_ci * sending any Quad SPI command to the memory. Actually, setting the QE bit 4228c2ecf20Sopenharmony_ci * tells the memory to reassign its WP# and HOLD#/RESET# pins to functions IO2 4238c2ecf20Sopenharmony_ci * and IO3 hence enabling 4 (Quad) I/O lines. 4248c2ecf20Sopenharmony_ci * 4258c2ecf20Sopenharmony_ci * Return: 0 on success, -errno otherwise. 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_cistatic int spi_nor_parse_bfpt(struct spi_nor *nor, 4288c2ecf20Sopenharmony_ci const struct sfdp_parameter_header *bfpt_header, 4298c2ecf20Sopenharmony_ci struct spi_nor_flash_parameter *params) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci struct spi_nor_erase_map *map = ¶ms->erase_map; 4328c2ecf20Sopenharmony_ci struct spi_nor_erase_type *erase_type = map->erase_type; 4338c2ecf20Sopenharmony_ci struct sfdp_bfpt bfpt; 4348c2ecf20Sopenharmony_ci size_t len; 4358c2ecf20Sopenharmony_ci int i, cmd, err; 4368c2ecf20Sopenharmony_ci u32 addr, val; 4378c2ecf20Sopenharmony_ci u16 half; 4388c2ecf20Sopenharmony_ci u8 erase_mask; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */ 4418c2ecf20Sopenharmony_ci if (bfpt_header->length < BFPT_DWORD_MAX_JESD216) 4428c2ecf20Sopenharmony_ci return -EINVAL; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* Read the Basic Flash Parameter Table. */ 4458c2ecf20Sopenharmony_ci len = min_t(size_t, sizeof(bfpt), 4468c2ecf20Sopenharmony_ci bfpt_header->length * sizeof(u32)); 4478c2ecf20Sopenharmony_ci addr = SFDP_PARAM_HEADER_PTP(bfpt_header); 4488c2ecf20Sopenharmony_ci memset(&bfpt, 0, sizeof(bfpt)); 4498c2ecf20Sopenharmony_ci err = spi_nor_read_sfdp_dma_unsafe(nor, addr, len, &bfpt); 4508c2ecf20Sopenharmony_ci if (err < 0) 4518c2ecf20Sopenharmony_ci return err; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* Fix endianness of the BFPT DWORDs. */ 4548c2ecf20Sopenharmony_ci le32_to_cpu_array(bfpt.dwords, BFPT_DWORD_MAX); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* Number of address bytes. */ 4578c2ecf20Sopenharmony_ci switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) { 4588c2ecf20Sopenharmony_ci case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY: 4598c2ecf20Sopenharmony_ci case BFPT_DWORD1_ADDRESS_BYTES_3_OR_4: 4608c2ecf20Sopenharmony_ci nor->addr_width = 3; 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY: 4648c2ecf20Sopenharmony_ci nor->addr_width = 4; 4658c2ecf20Sopenharmony_ci break; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci default: 4688c2ecf20Sopenharmony_ci break; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* Flash Memory Density (in bits). */ 4728c2ecf20Sopenharmony_ci val = bfpt.dwords[BFPT_DWORD(2)]; 4738c2ecf20Sopenharmony_ci if (val & BIT(31)) { 4748c2ecf20Sopenharmony_ci val &= ~BIT(31); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* 4778c2ecf20Sopenharmony_ci * Prevent overflows on params->size. Anyway, a NOR of 2^64 4788c2ecf20Sopenharmony_ci * bits is unlikely to exist so this error probably means 4798c2ecf20Sopenharmony_ci * the BFPT we are reading is corrupted/wrong. 4808c2ecf20Sopenharmony_ci */ 4818c2ecf20Sopenharmony_ci if (val > 63) 4828c2ecf20Sopenharmony_ci return -EINVAL; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci params->size = 1ULL << val; 4858c2ecf20Sopenharmony_ci } else { 4868c2ecf20Sopenharmony_ci params->size = val + 1; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci params->size >>= 3; /* Convert to bytes. */ 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* Fast Read settings. */ 4918c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_reads); i++) { 4928c2ecf20Sopenharmony_ci const struct sfdp_bfpt_read *rd = &sfdp_bfpt_reads[i]; 4938c2ecf20Sopenharmony_ci struct spi_nor_read_command *read; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (!(bfpt.dwords[rd->supported_dword] & rd->supported_bit)) { 4968c2ecf20Sopenharmony_ci params->hwcaps.mask &= ~rd->hwcaps; 4978c2ecf20Sopenharmony_ci continue; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci params->hwcaps.mask |= rd->hwcaps; 5018c2ecf20Sopenharmony_ci cmd = spi_nor_hwcaps_read2cmd(rd->hwcaps); 5028c2ecf20Sopenharmony_ci read = ¶ms->reads[cmd]; 5038c2ecf20Sopenharmony_ci half = bfpt.dwords[rd->settings_dword] >> rd->settings_shift; 5048c2ecf20Sopenharmony_ci spi_nor_set_read_settings_from_bfpt(read, half, rd->proto); 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* 5088c2ecf20Sopenharmony_ci * Sector Erase settings. Reinitialize the uniform erase map using the 5098c2ecf20Sopenharmony_ci * Erase Types defined in the bfpt table. 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_ci erase_mask = 0; 5128c2ecf20Sopenharmony_ci memset(¶ms->erase_map, 0, sizeof(params->erase_map)); 5138c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) { 5148c2ecf20Sopenharmony_ci const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i]; 5158c2ecf20Sopenharmony_ci u32 erasesize; 5168c2ecf20Sopenharmony_ci u8 opcode; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci half = bfpt.dwords[er->dword] >> er->shift; 5198c2ecf20Sopenharmony_ci erasesize = half & 0xff; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* erasesize == 0 means this Erase Type is not supported. */ 5228c2ecf20Sopenharmony_ci if (!erasesize) 5238c2ecf20Sopenharmony_ci continue; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci erasesize = 1U << erasesize; 5268c2ecf20Sopenharmony_ci opcode = (half >> 8) & 0xff; 5278c2ecf20Sopenharmony_ci erase_mask |= BIT(i); 5288c2ecf20Sopenharmony_ci spi_nor_set_erase_settings_from_bfpt(&erase_type[i], erasesize, 5298c2ecf20Sopenharmony_ci opcode, i); 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci spi_nor_init_uniform_erase_map(map, erase_mask, params->size); 5328c2ecf20Sopenharmony_ci /* 5338c2ecf20Sopenharmony_ci * Sort all the map's Erase Types in ascending order with the smallest 5348c2ecf20Sopenharmony_ci * erase size being the first member in the erase_type array. 5358c2ecf20Sopenharmony_ci */ 5368c2ecf20Sopenharmony_ci sort(erase_type, SNOR_ERASE_TYPE_MAX, sizeof(erase_type[0]), 5378c2ecf20Sopenharmony_ci spi_nor_map_cmp_erase_type, NULL); 5388c2ecf20Sopenharmony_ci /* 5398c2ecf20Sopenharmony_ci * Sort the erase types in the uniform region in order to update the 5408c2ecf20Sopenharmony_ci * uniform_erase_type bitmask. The bitmask will be used later on when 5418c2ecf20Sopenharmony_ci * selecting the uniform erase. 5428c2ecf20Sopenharmony_ci */ 5438c2ecf20Sopenharmony_ci spi_nor_regions_sort_erase_types(map); 5448c2ecf20Sopenharmony_ci map->uniform_erase_type = map->uniform_region.offset & 5458c2ecf20Sopenharmony_ci SNOR_ERASE_TYPE_MASK; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* Stop here if not JESD216 rev A or later. */ 5488c2ecf20Sopenharmony_ci if (bfpt_header->length == BFPT_DWORD_MAX_JESD216) 5498c2ecf20Sopenharmony_ci return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, 5508c2ecf20Sopenharmony_ci params); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* Page size: this field specifies 'N' so the page size = 2^N bytes. */ 5538c2ecf20Sopenharmony_ci val = bfpt.dwords[BFPT_DWORD(11)]; 5548c2ecf20Sopenharmony_ci val &= BFPT_DWORD11_PAGE_SIZE_MASK; 5558c2ecf20Sopenharmony_ci val >>= BFPT_DWORD11_PAGE_SIZE_SHIFT; 5568c2ecf20Sopenharmony_ci params->page_size = 1U << val; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* Quad Enable Requirements. */ 5598c2ecf20Sopenharmony_ci switch (bfpt.dwords[BFPT_DWORD(15)] & BFPT_DWORD15_QER_MASK) { 5608c2ecf20Sopenharmony_ci case BFPT_DWORD15_QER_NONE: 5618c2ecf20Sopenharmony_ci params->quad_enable = NULL; 5628c2ecf20Sopenharmony_ci break; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci case BFPT_DWORD15_QER_SR2_BIT1_BUGGY: 5658c2ecf20Sopenharmony_ci /* 5668c2ecf20Sopenharmony_ci * Writing only one byte to the Status Register has the 5678c2ecf20Sopenharmony_ci * side-effect of clearing Status Register 2. 5688c2ecf20Sopenharmony_ci */ 5698c2ecf20Sopenharmony_ci case BFPT_DWORD15_QER_SR2_BIT1_NO_RD: 5708c2ecf20Sopenharmony_ci /* 5718c2ecf20Sopenharmony_ci * Read Configuration Register (35h) instruction is not 5728c2ecf20Sopenharmony_ci * supported. 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_ci nor->flags |= SNOR_F_HAS_16BIT_SR | SNOR_F_NO_READ_CR; 5758c2ecf20Sopenharmony_ci params->quad_enable = spi_nor_sr2_bit1_quad_enable; 5768c2ecf20Sopenharmony_ci break; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci case BFPT_DWORD15_QER_SR1_BIT6: 5798c2ecf20Sopenharmony_ci nor->flags &= ~SNOR_F_HAS_16BIT_SR; 5808c2ecf20Sopenharmony_ci params->quad_enable = spi_nor_sr1_bit6_quad_enable; 5818c2ecf20Sopenharmony_ci break; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci case BFPT_DWORD15_QER_SR2_BIT7: 5848c2ecf20Sopenharmony_ci nor->flags &= ~SNOR_F_HAS_16BIT_SR; 5858c2ecf20Sopenharmony_ci params->quad_enable = spi_nor_sr2_bit7_quad_enable; 5868c2ecf20Sopenharmony_ci break; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci case BFPT_DWORD15_QER_SR2_BIT1: 5898c2ecf20Sopenharmony_ci /* 5908c2ecf20Sopenharmony_ci * JESD216 rev B or later does not specify if writing only one 5918c2ecf20Sopenharmony_ci * byte to the Status Register clears or not the Status 5928c2ecf20Sopenharmony_ci * Register 2, so let's be cautious and keep the default 5938c2ecf20Sopenharmony_ci * assumption of a 16-bit Write Status (01h) command. 5948c2ecf20Sopenharmony_ci */ 5958c2ecf20Sopenharmony_ci nor->flags |= SNOR_F_HAS_16BIT_SR; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci params->quad_enable = spi_nor_sr2_bit1_quad_enable; 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci default: 6018c2ecf20Sopenharmony_ci dev_dbg(nor->dev, "BFPT QER reserved value used\n"); 6028c2ecf20Sopenharmony_ci break; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* Stop here if not JESD216 rev C or later. */ 6068c2ecf20Sopenharmony_ci if (bfpt_header->length == BFPT_DWORD_MAX_JESD216B) 6078c2ecf20Sopenharmony_ci return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, 6088c2ecf20Sopenharmony_ci params); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, params); 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci/** 6148c2ecf20Sopenharmony_ci * spi_nor_smpt_addr_width() - return the address width used in the 6158c2ecf20Sopenharmony_ci * configuration detection command. 6168c2ecf20Sopenharmony_ci * @nor: pointer to a 'struct spi_nor' 6178c2ecf20Sopenharmony_ci * @settings: configuration detection command descriptor, dword1 6188c2ecf20Sopenharmony_ci */ 6198c2ecf20Sopenharmony_cistatic u8 spi_nor_smpt_addr_width(const struct spi_nor *nor, const u32 settings) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci switch (settings & SMPT_CMD_ADDRESS_LEN_MASK) { 6228c2ecf20Sopenharmony_ci case SMPT_CMD_ADDRESS_LEN_0: 6238c2ecf20Sopenharmony_ci return 0; 6248c2ecf20Sopenharmony_ci case SMPT_CMD_ADDRESS_LEN_3: 6258c2ecf20Sopenharmony_ci return 3; 6268c2ecf20Sopenharmony_ci case SMPT_CMD_ADDRESS_LEN_4: 6278c2ecf20Sopenharmony_ci return 4; 6288c2ecf20Sopenharmony_ci case SMPT_CMD_ADDRESS_LEN_USE_CURRENT: 6298c2ecf20Sopenharmony_ci default: 6308c2ecf20Sopenharmony_ci return nor->addr_width; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci/** 6358c2ecf20Sopenharmony_ci * spi_nor_smpt_read_dummy() - return the configuration detection command read 6368c2ecf20Sopenharmony_ci * latency, in clock cycles. 6378c2ecf20Sopenharmony_ci * @nor: pointer to a 'struct spi_nor' 6388c2ecf20Sopenharmony_ci * @settings: configuration detection command descriptor, dword1 6398c2ecf20Sopenharmony_ci * 6408c2ecf20Sopenharmony_ci * Return: the number of dummy cycles for an SMPT read 6418c2ecf20Sopenharmony_ci */ 6428c2ecf20Sopenharmony_cistatic u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci u8 read_dummy = SMPT_CMD_READ_DUMMY(settings); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (read_dummy == SMPT_CMD_READ_DUMMY_IS_VARIABLE) 6478c2ecf20Sopenharmony_ci return nor->read_dummy; 6488c2ecf20Sopenharmony_ci return read_dummy; 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci/** 6528c2ecf20Sopenharmony_ci * spi_nor_get_map_in_use() - get the configuration map in use 6538c2ecf20Sopenharmony_ci * @nor: pointer to a 'struct spi_nor' 6548c2ecf20Sopenharmony_ci * @smpt: pointer to the sector map parameter table 6558c2ecf20Sopenharmony_ci * @smpt_len: sector map parameter table length 6568c2ecf20Sopenharmony_ci * 6578c2ecf20Sopenharmony_ci * Return: pointer to the map in use, ERR_PTR(-errno) otherwise. 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_cistatic const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, 6608c2ecf20Sopenharmony_ci u8 smpt_len) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci const u32 *ret; 6638c2ecf20Sopenharmony_ci u8 *buf; 6648c2ecf20Sopenharmony_ci u32 addr; 6658c2ecf20Sopenharmony_ci int err; 6668c2ecf20Sopenharmony_ci u8 i; 6678c2ecf20Sopenharmony_ci u8 addr_width, read_opcode, read_dummy; 6688c2ecf20Sopenharmony_ci u8 read_data_mask, map_id; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */ 6718c2ecf20Sopenharmony_ci buf = kmalloc(sizeof(*buf), GFP_KERNEL); 6728c2ecf20Sopenharmony_ci if (!buf) 6738c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci addr_width = nor->addr_width; 6768c2ecf20Sopenharmony_ci read_dummy = nor->read_dummy; 6778c2ecf20Sopenharmony_ci read_opcode = nor->read_opcode; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci map_id = 0; 6808c2ecf20Sopenharmony_ci /* Determine if there are any optional Detection Command Descriptors */ 6818c2ecf20Sopenharmony_ci for (i = 0; i < smpt_len; i += 2) { 6828c2ecf20Sopenharmony_ci if (smpt[i] & SMPT_DESC_TYPE_MAP) 6838c2ecf20Sopenharmony_ci break; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci read_data_mask = SMPT_CMD_READ_DATA(smpt[i]); 6868c2ecf20Sopenharmony_ci nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]); 6878c2ecf20Sopenharmony_ci nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]); 6888c2ecf20Sopenharmony_ci nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]); 6898c2ecf20Sopenharmony_ci addr = smpt[i + 1]; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci err = spi_nor_read_raw(nor, addr, 1, buf); 6928c2ecf20Sopenharmony_ci if (err) { 6938c2ecf20Sopenharmony_ci ret = ERR_PTR(err); 6948c2ecf20Sopenharmony_ci goto out; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* 6988c2ecf20Sopenharmony_ci * Build an index value that is used to select the Sector Map 6998c2ecf20Sopenharmony_ci * Configuration that is currently in use. 7008c2ecf20Sopenharmony_ci */ 7018c2ecf20Sopenharmony_ci map_id = map_id << 1 | !!(*buf & read_data_mask); 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci /* 7058c2ecf20Sopenharmony_ci * If command descriptors are provided, they always precede map 7068c2ecf20Sopenharmony_ci * descriptors in the table. There is no need to start the iteration 7078c2ecf20Sopenharmony_ci * over smpt array all over again. 7088c2ecf20Sopenharmony_ci * 7098c2ecf20Sopenharmony_ci * Find the matching configuration map. 7108c2ecf20Sopenharmony_ci */ 7118c2ecf20Sopenharmony_ci ret = ERR_PTR(-EINVAL); 7128c2ecf20Sopenharmony_ci while (i < smpt_len) { 7138c2ecf20Sopenharmony_ci if (SMPT_MAP_ID(smpt[i]) == map_id) { 7148c2ecf20Sopenharmony_ci ret = smpt + i; 7158c2ecf20Sopenharmony_ci break; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* 7198c2ecf20Sopenharmony_ci * If there are no more configuration map descriptors and no 7208c2ecf20Sopenharmony_ci * configuration ID matched the configuration identifier, the 7218c2ecf20Sopenharmony_ci * sector address map is unknown. 7228c2ecf20Sopenharmony_ci */ 7238c2ecf20Sopenharmony_ci if (smpt[i] & SMPT_DESC_END) 7248c2ecf20Sopenharmony_ci break; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci /* increment the table index to the next map */ 7278c2ecf20Sopenharmony_ci i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* fall through */ 7318c2ecf20Sopenharmony_ciout: 7328c2ecf20Sopenharmony_ci kfree(buf); 7338c2ecf20Sopenharmony_ci nor->addr_width = addr_width; 7348c2ecf20Sopenharmony_ci nor->read_dummy = read_dummy; 7358c2ecf20Sopenharmony_ci nor->read_opcode = read_opcode; 7368c2ecf20Sopenharmony_ci return ret; 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic void spi_nor_region_mark_end(struct spi_nor_erase_region *region) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci region->offset |= SNOR_LAST_REGION; 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_cistatic void spi_nor_region_mark_overlay(struct spi_nor_erase_region *region) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci region->offset |= SNOR_OVERLAID_REGION; 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci/** 7508c2ecf20Sopenharmony_ci * spi_nor_region_check_overlay() - set overlay bit when the region is overlaid 7518c2ecf20Sopenharmony_ci * @region: pointer to a structure that describes a SPI NOR erase region 7528c2ecf20Sopenharmony_ci * @erase: pointer to a structure that describes a SPI NOR erase type 7538c2ecf20Sopenharmony_ci * @erase_type: erase type bitmask 7548c2ecf20Sopenharmony_ci */ 7558c2ecf20Sopenharmony_cistatic void 7568c2ecf20Sopenharmony_cispi_nor_region_check_overlay(struct spi_nor_erase_region *region, 7578c2ecf20Sopenharmony_ci const struct spi_nor_erase_type *erase, 7588c2ecf20Sopenharmony_ci const u8 erase_type) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci int i; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { 7638c2ecf20Sopenharmony_ci if (!(erase[i].size && erase_type & BIT(erase[i].idx))) 7648c2ecf20Sopenharmony_ci continue; 7658c2ecf20Sopenharmony_ci if (region->size & erase[i].size_mask) { 7668c2ecf20Sopenharmony_ci spi_nor_region_mark_overlay(region); 7678c2ecf20Sopenharmony_ci return; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci/** 7738c2ecf20Sopenharmony_ci * spi_nor_init_non_uniform_erase_map() - initialize the non-uniform erase map 7748c2ecf20Sopenharmony_ci * @nor: pointer to a 'struct spi_nor' 7758c2ecf20Sopenharmony_ci * @params: pointer to a duplicate 'struct spi_nor_flash_parameter' that is 7768c2ecf20Sopenharmony_ci * used for storing SFDP parsed data 7778c2ecf20Sopenharmony_ci * @smpt: pointer to the sector map parameter table 7788c2ecf20Sopenharmony_ci * 7798c2ecf20Sopenharmony_ci * Return: 0 on success, -errno otherwise. 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_cistatic int 7828c2ecf20Sopenharmony_cispi_nor_init_non_uniform_erase_map(struct spi_nor *nor, 7838c2ecf20Sopenharmony_ci struct spi_nor_flash_parameter *params, 7848c2ecf20Sopenharmony_ci const u32 *smpt) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci struct spi_nor_erase_map *map = ¶ms->erase_map; 7878c2ecf20Sopenharmony_ci struct spi_nor_erase_type *erase = map->erase_type; 7888c2ecf20Sopenharmony_ci struct spi_nor_erase_region *region; 7898c2ecf20Sopenharmony_ci u64 offset; 7908c2ecf20Sopenharmony_ci u32 region_count; 7918c2ecf20Sopenharmony_ci int i, j; 7928c2ecf20Sopenharmony_ci u8 uniform_erase_type, save_uniform_erase_type; 7938c2ecf20Sopenharmony_ci u8 erase_type, regions_erase_type; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci region_count = SMPT_MAP_REGION_COUNT(*smpt); 7968c2ecf20Sopenharmony_ci /* 7978c2ecf20Sopenharmony_ci * The regions will be freed when the driver detaches from the 7988c2ecf20Sopenharmony_ci * device. 7998c2ecf20Sopenharmony_ci */ 8008c2ecf20Sopenharmony_ci region = devm_kcalloc(nor->dev, region_count, sizeof(*region), 8018c2ecf20Sopenharmony_ci GFP_KERNEL); 8028c2ecf20Sopenharmony_ci if (!region) 8038c2ecf20Sopenharmony_ci return -ENOMEM; 8048c2ecf20Sopenharmony_ci map->regions = region; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci uniform_erase_type = 0xff; 8078c2ecf20Sopenharmony_ci regions_erase_type = 0; 8088c2ecf20Sopenharmony_ci offset = 0; 8098c2ecf20Sopenharmony_ci /* Populate regions. */ 8108c2ecf20Sopenharmony_ci for (i = 0; i < region_count; i++) { 8118c2ecf20Sopenharmony_ci j = i + 1; /* index for the region dword */ 8128c2ecf20Sopenharmony_ci region[i].size = SMPT_MAP_REGION_SIZE(smpt[j]); 8138c2ecf20Sopenharmony_ci erase_type = SMPT_MAP_REGION_ERASE_TYPE(smpt[j]); 8148c2ecf20Sopenharmony_ci region[i].offset = offset | erase_type; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci spi_nor_region_check_overlay(®ion[i], erase, erase_type); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* 8198c2ecf20Sopenharmony_ci * Save the erase types that are supported in all regions and 8208c2ecf20Sopenharmony_ci * can erase the entire flash memory. 8218c2ecf20Sopenharmony_ci */ 8228c2ecf20Sopenharmony_ci uniform_erase_type &= erase_type; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci /* 8258c2ecf20Sopenharmony_ci * regions_erase_type mask will indicate all the erase types 8268c2ecf20Sopenharmony_ci * supported in this configuration map. 8278c2ecf20Sopenharmony_ci */ 8288c2ecf20Sopenharmony_ci regions_erase_type |= erase_type; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci offset = (region[i].offset & ~SNOR_ERASE_FLAGS_MASK) + 8318c2ecf20Sopenharmony_ci region[i].size; 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci spi_nor_region_mark_end(®ion[i - 1]); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci save_uniform_erase_type = map->uniform_erase_type; 8368c2ecf20Sopenharmony_ci map->uniform_erase_type = spi_nor_sort_erase_mask(map, 8378c2ecf20Sopenharmony_ci uniform_erase_type); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (!regions_erase_type) { 8408c2ecf20Sopenharmony_ci /* 8418c2ecf20Sopenharmony_ci * Roll back to the previous uniform_erase_type mask, SMPT is 8428c2ecf20Sopenharmony_ci * broken. 8438c2ecf20Sopenharmony_ci */ 8448c2ecf20Sopenharmony_ci map->uniform_erase_type = save_uniform_erase_type; 8458c2ecf20Sopenharmony_ci return -EINVAL; 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* 8498c2ecf20Sopenharmony_ci * BFPT advertises all the erase types supported by all the possible 8508c2ecf20Sopenharmony_ci * map configurations. Mask out the erase types that are not supported 8518c2ecf20Sopenharmony_ci * by the current map configuration. 8528c2ecf20Sopenharmony_ci */ 8538c2ecf20Sopenharmony_ci for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) 8548c2ecf20Sopenharmony_ci if (!(regions_erase_type & BIT(erase[i].idx))) 8558c2ecf20Sopenharmony_ci spi_nor_mask_erase_type(&erase[i]); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci return 0; 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci/** 8618c2ecf20Sopenharmony_ci * spi_nor_parse_smpt() - parse Sector Map Parameter Table 8628c2ecf20Sopenharmony_ci * @nor: pointer to a 'struct spi_nor' 8638c2ecf20Sopenharmony_ci * @smpt_header: sector map parameter table header 8648c2ecf20Sopenharmony_ci * @params: pointer to a duplicate 'struct spi_nor_flash_parameter' 8658c2ecf20Sopenharmony_ci * that is used for storing SFDP parsed data 8668c2ecf20Sopenharmony_ci * 8678c2ecf20Sopenharmony_ci * This table is optional, but when available, we parse it to identify the 8688c2ecf20Sopenharmony_ci * location and size of sectors within the main data array of the flash memory 8698c2ecf20Sopenharmony_ci * device and to identify which Erase Types are supported by each sector. 8708c2ecf20Sopenharmony_ci * 8718c2ecf20Sopenharmony_ci * Return: 0 on success, -errno otherwise. 8728c2ecf20Sopenharmony_ci */ 8738c2ecf20Sopenharmony_cistatic int spi_nor_parse_smpt(struct spi_nor *nor, 8748c2ecf20Sopenharmony_ci const struct sfdp_parameter_header *smpt_header, 8758c2ecf20Sopenharmony_ci struct spi_nor_flash_parameter *params) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci const u32 *sector_map; 8788c2ecf20Sopenharmony_ci u32 *smpt; 8798c2ecf20Sopenharmony_ci size_t len; 8808c2ecf20Sopenharmony_ci u32 addr; 8818c2ecf20Sopenharmony_ci int ret; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci /* Read the Sector Map Parameter Table. */ 8848c2ecf20Sopenharmony_ci len = smpt_header->length * sizeof(*smpt); 8858c2ecf20Sopenharmony_ci smpt = kmalloc(len, GFP_KERNEL); 8868c2ecf20Sopenharmony_ci if (!smpt) 8878c2ecf20Sopenharmony_ci return -ENOMEM; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci addr = SFDP_PARAM_HEADER_PTP(smpt_header); 8908c2ecf20Sopenharmony_ci ret = spi_nor_read_sfdp(nor, addr, len, smpt); 8918c2ecf20Sopenharmony_ci if (ret) 8928c2ecf20Sopenharmony_ci goto out; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci /* Fix endianness of the SMPT DWORDs. */ 8958c2ecf20Sopenharmony_ci le32_to_cpu_array(smpt, smpt_header->length); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci sector_map = spi_nor_get_map_in_use(nor, smpt, smpt_header->length); 8988c2ecf20Sopenharmony_ci if (IS_ERR(sector_map)) { 8998c2ecf20Sopenharmony_ci ret = PTR_ERR(sector_map); 9008c2ecf20Sopenharmony_ci goto out; 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci ret = spi_nor_init_non_uniform_erase_map(nor, params, sector_map); 9048c2ecf20Sopenharmony_ci if (ret) 9058c2ecf20Sopenharmony_ci goto out; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci spi_nor_regions_sort_erase_types(¶ms->erase_map); 9088c2ecf20Sopenharmony_ci /* fall through */ 9098c2ecf20Sopenharmony_ciout: 9108c2ecf20Sopenharmony_ci kfree(smpt); 9118c2ecf20Sopenharmony_ci return ret; 9128c2ecf20Sopenharmony_ci} 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci/** 9158c2ecf20Sopenharmony_ci * spi_nor_parse_4bait() - parse the 4-Byte Address Instruction Table 9168c2ecf20Sopenharmony_ci * @nor: pointer to a 'struct spi_nor'. 9178c2ecf20Sopenharmony_ci * @param_header: pointer to the 'struct sfdp_parameter_header' describing 9188c2ecf20Sopenharmony_ci * the 4-Byte Address Instruction Table length and version. 9198c2ecf20Sopenharmony_ci * @params: pointer to the 'struct spi_nor_flash_parameter' to be. 9208c2ecf20Sopenharmony_ci * 9218c2ecf20Sopenharmony_ci * Return: 0 on success, -errno otherwise. 9228c2ecf20Sopenharmony_ci */ 9238c2ecf20Sopenharmony_cistatic int spi_nor_parse_4bait(struct spi_nor *nor, 9248c2ecf20Sopenharmony_ci const struct sfdp_parameter_header *param_header, 9258c2ecf20Sopenharmony_ci struct spi_nor_flash_parameter *params) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci static const struct sfdp_4bait reads[] = { 9288c2ecf20Sopenharmony_ci { SNOR_HWCAPS_READ, BIT(0) }, 9298c2ecf20Sopenharmony_ci { SNOR_HWCAPS_READ_FAST, BIT(1) }, 9308c2ecf20Sopenharmony_ci { SNOR_HWCAPS_READ_1_1_2, BIT(2) }, 9318c2ecf20Sopenharmony_ci { SNOR_HWCAPS_READ_1_2_2, BIT(3) }, 9328c2ecf20Sopenharmony_ci { SNOR_HWCAPS_READ_1_1_4, BIT(4) }, 9338c2ecf20Sopenharmony_ci { SNOR_HWCAPS_READ_1_4_4, BIT(5) }, 9348c2ecf20Sopenharmony_ci { SNOR_HWCAPS_READ_1_1_1_DTR, BIT(13) }, 9358c2ecf20Sopenharmony_ci { SNOR_HWCAPS_READ_1_2_2_DTR, BIT(14) }, 9368c2ecf20Sopenharmony_ci { SNOR_HWCAPS_READ_1_4_4_DTR, BIT(15) }, 9378c2ecf20Sopenharmony_ci }; 9388c2ecf20Sopenharmony_ci static const struct sfdp_4bait programs[] = { 9398c2ecf20Sopenharmony_ci { SNOR_HWCAPS_PP, BIT(6) }, 9408c2ecf20Sopenharmony_ci { SNOR_HWCAPS_PP_1_1_4, BIT(7) }, 9418c2ecf20Sopenharmony_ci { SNOR_HWCAPS_PP_1_4_4, BIT(8) }, 9428c2ecf20Sopenharmony_ci }; 9438c2ecf20Sopenharmony_ci static const struct sfdp_4bait erases[SNOR_ERASE_TYPE_MAX] = { 9448c2ecf20Sopenharmony_ci { 0u /* not used */, BIT(9) }, 9458c2ecf20Sopenharmony_ci { 0u /* not used */, BIT(10) }, 9468c2ecf20Sopenharmony_ci { 0u /* not used */, BIT(11) }, 9478c2ecf20Sopenharmony_ci { 0u /* not used */, BIT(12) }, 9488c2ecf20Sopenharmony_ci }; 9498c2ecf20Sopenharmony_ci struct spi_nor_pp_command *params_pp = params->page_programs; 9508c2ecf20Sopenharmony_ci struct spi_nor_erase_map *map = ¶ms->erase_map; 9518c2ecf20Sopenharmony_ci struct spi_nor_erase_type *erase_type = map->erase_type; 9528c2ecf20Sopenharmony_ci u32 *dwords; 9538c2ecf20Sopenharmony_ci size_t len; 9548c2ecf20Sopenharmony_ci u32 addr, discard_hwcaps, read_hwcaps, pp_hwcaps, erase_mask; 9558c2ecf20Sopenharmony_ci int i, ret; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (param_header->major != SFDP_JESD216_MAJOR || 9588c2ecf20Sopenharmony_ci param_header->length < SFDP_4BAIT_DWORD_MAX) 9598c2ecf20Sopenharmony_ci return -EINVAL; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci /* Read the 4-byte Address Instruction Table. */ 9628c2ecf20Sopenharmony_ci len = sizeof(*dwords) * SFDP_4BAIT_DWORD_MAX; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */ 9658c2ecf20Sopenharmony_ci dwords = kmalloc(len, GFP_KERNEL); 9668c2ecf20Sopenharmony_ci if (!dwords) 9678c2ecf20Sopenharmony_ci return -ENOMEM; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci addr = SFDP_PARAM_HEADER_PTP(param_header); 9708c2ecf20Sopenharmony_ci ret = spi_nor_read_sfdp(nor, addr, len, dwords); 9718c2ecf20Sopenharmony_ci if (ret) 9728c2ecf20Sopenharmony_ci goto out; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci /* Fix endianness of the 4BAIT DWORDs. */ 9758c2ecf20Sopenharmony_ci le32_to_cpu_array(dwords, SFDP_4BAIT_DWORD_MAX); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci /* 9788c2ecf20Sopenharmony_ci * Compute the subset of (Fast) Read commands for which the 4-byte 9798c2ecf20Sopenharmony_ci * version is supported. 9808c2ecf20Sopenharmony_ci */ 9818c2ecf20Sopenharmony_ci discard_hwcaps = 0; 9828c2ecf20Sopenharmony_ci read_hwcaps = 0; 9838c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(reads); i++) { 9848c2ecf20Sopenharmony_ci const struct sfdp_4bait *read = &reads[i]; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci discard_hwcaps |= read->hwcaps; 9878c2ecf20Sopenharmony_ci if ((params->hwcaps.mask & read->hwcaps) && 9888c2ecf20Sopenharmony_ci (dwords[0] & read->supported_bit)) 9898c2ecf20Sopenharmony_ci read_hwcaps |= read->hwcaps; 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci /* 9938c2ecf20Sopenharmony_ci * Compute the subset of Page Program commands for which the 4-byte 9948c2ecf20Sopenharmony_ci * version is supported. 9958c2ecf20Sopenharmony_ci */ 9968c2ecf20Sopenharmony_ci pp_hwcaps = 0; 9978c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(programs); i++) { 9988c2ecf20Sopenharmony_ci const struct sfdp_4bait *program = &programs[i]; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci /* 10018c2ecf20Sopenharmony_ci * The 4 Byte Address Instruction (Optional) Table is the only 10028c2ecf20Sopenharmony_ci * SFDP table that indicates support for Page Program Commands. 10038c2ecf20Sopenharmony_ci * Bypass the params->hwcaps.mask and consider 4BAIT the biggest 10048c2ecf20Sopenharmony_ci * authority for specifying Page Program support. 10058c2ecf20Sopenharmony_ci */ 10068c2ecf20Sopenharmony_ci discard_hwcaps |= program->hwcaps; 10078c2ecf20Sopenharmony_ci if (dwords[0] & program->supported_bit) 10088c2ecf20Sopenharmony_ci pp_hwcaps |= program->hwcaps; 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci /* 10128c2ecf20Sopenharmony_ci * Compute the subset of Sector Erase commands for which the 4-byte 10138c2ecf20Sopenharmony_ci * version is supported. 10148c2ecf20Sopenharmony_ci */ 10158c2ecf20Sopenharmony_ci erase_mask = 0; 10168c2ecf20Sopenharmony_ci for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { 10178c2ecf20Sopenharmony_ci const struct sfdp_4bait *erase = &erases[i]; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci if (dwords[0] & erase->supported_bit) 10208c2ecf20Sopenharmony_ci erase_mask |= BIT(i); 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci /* Replicate the sort done for the map's erase types in BFPT. */ 10248c2ecf20Sopenharmony_ci erase_mask = spi_nor_sort_erase_mask(map, erase_mask); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* 10278c2ecf20Sopenharmony_ci * We need at least one 4-byte op code per read, program and erase 10288c2ecf20Sopenharmony_ci * operation; the .read(), .write() and .erase() hooks share the 10298c2ecf20Sopenharmony_ci * nor->addr_width value. 10308c2ecf20Sopenharmony_ci */ 10318c2ecf20Sopenharmony_ci if (!read_hwcaps || !pp_hwcaps || !erase_mask) 10328c2ecf20Sopenharmony_ci goto out; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci /* 10358c2ecf20Sopenharmony_ci * Discard all operations from the 4-byte instruction set which are 10368c2ecf20Sopenharmony_ci * not supported by this memory. 10378c2ecf20Sopenharmony_ci */ 10388c2ecf20Sopenharmony_ci params->hwcaps.mask &= ~discard_hwcaps; 10398c2ecf20Sopenharmony_ci params->hwcaps.mask |= (read_hwcaps | pp_hwcaps); 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci /* Use the 4-byte address instruction set. */ 10428c2ecf20Sopenharmony_ci for (i = 0; i < SNOR_CMD_READ_MAX; i++) { 10438c2ecf20Sopenharmony_ci struct spi_nor_read_command *read_cmd = ¶ms->reads[i]; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci read_cmd->opcode = spi_nor_convert_3to4_read(read_cmd->opcode); 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci /* 4BAIT is the only SFDP table that indicates page program support. */ 10498c2ecf20Sopenharmony_ci if (pp_hwcaps & SNOR_HWCAPS_PP) 10508c2ecf20Sopenharmony_ci spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP], 10518c2ecf20Sopenharmony_ci SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1); 10528c2ecf20Sopenharmony_ci if (pp_hwcaps & SNOR_HWCAPS_PP_1_1_4) 10538c2ecf20Sopenharmony_ci spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_1_1_4], 10548c2ecf20Sopenharmony_ci SPINOR_OP_PP_1_1_4_4B, 10558c2ecf20Sopenharmony_ci SNOR_PROTO_1_1_4); 10568c2ecf20Sopenharmony_ci if (pp_hwcaps & SNOR_HWCAPS_PP_1_4_4) 10578c2ecf20Sopenharmony_ci spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_1_4_4], 10588c2ecf20Sopenharmony_ci SPINOR_OP_PP_1_4_4_4B, 10598c2ecf20Sopenharmony_ci SNOR_PROTO_1_4_4); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { 10628c2ecf20Sopenharmony_ci if (erase_mask & BIT(i)) 10638c2ecf20Sopenharmony_ci erase_type[i].opcode = (dwords[1] >> 10648c2ecf20Sopenharmony_ci erase_type[i].idx * 8) & 0xFF; 10658c2ecf20Sopenharmony_ci else 10668c2ecf20Sopenharmony_ci spi_nor_mask_erase_type(&erase_type[i]); 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci /* 10708c2ecf20Sopenharmony_ci * We set SNOR_F_HAS_4BAIT in order to skip spi_nor_set_4byte_opcodes() 10718c2ecf20Sopenharmony_ci * later because we already did the conversion to 4byte opcodes. Also, 10728c2ecf20Sopenharmony_ci * this latest function implements a legacy quirk for the erase size of 10738c2ecf20Sopenharmony_ci * Spansion memory. However this quirk is no longer needed with new 10748c2ecf20Sopenharmony_ci * SFDP compliant memories. 10758c2ecf20Sopenharmony_ci */ 10768c2ecf20Sopenharmony_ci nor->addr_width = 4; 10778c2ecf20Sopenharmony_ci nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci /* fall through */ 10808c2ecf20Sopenharmony_ciout: 10818c2ecf20Sopenharmony_ci kfree(dwords); 10828c2ecf20Sopenharmony_ci return ret; 10838c2ecf20Sopenharmony_ci} 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci/** 10868c2ecf20Sopenharmony_ci * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters. 10878c2ecf20Sopenharmony_ci * @nor: pointer to a 'struct spi_nor' 10888c2ecf20Sopenharmony_ci * @params: pointer to the 'struct spi_nor_flash_parameter' to be 10898c2ecf20Sopenharmony_ci * filled 10908c2ecf20Sopenharmony_ci * 10918c2ecf20Sopenharmony_ci * The Serial Flash Discoverable Parameters are described by the JEDEC JESD216 10928c2ecf20Sopenharmony_ci * specification. This is a standard which tends to supported by almost all 10938c2ecf20Sopenharmony_ci * (Q)SPI memory manufacturers. Those hard-coded tables allow us to learn at 10948c2ecf20Sopenharmony_ci * runtime the main parameters needed to perform basic SPI flash operations such 10958c2ecf20Sopenharmony_ci * as Fast Read, Page Program or Sector Erase commands. 10968c2ecf20Sopenharmony_ci * 10978c2ecf20Sopenharmony_ci * Return: 0 on success, -errno otherwise. 10988c2ecf20Sopenharmony_ci */ 10998c2ecf20Sopenharmony_ciint spi_nor_parse_sfdp(struct spi_nor *nor, 11008c2ecf20Sopenharmony_ci struct spi_nor_flash_parameter *params) 11018c2ecf20Sopenharmony_ci{ 11028c2ecf20Sopenharmony_ci const struct sfdp_parameter_header *param_header, *bfpt_header; 11038c2ecf20Sopenharmony_ci struct sfdp_parameter_header *param_headers = NULL; 11048c2ecf20Sopenharmony_ci struct sfdp_header header; 11058c2ecf20Sopenharmony_ci struct device *dev = nor->dev; 11068c2ecf20Sopenharmony_ci size_t psize; 11078c2ecf20Sopenharmony_ci int i, err; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci /* Get the SFDP header. */ 11108c2ecf20Sopenharmony_ci err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(header), &header); 11118c2ecf20Sopenharmony_ci if (err < 0) 11128c2ecf20Sopenharmony_ci return err; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci /* Check the SFDP header version. */ 11158c2ecf20Sopenharmony_ci if (le32_to_cpu(header.signature) != SFDP_SIGNATURE || 11168c2ecf20Sopenharmony_ci header.major != SFDP_JESD216_MAJOR) 11178c2ecf20Sopenharmony_ci return -EINVAL; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci /* 11208c2ecf20Sopenharmony_ci * Verify that the first and only mandatory parameter header is a 11218c2ecf20Sopenharmony_ci * Basic Flash Parameter Table header as specified in JESD216. 11228c2ecf20Sopenharmony_ci */ 11238c2ecf20Sopenharmony_ci bfpt_header = &header.bfpt_header; 11248c2ecf20Sopenharmony_ci if (SFDP_PARAM_HEADER_ID(bfpt_header) != SFDP_BFPT_ID || 11258c2ecf20Sopenharmony_ci bfpt_header->major != SFDP_JESD216_MAJOR) 11268c2ecf20Sopenharmony_ci return -EINVAL; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci /* 11298c2ecf20Sopenharmony_ci * Allocate memory then read all parameter headers with a single 11308c2ecf20Sopenharmony_ci * Read SFDP command. These parameter headers will actually be parsed 11318c2ecf20Sopenharmony_ci * twice: a first time to get the latest revision of the basic flash 11328c2ecf20Sopenharmony_ci * parameter table, then a second time to handle the supported optional 11338c2ecf20Sopenharmony_ci * tables. 11348c2ecf20Sopenharmony_ci * Hence we read the parameter headers once for all to reduce the 11358c2ecf20Sopenharmony_ci * processing time. Also we use kmalloc() instead of devm_kmalloc() 11368c2ecf20Sopenharmony_ci * because we don't need to keep these parameter headers: the allocated 11378c2ecf20Sopenharmony_ci * memory is always released with kfree() before exiting this function. 11388c2ecf20Sopenharmony_ci */ 11398c2ecf20Sopenharmony_ci if (header.nph) { 11408c2ecf20Sopenharmony_ci psize = header.nph * sizeof(*param_headers); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci param_headers = kmalloc(psize, GFP_KERNEL); 11438c2ecf20Sopenharmony_ci if (!param_headers) 11448c2ecf20Sopenharmony_ci return -ENOMEM; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci err = spi_nor_read_sfdp(nor, sizeof(header), 11478c2ecf20Sopenharmony_ci psize, param_headers); 11488c2ecf20Sopenharmony_ci if (err < 0) { 11498c2ecf20Sopenharmony_ci dev_dbg(dev, "failed to read SFDP parameter headers\n"); 11508c2ecf20Sopenharmony_ci goto exit; 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci /* 11558c2ecf20Sopenharmony_ci * Check other parameter headers to get the latest revision of 11568c2ecf20Sopenharmony_ci * the basic flash parameter table. 11578c2ecf20Sopenharmony_ci */ 11588c2ecf20Sopenharmony_ci for (i = 0; i < header.nph; i++) { 11598c2ecf20Sopenharmony_ci param_header = ¶m_headers[i]; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci if (SFDP_PARAM_HEADER_ID(param_header) == SFDP_BFPT_ID && 11628c2ecf20Sopenharmony_ci param_header->major == SFDP_JESD216_MAJOR && 11638c2ecf20Sopenharmony_ci (param_header->minor > bfpt_header->minor || 11648c2ecf20Sopenharmony_ci (param_header->minor == bfpt_header->minor && 11658c2ecf20Sopenharmony_ci param_header->length > bfpt_header->length))) 11668c2ecf20Sopenharmony_ci bfpt_header = param_header; 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci err = spi_nor_parse_bfpt(nor, bfpt_header, params); 11708c2ecf20Sopenharmony_ci if (err) 11718c2ecf20Sopenharmony_ci goto exit; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci /* Parse optional parameter tables. */ 11748c2ecf20Sopenharmony_ci for (i = 0; i < header.nph; i++) { 11758c2ecf20Sopenharmony_ci param_header = ¶m_headers[i]; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci switch (SFDP_PARAM_HEADER_ID(param_header)) { 11788c2ecf20Sopenharmony_ci case SFDP_SECTOR_MAP_ID: 11798c2ecf20Sopenharmony_ci err = spi_nor_parse_smpt(nor, param_header, params); 11808c2ecf20Sopenharmony_ci break; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci case SFDP_4BAIT_ID: 11838c2ecf20Sopenharmony_ci err = spi_nor_parse_4bait(nor, param_header, params); 11848c2ecf20Sopenharmony_ci break; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci default: 11878c2ecf20Sopenharmony_ci break; 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (err) { 11918c2ecf20Sopenharmony_ci dev_warn(dev, "Failed to parse optional parameter table: %04x\n", 11928c2ecf20Sopenharmony_ci SFDP_PARAM_HEADER_ID(param_header)); 11938c2ecf20Sopenharmony_ci /* 11948c2ecf20Sopenharmony_ci * Let's not drop all information we extracted so far 11958c2ecf20Sopenharmony_ci * if optional table parsers fail. In case of failing, 11968c2ecf20Sopenharmony_ci * each optional parser is responsible to roll back to 11978c2ecf20Sopenharmony_ci * the previously known spi_nor data. 11988c2ecf20Sopenharmony_ci */ 11998c2ecf20Sopenharmony_ci err = 0; 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci } 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ciexit: 12048c2ecf20Sopenharmony_ci kfree(param_headers); 12058c2ecf20Sopenharmony_ci return err; 12068c2ecf20Sopenharmony_ci} 1207