mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-28 14:44:10 +08:00
SPI NOR core changes:
* Add support for flash reset using the dt reset-gpios property. * Update hwcaps.mask to include 8D-8D-8D read and page program ops when xSPI profile 1.0 table is defined. * Bypass zero erase size in spi_nor_find_best_erase_type(). * Fix select_uniform_erase to skip 0 erase size * Add generic flash driver. If a flash is not found in the flash_info array, fall back to the generic flash driver which is described solely by the flash's SFDP tables. * Fix the number of bytes for the dummy cycles in spi_nor_spimem_check_readop(). * Introduce SPI_NOR_QUAD_PP flag, as PP_1_1_4 is not SFDP discoverable. SPI NOR manufacturer drivers changes: * Spansion: - use PARSE_SFDP for s28hs512t, - add support for s28hl512t, s28hl01gt, and s28hs01gt. * Gigadevice: Replace default_init() with post_bfpt() for gd25q256. * Micron - ST: Enable locking for mt25qu256a. * Winbond: Add support for W25Q512NW-IQ. * ISSI: Use PARSE_SFDP and SPI_NOR_QUAD_PP. -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEHUIqys8OyG1eHf7fS1VPR6WNFOkFAmOJ59AACgkQS1VPR6WN FOnzOQf/WLBTNFtPcJoS7cdjlh4fuGySDUKuLifOX+Tmk/ki3XFCZR98cESgb5m+ hCyFDeER/qfdOK9ObT3K1GBS6x7zx8YkskbucjAU0vlIsr2ZHCjNpNiAHrADsKjR 579N0Xwh2ibh7uVJ1xvsPwObdAc8P/tjGHHWF6hGEZzIPYXJVce+qDktkkLfbZ/P Fik6+y3h68wW/0kDNt/gNmNcb5Z3fjxySs21Np5sy7bUtVVBjHWLo0OpjNadDH9M m6AkhFA5h9OVl5E4jHBpxc4NvlAa5f6520vESpieseNJiIMWo0W/eSkVsframFew ILdtXCHjkbS6kgvSpUViobMFsgCjaA== =Yi2q -----END PGP SIGNATURE----- Merge tag 'spi-nor/for-6.2' into mtd/next SPI NOR core changes: * Add support for flash reset using the dt reset-gpios property. * Update hwcaps.mask to include 8D-8D-8D read and page program ops when xSPI profile 1.0 table is defined. * Bypass zero erase size in spi_nor_find_best_erase_type(). * Fix select_uniform_erase to skip 0 erase size * Add generic flash driver. If a flash is not found in the flash_info array, fall back to the generic flash driver which is described solely by the flash's SFDP tables. * Fix the number of bytes for the dummy cycles in spi_nor_spimem_check_readop(). * Introduce SPI_NOR_QUAD_PP flag, as PP_1_1_4 is not SFDP discoverable. SPI NOR manufacturer drivers changes: * Spansion: - use PARSE_SFDP for s28hs512t, - add support for s28hl512t, s28hl01gt, and s28hs01gt. * Gigadevice: Replace default_init() with post_bfpt() for gd25q256. * Micron - ST: Enable locking for mt25qu256a. * Winbond: Add support for W25Q512NW-IQ. * ISSI: Use PARSE_SFDP and SPI_NOR_QUAD_PP. Fix merge conflict in the jedec,spi-nor bindings. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
This commit is contained in:
commit
a34506e08d
@ -5,6 +5,9 @@ Contact: linux-mtd@lists.infradead.org
|
||||
Description: (RO) The JEDEC ID of the SPI NOR flash as reported by the
|
||||
flash device.
|
||||
|
||||
The attribute is not present if the flash doesn't support
|
||||
the "Read JEDEC ID" command (9Fh). This is the case for
|
||||
non-JEDEC compliant flashes.
|
||||
|
||||
What: /sys/bus/spi/devices/.../spi-nor/manufacturer
|
||||
Date: April 2021
|
||||
@ -12,6 +15,9 @@ KernelVersion: 5.14
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description: (RO) Manufacturer of the SPI NOR flash.
|
||||
|
||||
The attribute is not present if the flash device isn't
|
||||
known to the kernel and is only probed by its SFDP
|
||||
tables.
|
||||
|
||||
What: /sys/bus/spi/devices/.../spi-nor/partname
|
||||
Date: April 2021
|
||||
|
@ -70,10 +70,17 @@ properties:
|
||||
be used on such systems, to denote the absence of a reliable reset
|
||||
mechanism.
|
||||
|
||||
reset-gpios:
|
||||
description:
|
||||
A GPIO line connected to the RESET (active low) signal of the device.
|
||||
If "broken-flash-reset" is present then having this property does not
|
||||
make any difference.
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
@ -83,6 +90,7 @@ examples:
|
||||
reg = <0>;
|
||||
spi-max-frequency = <40000000>;
|
||||
m25p,fast-read;
|
||||
reset-gpios = <&gpio 12 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
@ -1184,6 +1184,8 @@ spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
|
||||
continue;
|
||||
|
||||
erase = &map->erase_type[i];
|
||||
if (!erase->size)
|
||||
continue;
|
||||
|
||||
/* Alignment is not mandatory for overlaid regions */
|
||||
if (region->offset & SNOR_OVERLAID_REGION &&
|
||||
@ -1632,6 +1634,16 @@ static const struct spi_nor_manufacturer *manufacturers[] = {
|
||||
&spi_nor_xmc,
|
||||
};
|
||||
|
||||
static const struct flash_info spi_nor_generic_flash = {
|
||||
.name = "spi-nor-generic",
|
||||
/*
|
||||
* JESD216 rev A doesn't specify the page size, therefore we need a
|
||||
* sane default.
|
||||
*/
|
||||
.page_size = 256,
|
||||
.parse_sfdp = true,
|
||||
};
|
||||
|
||||
static const struct flash_info *spi_nor_match_id(struct spi_nor *nor,
|
||||
const u8 *id)
|
||||
{
|
||||
@ -1664,7 +1676,20 @@ static const struct flash_info *spi_nor_detect(struct spi_nor *nor)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/* Cache the complete flash ID. */
|
||||
nor->id = devm_kmemdup(nor->dev, id, SPI_NOR_MAX_ID_LEN, GFP_KERNEL);
|
||||
if (!nor->id)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
info = spi_nor_match_id(nor, id);
|
||||
|
||||
/* Fallback to a generic flash described only by its SFDP data. */
|
||||
if (!info) {
|
||||
ret = spi_nor_check_sfdp_signature(nor);
|
||||
if (!ret)
|
||||
info = &spi_nor_generic_flash;
|
||||
}
|
||||
|
||||
if (!info) {
|
||||
dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n",
|
||||
SPI_NOR_MAX_ID_LEN, id);
|
||||
@ -1914,7 +1939,8 @@ static int spi_nor_spimem_check_readop(struct spi_nor *nor,
|
||||
spi_nor_spimem_setup_op(nor, &op, read->proto);
|
||||
|
||||
/* convert the dummy cycles to the number of bytes */
|
||||
op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
|
||||
op.dummy.nbytes = (read->num_mode_clocks + read->num_wait_states) *
|
||||
op.dummy.buswidth / 8;
|
||||
if (spi_nor_protocol_is_dtr(nor->read_proto))
|
||||
op.dummy.nbytes *= 2;
|
||||
|
||||
@ -2091,8 +2117,12 @@ static int spi_nor_select_pp(struct spi_nor *nor,
|
||||
* spi_nor_select_uniform_erase() - select optimum uniform erase type
|
||||
* @map: the erase map of the SPI NOR
|
||||
* @wanted_size: the erase type size to search for. Contains the value of
|
||||
* info->sector_size or of the "small sector" size in case
|
||||
* CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined.
|
||||
* info->sector_size, the "small sector" size in case
|
||||
* CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined or 0 if
|
||||
* there is no information about the sector size. The
|
||||
* latter is the case if the flash parameters are parsed
|
||||
* solely by SFDP, then the largest supported erase type
|
||||
* is selected.
|
||||
*
|
||||
* Once the optimum uniform sector erase command is found, disable all the
|
||||
* other.
|
||||
@ -2113,6 +2143,10 @@ spi_nor_select_uniform_erase(struct spi_nor_erase_map *map,
|
||||
|
||||
tested_erase = &map->erase_type[i];
|
||||
|
||||
/* Skip masked erase types. */
|
||||
if (!tested_erase->size)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If the current erase size is the one, stop here:
|
||||
* we have found the right uniform Sector Erase command.
|
||||
@ -2565,6 +2599,12 @@ static void spi_nor_init_default_params(struct spi_nor *nor)
|
||||
params->hwcaps.mask |= SNOR_HWCAPS_PP;
|
||||
spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP],
|
||||
SPINOR_OP_PP, SNOR_PROTO_1_1_1);
|
||||
|
||||
if (info->flags & SPI_NOR_QUAD_PP) {
|
||||
params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4;
|
||||
spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_1_1_4],
|
||||
SPINOR_OP_PP_1_1_4, SNOR_PROTO_1_1_4);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2840,10 +2880,20 @@ static void spi_nor_put_device(struct mtd_info *mtd)
|
||||
|
||||
void spi_nor_restore(struct spi_nor *nor)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* restore the addressing mode */
|
||||
if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
|
||||
nor->flags & SNOR_F_BROKEN_RESET)
|
||||
nor->params->set_4byte_addr_mode(nor, false);
|
||||
nor->flags & SNOR_F_BROKEN_RESET) {
|
||||
ret = nor->params->set_4byte_addr_mode(nor, false);
|
||||
if (ret)
|
||||
/*
|
||||
* Do not stop the execution in the hope that the flash
|
||||
* will default to the 3-byte address mode after the
|
||||
* software reset.
|
||||
*/
|
||||
dev_err(nor->dev, "Failed to exit 4-byte address mode, err = %d\n", ret);
|
||||
}
|
||||
|
||||
if (nor->flags & SNOR_F_SOFT_RESET)
|
||||
spi_nor_soft_reset(nor);
|
||||
@ -2935,6 +2985,27 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
|
||||
mtd->_put_device = spi_nor_put_device;
|
||||
}
|
||||
|
||||
static int spi_nor_hw_reset(struct spi_nor *nor)
|
||||
{
|
||||
struct gpio_desc *reset;
|
||||
|
||||
reset = devm_gpiod_get_optional(nor->dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR_OR_NULL(reset))
|
||||
return PTR_ERR_OR_ZERO(reset);
|
||||
|
||||
/*
|
||||
* Experimental delay values by looking at different flash device
|
||||
* vendors datasheets.
|
||||
*/
|
||||
usleep_range(1, 5);
|
||||
gpiod_set_value_cansleep(reset, 1);
|
||||
usleep_range(100, 150);
|
||||
gpiod_set_value_cansleep(reset, 0);
|
||||
usleep_range(1000, 1200);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spi_nor_scan(struct spi_nor *nor, const char *name,
|
||||
const struct spi_nor_hwcaps *hwcaps)
|
||||
{
|
||||
@ -2967,6 +3038,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
|
||||
if (!nor->bouncebuf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = spi_nor_hw_reset(nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
info = spi_nor_get_flash_info(nor, name);
|
||||
if (IS_ERR(info))
|
||||
return PTR_ERR(info);
|
||||
|
@ -458,6 +458,7 @@ struct spi_nor_fixups {
|
||||
* SPI_NOR_NO_ERASE: no erase command needed.
|
||||
* NO_CHIP_ERASE: chip does not support chip erase.
|
||||
* SPI_NOR_NO_FR: can't do fastread.
|
||||
* SPI_NOR_QUAD_PP: flash supports Quad Input Page Program.
|
||||
*
|
||||
* @no_sfdp_flags: flags that indicate support that can be discovered via SFDP.
|
||||
* Used when SFDP tables are not defined in the flash. These
|
||||
@ -507,6 +508,7 @@ struct flash_info {
|
||||
#define SPI_NOR_NO_ERASE BIT(6)
|
||||
#define NO_CHIP_ERASE BIT(7)
|
||||
#define SPI_NOR_NO_FR BIT(8)
|
||||
#define SPI_NOR_QUAD_PP BIT(9)
|
||||
|
||||
u8 no_sfdp_flags;
|
||||
#define SPI_NOR_SKIP_SFDP BIT(0)
|
||||
@ -701,6 +703,9 @@ int spi_nor_controller_ops_read_reg(struct spi_nor *nor, u8 opcode,
|
||||
int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode,
|
||||
const u8 *buf, size_t len);
|
||||
|
||||
int spi_nor_check_sfdp_signature(struct spi_nor *nor);
|
||||
int spi_nor_parse_sfdp(struct spi_nor *nor);
|
||||
|
||||
static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
|
||||
{
|
||||
return container_of(mtd, struct spi_nor, mtd);
|
||||
|
@ -81,7 +81,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data)
|
||||
int i;
|
||||
|
||||
seq_printf(s, "name\t\t%s\n", info->name);
|
||||
seq_printf(s, "id\t\t%*ph\n", info->id_len, info->id);
|
||||
seq_printf(s, "id\t\t%*ph\n", SPI_NOR_MAX_ID_LEN, nor->id);
|
||||
string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf));
|
||||
seq_printf(s, "size\t\t%s\n", buf);
|
||||
seq_printf(s, "write size\t%u\n", params->writesize);
|
||||
|
@ -8,19 +8,29 @@
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static void gd25q256_default_init(struct spi_nor *nor)
|
||||
static int
|
||||
gd25q256_post_bfpt(struct spi_nor *nor,
|
||||
const struct sfdp_parameter_header *bfpt_header,
|
||||
const struct sfdp_bfpt *bfpt)
|
||||
{
|
||||
/*
|
||||
* Some manufacturer like GigaDevice may use different
|
||||
* bit to set QE on different memories, so the MFR can't
|
||||
* indicate the quad_enable method for this case, we need
|
||||
* to set it in the default_init fixup hook.
|
||||
* GD25Q256C supports the first version of JESD216 which does not define
|
||||
* the Quad Enable methods. Overwrite the default Quad Enable method.
|
||||
*
|
||||
* GD25Q256 GENERATION | SFDP MAJOR VERSION | SFDP MINOR VERSION
|
||||
* GD25Q256C | SFDP_JESD216_MAJOR | SFDP_JESD216_MINOR
|
||||
* GD25Q256D | SFDP_JESD216_MAJOR | SFDP_JESD216B_MINOR
|
||||
* GD25Q256E | SFDP_JESD216_MAJOR | SFDP_JESD216B_MINOR
|
||||
*/
|
||||
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
|
||||
if (bfpt_header->major == SFDP_JESD216_MAJOR &&
|
||||
bfpt_header->minor == SFDP_JESD216_MINOR)
|
||||
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_nor_fixups gd25q256_fixups = {
|
||||
.default_init = gd25q256_default_init,
|
||||
.post_bfpt = gd25q256_post_bfpt,
|
||||
};
|
||||
|
||||
static const struct flash_info gigadevice_nor_parts[] = {
|
||||
|
@ -70,9 +70,10 @@ static const struct flash_info issi_nor_parts[] = {
|
||||
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
{ "is25wp128", INFO(0x9d7018, 0, 64 * 1024, 256)
|
||||
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
{ "is25wp256", INFO(0x9d7019, 0, 64 * 1024, 512)
|
||||
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
|
||||
{ "is25wp256", INFO(0x9d7019, 0, 0, 0)
|
||||
PARSE_SFDP
|
||||
FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
|
||||
FLAGS(SPI_NOR_QUAD_PP)
|
||||
.fixups = &is25lp256_fixups },
|
||||
|
||||
/* PMC */
|
||||
|
@ -52,18 +52,21 @@ static int micron_st_nor_octal_dtr_en(struct spi_nor *nor)
|
||||
struct spi_mem_op op;
|
||||
u8 *buf = nor->bouncebuf;
|
||||
int ret;
|
||||
u8 addr_mode_nbytes = nor->params->addr_mode_nbytes;
|
||||
|
||||
/* Use 20 dummy cycles for memory array reads. */
|
||||
*buf = 20;
|
||||
op = (struct spi_mem_op)
|
||||
MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR1V, 1, buf);
|
||||
MICRON_ST_NOR_WR_ANY_REG_OP(addr_mode_nbytes,
|
||||
SPINOR_REG_MT_CFR1V, 1, buf);
|
||||
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
buf[0] = SPINOR_MT_OCT_DTR;
|
||||
op = (struct spi_mem_op)
|
||||
MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR0V, 1, buf);
|
||||
MICRON_ST_NOR_WR_ANY_REG_OP(addr_mode_nbytes,
|
||||
SPINOR_REG_MT_CFR0V, 1, buf);
|
||||
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -98,7 +101,8 @@ static int micron_st_nor_octal_dtr_dis(struct spi_nor *nor)
|
||||
buf[0] = SPINOR_MT_EXSPI;
|
||||
buf[1] = SPINOR_REG_MT_CFR1V_DEF;
|
||||
op = (struct spi_mem_op)
|
||||
MICRON_ST_NOR_WR_ANY_REG_OP(4, SPINOR_REG_MT_CFR0V, 2, buf);
|
||||
MICRON_ST_NOR_WR_ANY_REG_OP(nor->addr_nbytes,
|
||||
SPINOR_REG_MT_CFR0V, 2, buf);
|
||||
ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -201,6 +205,8 @@ static const struct flash_info st_nor_parts[] = {
|
||||
MFR_FLAGS(USE_FSR)
|
||||
},
|
||||
{ "mt25qu256a", INFO6(0x20bb19, 0x104400, 64 * 1024, 512)
|
||||
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
|
||||
SPI_NOR_BP3_SR_BIT6)
|
||||
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
|
||||
FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
|
||||
MFR_FLAGS(USE_FSR)
|
||||
|
@ -135,8 +135,7 @@ struct sfdp_4bait {
|
||||
/**
|
||||
* spi_nor_read_raw() - raw read of serial flash memory. read_opcode,
|
||||
* addr_nbytes and read_dummy members of the struct spi_nor
|
||||
* should be previously
|
||||
* set.
|
||||
* should be previously set.
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
* @addr: offset in the serial flash memory
|
||||
* @len: number of bytes to read
|
||||
@ -1183,10 +1182,17 @@ static int spi_nor_parse_profile1(struct spi_nor *nor,
|
||||
dummy = round_up(dummy, 2);
|
||||
|
||||
/* Update the fast read settings. */
|
||||
nor->params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR;
|
||||
spi_nor_set_read_settings(&nor->params->reads[SNOR_CMD_READ_8_8_8_DTR],
|
||||
0, dummy, opcode,
|
||||
SNOR_PROTO_8_8_8_DTR);
|
||||
|
||||
/*
|
||||
* Page Program is "Required Command" in the xSPI Profile 1.0. Update
|
||||
* the params->hwcaps.mask here.
|
||||
*/
|
||||
nor->params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR;
|
||||
|
||||
out:
|
||||
kfree(dwords);
|
||||
return ret;
|
||||
@ -1249,6 +1255,33 @@ static void spi_nor_post_sfdp_fixups(struct spi_nor *nor)
|
||||
nor->info->fixups->post_sfdp(nor);
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_check_sfdp_signature() - check for a valid SFDP signature
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
*
|
||||
* Used to detect if the flash supports the RDSFDP command as well as the
|
||||
* presence of a valid SFDP table.
|
||||
*
|
||||
* Return: 0 on success, -errno otherwise.
|
||||
*/
|
||||
int spi_nor_check_sfdp_signature(struct spi_nor *nor)
|
||||
{
|
||||
u32 signature;
|
||||
int err;
|
||||
|
||||
/* Get the SFDP header. */
|
||||
err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(signature),
|
||||
&signature);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Check the SFDP signature. */
|
||||
if (le32_to_cpu(signature) != SFDP_SIGNATURE)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
|
@ -107,6 +107,4 @@ struct sfdp_parameter_header {
|
||||
u8 id_msb;
|
||||
};
|
||||
|
||||
int spi_nor_parse_sfdp(struct spi_nor *nor);
|
||||
|
||||
#endif /* __LINUX_MTD_SFDP_H */
|
||||
|
@ -49,11 +49,13 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor)
|
||||
struct spi_mem_op op;
|
||||
u8 *buf = nor->bouncebuf;
|
||||
int ret;
|
||||
u8 addr_mode_nbytes = nor->params->addr_mode_nbytes;
|
||||
|
||||
/* Use 24 dummy cycles for memory array reads. */
|
||||
*buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24;
|
||||
op = (struct spi_mem_op)
|
||||
CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR2V, 1, buf);
|
||||
CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes,
|
||||
SPINOR_REG_CYPRESS_CFR2V, 1, buf);
|
||||
|
||||
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
|
||||
if (ret)
|
||||
@ -64,14 +66,16 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor)
|
||||
/* Set the octal and DTR enable bits. */
|
||||
buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN;
|
||||
op = (struct spi_mem_op)
|
||||
CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR5V, 1, buf);
|
||||
CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes,
|
||||
SPINOR_REG_CYPRESS_CFR5V, 1, buf);
|
||||
|
||||
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Read flash ID to make sure the switch was successful. */
|
||||
ret = spi_nor_read_id(nor, 4, 3, buf, SNOR_PROTO_8_8_8_DTR);
|
||||
ret = spi_nor_read_id(nor, nor->addr_nbytes, 3, buf,
|
||||
SNOR_PROTO_8_8_8_DTR);
|
||||
if (ret) {
|
||||
dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n", ret);
|
||||
return ret;
|
||||
@ -97,7 +101,8 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor)
|
||||
buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS;
|
||||
buf[1] = 0;
|
||||
op = (struct spi_mem_op)
|
||||
CYPRESS_NOR_WR_ANY_REG_OP(4, SPINOR_REG_CYPRESS_CFR5V, 2, buf);
|
||||
CYPRESS_NOR_WR_ANY_REG_OP(nor->addr_nbytes,
|
||||
SPINOR_REG_CYPRESS_CFR5V, 2, buf);
|
||||
ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -191,7 +196,8 @@ static int cypress_nor_quad_enable_volatile(struct spi_nor *nor)
|
||||
static int cypress_nor_set_page_size(struct spi_nor *nor)
|
||||
{
|
||||
struct spi_mem_op op =
|
||||
CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR3V,
|
||||
CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes,
|
||||
SPINOR_REG_CYPRESS_CFR3V,
|
||||
nor->bouncebuf);
|
||||
int ret;
|
||||
|
||||
@ -275,13 +281,7 @@ static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
|
||||
cypress_nor_octal_dtr_dis(nor);
|
||||
}
|
||||
|
||||
static void s28hs512t_default_init(struct spi_nor *nor)
|
||||
{
|
||||
nor->params->octal_dtr_enable = cypress_nor_octal_dtr_enable;
|
||||
nor->params->writesize = 16;
|
||||
}
|
||||
|
||||
static void s28hs512t_post_sfdp_fixup(struct spi_nor *nor)
|
||||
static void s28hx_t_post_sfdp_fixup(struct spi_nor *nor)
|
||||
{
|
||||
/*
|
||||
* On older versions of the flash the xSPI Profile 1.0 table has the
|
||||
@ -309,17 +309,23 @@ static void s28hs512t_post_sfdp_fixup(struct spi_nor *nor)
|
||||
nor->params->rdsr_addr_nbytes = 4;
|
||||
}
|
||||
|
||||
static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor,
|
||||
const struct sfdp_parameter_header *bfpt_header,
|
||||
const struct sfdp_bfpt *bfpt)
|
||||
static int s28hx_t_post_bfpt_fixup(struct spi_nor *nor,
|
||||
const struct sfdp_parameter_header *bfpt_header,
|
||||
const struct sfdp_bfpt *bfpt)
|
||||
{
|
||||
return cypress_nor_set_page_size(nor);
|
||||
}
|
||||
|
||||
static const struct spi_nor_fixups s28hs512t_fixups = {
|
||||
.default_init = s28hs512t_default_init,
|
||||
.post_sfdp = s28hs512t_post_sfdp_fixup,
|
||||
.post_bfpt = s28hs512t_post_bfpt_fixup,
|
||||
static void s28hx_t_late_init(struct spi_nor *nor)
|
||||
{
|
||||
nor->params->octal_dtr_enable = cypress_nor_octal_dtr_enable;
|
||||
nor->params->writesize = 16;
|
||||
}
|
||||
|
||||
static const struct spi_nor_fixups s28hx_t_fixups = {
|
||||
.post_sfdp = s28hx_t_post_sfdp_fixup,
|
||||
.post_bfpt = s28hx_t_post_bfpt_fixup,
|
||||
.late_init = s28hx_t_late_init,
|
||||
};
|
||||
|
||||
static int
|
||||
@ -453,10 +459,21 @@ static const struct flash_info spansion_nor_parts[] = {
|
||||
.fixups = &s25hx_t_fixups },
|
||||
{ "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1)
|
||||
FLAGS(SPI_NOR_NO_ERASE) },
|
||||
{ "s28hl512t", INFO(0x345a1a, 0, 256 * 1024, 256)
|
||||
PARSE_SFDP
|
||||
.fixups = &s28hx_t_fixups,
|
||||
},
|
||||
{ "s28hl01gt", INFO(0x345a1b, 0, 256 * 1024, 512)
|
||||
PARSE_SFDP
|
||||
.fixups = &s28hx_t_fixups,
|
||||
},
|
||||
{ "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256)
|
||||
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_OCTAL_DTR_READ |
|
||||
SPI_NOR_OCTAL_DTR_PP)
|
||||
.fixups = &s28hs512t_fixups,
|
||||
PARSE_SFDP
|
||||
.fixups = &s28hx_t_fixups,
|
||||
},
|
||||
{ "s28hs01gt", INFO(0x345b1b, 0, 256 * 1024, 512)
|
||||
PARSE_SFDP
|
||||
.fixups = &s28hx_t_fixups,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -35,8 +35,10 @@ static ssize_t jedec_id_show(struct device *dev,
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct spi_mem *spimem = spi_get_drvdata(spi);
|
||||
struct spi_nor *nor = spi_mem_get_drvdata(spimem);
|
||||
const u8 *id = nor->info->id_len ? nor->info->id : nor->id;
|
||||
u8 id_len = nor->info->id_len ?: SPI_NOR_MAX_ID_LEN;
|
||||
|
||||
return sysfs_emit(buf, "%*phN\n", nor->info->id_len, nor->info->id);
|
||||
return sysfs_emit(buf, "%*phN\n", id_len, id);
|
||||
}
|
||||
static DEVICE_ATTR_RO(jedec_id);
|
||||
|
||||
@ -67,6 +69,21 @@ static struct bin_attribute *spi_nor_sysfs_bin_entries[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static umode_t spi_nor_sysfs_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(kobj_to_dev(kobj));
|
||||
struct spi_mem *spimem = spi_get_drvdata(spi);
|
||||
struct spi_nor *nor = spi_mem_get_drvdata(spimem);
|
||||
|
||||
if (attr == &dev_attr_manufacturer.attr && !nor->manufacturer)
|
||||
return 0;
|
||||
if (attr == &dev_attr_jedec_id.attr && !nor->info->id_len && !nor->id)
|
||||
return 0;
|
||||
|
||||
return 0444;
|
||||
}
|
||||
|
||||
static umode_t spi_nor_sysfs_is_bin_visible(struct kobject *kobj,
|
||||
struct bin_attribute *attr, int n)
|
||||
{
|
||||
@ -82,6 +99,7 @@ static umode_t spi_nor_sysfs_is_bin_visible(struct kobject *kobj,
|
||||
|
||||
static const struct attribute_group spi_nor_sysfs_group = {
|
||||
.name = "spi-nor",
|
||||
.is_visible = spi_nor_sysfs_is_visible,
|
||||
.is_bin_visible = spi_nor_sysfs_is_bin_visible,
|
||||
.attrs = spi_nor_sysfs_entries,
|
||||
.bin_attrs = spi_nor_sysfs_bin_entries,
|
||||
|
@ -133,6 +133,9 @@ static const struct flash_info winbond_nor_parts[] = {
|
||||
{ "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024)
|
||||
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ |
|
||||
SPI_NOR_DUAL_READ) },
|
||||
{ "w25q512nwq", INFO(0xef6020, 0, 0, 0)
|
||||
PARSE_SFDP
|
||||
OTP_INFO(256, 3, 0x1000, 0x1000) },
|
||||
{ "w25q512nwm", INFO(0xef8020, 0, 64 * 1024, 1024)
|
||||
PARSE_SFDP
|
||||
OTP_INFO(256, 3, 0x1000, 0x1000) },
|
||||
|
@ -349,6 +349,8 @@ struct spi_nor_flash_parameter;
|
||||
* @bouncebuf: bounce buffer used when the buffer passed by the MTD
|
||||
* layer is not DMA-able
|
||||
* @bouncebuf_size: size of the bounce buffer
|
||||
* @id: The flash's ID bytes. Always contains
|
||||
* SPI_NOR_MAX_ID_LEN bytes.
|
||||
* @info: SPI NOR part JEDEC MFR ID and other info
|
||||
* @manufacturer: SPI NOR manufacturer
|
||||
* @addr_nbytes: number of address bytes
|
||||
@ -379,6 +381,7 @@ struct spi_nor {
|
||||
struct spi_mem *spimem;
|
||||
u8 *bouncebuf;
|
||||
size_t bouncebuf_size;
|
||||
u8 *id;
|
||||
const struct flash_info *info;
|
||||
const struct spi_nor_manufacturer *manufacturer;
|
||||
u8 addr_nbytes;
|
||||
|
Loading…
Reference in New Issue
Block a user