Commit 74cae210 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mtd/for-5.20' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux

Pull MTD updates from Richard Weinberger:
 "MTD core changes:

   - Dynamic partition support

   - Fix deadlock in sm_ftl

   - Various refcount fixes in maps, partitions and parser code

   - Integer overflow fixes in mtdchar

   - Support for Sercomm partitions

  NAND driver changes:

   - Clockrate fix for arasan

   - Add ATO25D1GA support

   - Double free fix for meson driver

   - Fix probe/remove methods in cafe NAND

   - Support unprotected spare data pages in qcom_nandc

  SPI NOR core changes:

   - move SECT_4K_PMC flag out of the core as it's a vendor specific
     flag

   - s/addr_width/addr_nbytes/g: address width means the number of IO
     lines used for the address, whereas in the code it is used as the
     number of address bytes.

   - do not change nor->addr_nbytes at SFDP parsing time. At the SFDP
     parsing time we should not change members of struct spi_nor, but
     instead fill members of struct spi_nor_flash_parameters which could
     later on be used by the callers.

   - track flash's internal address mode so that we can use 4B opcodes
     together with opcodes that don't have a 4B opcode correspondent.

  SPI NOR manufacturer drivers changes:

   - esmt: Rename "f25l32qa" flash name to "f25l32qa-2s".

   - micron-st: Skip FSR reading if SPI controller does not support it
     to allow flashes that support FSR to work even when attached to
     such SPI controllers.

   - spansion: Add s25hl-t/s25hs-t IDs and fixups"

* tag 'mtd/for-5.20' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (53 commits)
  mtd: core: check partition before dereference
  mtd: spi-nor: fix spi_nor_spimem_setup_op() call in spi_nor_erase_{sector,chip}()
  mtd: spi-nor: spansion: Add s25hl-t/s25hs-t IDs and fixups
  mtd: spi-nor: spansion: Add local function to discover page size
  mtd: spi-nor: core: Track flash's internal address mode
  mtd: spi-nor: core: Return error code from set_4byte_addr_mode()
  mtd: spi-nor: Do not change nor->addr_nbytes at SFDP parsing time
  mtd: spi-nor: core: Shrink the storage size of the flash_info's addr_nbytes
  mtd: spi-nor: s/addr_width/addr_nbytes
  mtd: spi-nor: esmt: Use correct name of f25l32qa
  mtd: spi-nor: micron-st: Skip FSR reading if SPI controller does not support it
  MAINTAINERS: Use my kernel.org email
  mtd: rawnand: arasan: Fix clock rate in NV-DDR
  mtd: rawnand: arasan: Update NAND bus clock instead of system clock
  mtd: core: introduce of support for dynamic partitions
  dt-bindings: mtd: partitions: add additional example for qcom,smem-part
  dt-bindings: mtd: partitions: support label/name only partition
  mtd: spi-nor: move SECT_4K_PMC special handling
  mtd: dataflash: Add SPI ID table
  mtd: hyperbus: rpc-if: Fix RPM imbalance in probe error path
  ...
parents 79b7e67b 7ec4cdb3
...@@ -37,6 +37,4 @@ examples: ...@@ -37,6 +37,4 @@ examples:
compatible = "fsl,imx27-nand"; compatible = "fsl,imx27-nand";
reg = <0xd8000000 0x1000>; reg = <0xd8000000 0x1000>;
interrupts = <29>; interrupts = <29>;
nand-bus-width = <8>;
nand-ecc-mode = "hw";
}; };
...@@ -11,6 +11,17 @@ description: | ...@@ -11,6 +11,17 @@ description: |
relative offset and size specified. Depending on partition function extra relative offset and size specified. Depending on partition function extra
properties can be used. properties can be used.
A partition may be dynamically allocated by a specific parser at runtime.
In this specific case, a specific suffix is required to the node name.
Everything after 'partition-' will be used as the partition name to compare
with the one dynamically allocated by the specific parser.
If the partition contains invalid char a label can be provided that will
be used instead of the node name to make the comparison.
This is used to assign an OF node to the dynamiccally allocated partition
so that subsystem like NVMEM can provide an OF node and declare NVMEM cells.
The OF node will be assigned only if the partition label declared match the
one assigned by the parser at runtime.
maintainers: maintainers:
- Rafał Miłecki <rafal@milecki.pl> - Rafał Miłecki <rafal@milecki.pl>
...@@ -41,7 +52,12 @@ properties: ...@@ -41,7 +52,12 @@ properties:
immune to paired-pages corruptions immune to paired-pages corruptions
type: boolean type: boolean
required: if:
- reg not:
required: [ reg ]
then:
properties:
$nodename:
pattern: '^partition-.*$'
additionalProperties: true additionalProperties: true
...@@ -19,6 +19,10 @@ properties: ...@@ -19,6 +19,10 @@ properties:
compatible: compatible:
const: qcom,smem-part const: qcom,smem-part
patternProperties:
"^partition-[0-9a-z]+$":
$ref: partition.yaml#
required: required:
- compatible - compatible
...@@ -31,3 +35,26 @@ examples: ...@@ -31,3 +35,26 @@ examples:
compatible = "qcom,smem-part"; compatible = "qcom,smem-part";
}; };
}; };
- |
/* Example declaring dynamic partition */
flash {
partitions {
compatible = "qcom,smem-part";
partition-art {
compatible = "nvmem-cells";
#address-cells = <1>;
#size-cells = <1>;
label = "0:art";
macaddr_art_0: macaddr@0 {
reg = <0x0 0x6>;
};
macaddr_art_6: macaddr@6 {
reg = <0x6 0x6>;
};
};
};
};
...@@ -102,6 +102,31 @@ allOf: ...@@ -102,6 +102,31 @@ allOf:
- const: rx - const: rx
- const: cmd - const: cmd
- if:
properties:
compatible:
contains:
enum:
- qcom,ipq806x-nand
then:
properties:
qcom,boot-partitions:
$ref: /schemas/types.yaml#/definitions/uint32-matrix
items:
items:
- description: offset
- description: size
description:
Boot partition use a different layout where the 4 bytes of spare
data are not protected by ECC. Use this to declare these special
partitions by defining first the offset and then the size.
It's in the form of <offset1 size1 offset2 size2 offset3 ...>
and should be declared in ascending order.
Refer to the ipq8064 example on how to use this special binding.
required: required:
- compatible - compatible
- reg - reg
...@@ -135,6 +160,8 @@ examples: ...@@ -135,6 +160,8 @@ examples:
nand-ecc-strength = <4>; nand-ecc-strength = <4>;
nand-bus-width = <8>; nand-bus-width = <8>;
qcom,boot-partitions = <0x0 0x58a0000>;
partitions { partitions {
compatible = "fixed-partitions"; compatible = "fixed-partitions";
#address-cells = <1>; #address-cells = <1>;
......
...@@ -19130,7 +19130,7 @@ F: drivers/pinctrl/spear/ ...@@ -19130,7 +19130,7 @@ F: drivers/pinctrl/spear/
SPI NOR SUBSYSTEM SPI NOR SUBSYSTEM
M: Tudor Ambarus <tudor.ambarus@microchip.com> M: Tudor Ambarus <tudor.ambarus@microchip.com>
M: Pratyush Yadav <p.yadav@ti.com> M: Pratyush Yadav <pratyush@kernel.org>
R: Michael Walle <michael@walle.cc> R: Michael Walle <michael@walle.cc>
L: linux-mtd@lists.infradead.org L: linux-mtd@lists.infradead.org
S: Maintained S: Maintained
......
...@@ -112,6 +112,13 @@ static const struct of_device_id dataflash_dt_ids[] = { ...@@ -112,6 +112,13 @@ static const struct of_device_id dataflash_dt_ids[] = {
MODULE_DEVICE_TABLE(of, dataflash_dt_ids); MODULE_DEVICE_TABLE(of, dataflash_dt_ids);
#endif #endif
static const struct spi_device_id dataflash_spi_ids[] = {
{ .name = "at45", },
{ .name = "dataflash", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(spi, dataflash_spi_ids);
/* ......................................................................... */ /* ......................................................................... */
/* /*
...@@ -936,6 +943,7 @@ static struct spi_driver dataflash_driver = { ...@@ -936,6 +943,7 @@ static struct spi_driver dataflash_driver = {
.probe = dataflash_probe, .probe = dataflash_probe,
.remove = dataflash_remove, .remove = dataflash_remove,
.id_table = dataflash_spi_ids,
/* FIXME: investigate suspend and resume... */ /* FIXME: investigate suspend and resume... */
}; };
......
...@@ -270,7 +270,9 @@ static int powernv_flash_release(struct platform_device *pdev) ...@@ -270,7 +270,9 @@ static int powernv_flash_release(struct platform_device *pdev)
struct powernv_flash *data = dev_get_drvdata(&(pdev->dev)); struct powernv_flash *data = dev_get_drvdata(&(pdev->dev));
/* All resources should be freed automatically */ /* All resources should be freed automatically */
return mtd_device_unregister(&(data->mtd)); WARN_ON(mtd_device_unregister(&data->mtd));
return 0;
} }
static const struct of_device_id powernv_flash_match[] = { static const struct of_device_id powernv_flash_match[] = {
......
...@@ -1045,13 +1045,9 @@ static int spear_smi_remove(struct platform_device *pdev) ...@@ -1045,13 +1045,9 @@ static int spear_smi_remove(struct platform_device *pdev)
{ {
struct spear_smi *dev; struct spear_smi *dev;
struct spear_snor_flash *flash; struct spear_snor_flash *flash;
int ret, i; int i;
dev = platform_get_drvdata(pdev); dev = platform_get_drvdata(pdev);
if (!dev) {
dev_err(&pdev->dev, "dev is null\n");
return -ENODEV;
}
/* clean up for all nor flash */ /* clean up for all nor flash */
for (i = 0; i < dev->num_flashes; i++) { for (i = 0; i < dev->num_flashes; i++) {
...@@ -1060,9 +1056,7 @@ static int spear_smi_remove(struct platform_device *pdev) ...@@ -1060,9 +1056,7 @@ static int spear_smi_remove(struct platform_device *pdev)
continue; continue;
/* clean up mtd stuff */ /* clean up mtd stuff */
ret = mtd_device_unregister(&flash->mtd); WARN_ON(mtd_device_unregister(&flash->mtd));
if (ret)
dev_err(&pdev->dev, "error removing mtd\n");
} }
clk_disable_unprepare(dev->clk); clk_disable_unprepare(dev->clk);
......
...@@ -2084,15 +2084,12 @@ static int stfsm_probe(struct platform_device *pdev) ...@@ -2084,15 +2084,12 @@ static int stfsm_probe(struct platform_device *pdev)
* Configure READ/WRITE/ERASE sequences according to platform and * Configure READ/WRITE/ERASE sequences according to platform and
* device flags. * device flags.
*/ */
if (info->config) { if (info->config)
ret = info->config(fsm); ret = info->config(fsm);
if (ret) else
goto err_clk_unprepare;
} else {
ret = stfsm_prepare_rwe_seqs_default(fsm); ret = stfsm_prepare_rwe_seqs_default(fsm);
if (ret) if (ret)
goto err_clk_unprepare; goto err_clk_unprepare;
}
fsm->mtd.name = info->name; fsm->mtd.name = info->name;
fsm->mtd.dev.parent = &pdev->dev; fsm->mtd.dev.parent = &pdev->dev;
...@@ -2115,10 +2112,12 @@ static int stfsm_probe(struct platform_device *pdev) ...@@ -2115,10 +2112,12 @@ static int stfsm_probe(struct platform_device *pdev)
(long long)fsm->mtd.size, (long long)(fsm->mtd.size >> 20), (long long)fsm->mtd.size, (long long)(fsm->mtd.size >> 20),
fsm->mtd.erasesize, (fsm->mtd.erasesize >> 10)); fsm->mtd.erasesize, (fsm->mtd.erasesize >> 10));
return mtd_device_register(&fsm->mtd, NULL, 0); ret = mtd_device_register(&fsm->mtd, NULL, 0);
if (ret) {
err_clk_unprepare: err_clk_unprepare:
clk_disable_unprepare(fsm->clk); clk_disable_unprepare(fsm->clk);
}
return ret; return ret;
} }
...@@ -2126,9 +2125,11 @@ static int stfsm_remove(struct platform_device *pdev) ...@@ -2126,9 +2125,11 @@ static int stfsm_remove(struct platform_device *pdev)
{ {
struct stfsm *fsm = platform_get_drvdata(pdev); struct stfsm *fsm = platform_get_drvdata(pdev);
WARN_ON(mtd_device_unregister(&fsm->mtd));
clk_disable_unprepare(fsm->clk); clk_disable_unprepare(fsm->clk);
return mtd_device_unregister(&fsm->mtd); return 0;
} }
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
......
...@@ -233,16 +233,16 @@ static int am654_hbmc_remove(struct platform_device *pdev) ...@@ -233,16 +233,16 @@ static int am654_hbmc_remove(struct platform_device *pdev)
{ {
struct am654_hbmc_priv *priv = platform_get_drvdata(pdev); struct am654_hbmc_priv *priv = platform_get_drvdata(pdev);
struct am654_hbmc_device_priv *dev_priv = priv->hbdev.priv; struct am654_hbmc_device_priv *dev_priv = priv->hbdev.priv;
int ret;
ret = hyperbus_unregister_device(&priv->hbdev); hyperbus_unregister_device(&priv->hbdev);
if (priv->mux_ctrl) if (priv->mux_ctrl)
mux_control_deselect(priv->mux_ctrl); mux_control_deselect(priv->mux_ctrl);
if (dev_priv->rx_chan) if (dev_priv->rx_chan)
dma_release_channel(dev_priv->rx_chan); dma_release_channel(dev_priv->rx_chan);
return ret; return 0;
} }
static const struct of_device_id am654_hbmc_dt_ids[] = { static const struct of_device_id am654_hbmc_dt_ids[] = {
......
...@@ -126,16 +126,12 @@ int hyperbus_register_device(struct hyperbus_device *hbdev) ...@@ -126,16 +126,12 @@ int hyperbus_register_device(struct hyperbus_device *hbdev)
} }
EXPORT_SYMBOL_GPL(hyperbus_register_device); EXPORT_SYMBOL_GPL(hyperbus_register_device);
int hyperbus_unregister_device(struct hyperbus_device *hbdev) void hyperbus_unregister_device(struct hyperbus_device *hbdev)
{ {
int ret = 0;
if (hbdev && hbdev->mtd) { if (hbdev && hbdev->mtd) {
ret = mtd_device_unregister(hbdev->mtd); WARN_ON(mtd_device_unregister(hbdev->mtd));
map_destroy(hbdev->mtd); map_destroy(hbdev->mtd);
} }
return ret;
} }
EXPORT_SYMBOL_GPL(hyperbus_unregister_device); EXPORT_SYMBOL_GPL(hyperbus_unregister_device);
......
...@@ -134,7 +134,7 @@ static int rpcif_hb_probe(struct platform_device *pdev) ...@@ -134,7 +134,7 @@ static int rpcif_hb_probe(struct platform_device *pdev)
error = rpcif_hw_init(&hyperbus->rpc, true); error = rpcif_hw_init(&hyperbus->rpc, true);
if (error) if (error)
return error; goto out_disable_rpm;
hyperbus->hbdev.map.size = hyperbus->rpc.size; hyperbus->hbdev.map.size = hyperbus->rpc.size;
hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap; hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap;
...@@ -145,19 +145,24 @@ static int rpcif_hb_probe(struct platform_device *pdev) ...@@ -145,19 +145,24 @@ static int rpcif_hb_probe(struct platform_device *pdev)
hyperbus->hbdev.np = of_get_next_child(pdev->dev.parent->of_node, NULL); hyperbus->hbdev.np = of_get_next_child(pdev->dev.parent->of_node, NULL);
error = hyperbus_register_device(&hyperbus->hbdev); error = hyperbus_register_device(&hyperbus->hbdev);
if (error) if (error)
rpcif_disable_rpm(&hyperbus->rpc); goto out_disable_rpm;
return 0;
out_disable_rpm:
rpcif_disable_rpm(&hyperbus->rpc);
return error; return error;
} }
static int rpcif_hb_remove(struct platform_device *pdev) static int rpcif_hb_remove(struct platform_device *pdev)
{ {
struct rpcif_hyperbus *hyperbus = platform_get_drvdata(pdev); struct rpcif_hyperbus *hyperbus = platform_get_drvdata(pdev);
int error = hyperbus_unregister_device(&hyperbus->hbdev);
hyperbus_unregister_device(&hyperbus->hbdev);
rpcif_disable_rpm(&hyperbus->rpc); rpcif_disable_rpm(&hyperbus->rpc);
return error; return 0;
} }
static struct platform_driver rpcif_platform_driver = { static struct platform_driver rpcif_platform_driver = {
......
...@@ -478,7 +478,9 @@ static int lpddr2_nvm_probe(struct platform_device *pdev) ...@@ -478,7 +478,9 @@ static int lpddr2_nvm_probe(struct platform_device *pdev)
*/ */
static int lpddr2_nvm_remove(struct platform_device *pdev) static int lpddr2_nvm_remove(struct platform_device *pdev)
{ {
return mtd_device_unregister(dev_get_drvdata(&pdev->dev)); WARN_ON(mtd_device_unregister(dev_get_drvdata(&pdev->dev)));
return 0;
} }
/* Initialize platform_driver data structure for lpddr2_nvm */ /* Initialize platform_driver data structure for lpddr2_nvm */
......
...@@ -66,18 +66,12 @@ static int physmap_flash_remove(struct platform_device *dev) ...@@ -66,18 +66,12 @@ static int physmap_flash_remove(struct platform_device *dev)
{ {
struct physmap_flash_info *info; struct physmap_flash_info *info;
struct physmap_flash_data *physmap_data; struct physmap_flash_data *physmap_data;
int i, err = 0; int i;
info = platform_get_drvdata(dev); info = platform_get_drvdata(dev);
if (!info) {
err = -EINVAL;
goto out;
}
if (info->cmtd) { if (info->cmtd) {
err = mtd_device_unregister(info->cmtd); WARN_ON(mtd_device_unregister(info->cmtd));
if (err)
goto out;
if (info->cmtd != info->mtds[0]) if (info->cmtd != info->mtds[0])
mtd_concat_destroy(info->cmtd); mtd_concat_destroy(info->cmtd);
...@@ -92,10 +86,9 @@ static int physmap_flash_remove(struct platform_device *dev) ...@@ -92,10 +86,9 @@ static int physmap_flash_remove(struct platform_device *dev)
if (physmap_data && physmap_data->exit) if (physmap_data && physmap_data->exit)
physmap_data->exit(dev); physmap_data->exit(dev);
out:
pm_runtime_put(&dev->dev); pm_runtime_put(&dev->dev);
pm_runtime_disable(&dev->dev); pm_runtime_disable(&dev->dev);
return err; return 0;
} }
static void physmap_set_vpp(struct map_info *map, int state) static void physmap_set_vpp(struct map_info *map, int state)
......
...@@ -93,6 +93,7 @@ static int ap_flash_init(struct platform_device *pdev) ...@@ -93,6 +93,7 @@ static int ap_flash_init(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
} }
ebi_base = of_iomap(ebi, 0); ebi_base = of_iomap(ebi, 0);
of_node_put(ebi);
if (!ebi_base) if (!ebi_base)
return -ENODEV; return -ENODEV;
...@@ -207,6 +208,7 @@ int of_flash_probe_versatile(struct platform_device *pdev, ...@@ -207,6 +208,7 @@ int of_flash_probe_versatile(struct platform_device *pdev,
versatile_flashprot = (enum versatile_flashprot)devid->data; versatile_flashprot = (enum versatile_flashprot)devid->data;
rmap = syscon_node_to_regmap(sysnp); rmap = syscon_node_to_regmap(sysnp);
of_node_put(sysnp);
if (IS_ERR(rmap)) if (IS_ERR(rmap))
return PTR_ERR(rmap); return PTR_ERR(rmap);
......
...@@ -615,21 +615,24 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, ...@@ -615,21 +615,24 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
if (!usr_oob) if (!usr_oob)
req.ooblen = 0; req.ooblen = 0;
req.len &= 0xffffffff;
req.ooblen &= 0xffffffff;
if (req.start + req.len > mtd->size) if (req.start + req.len > mtd->size)
return -EINVAL; return -EINVAL;
datbuf_len = min_t(size_t, req.len, mtd->erasesize); datbuf_len = min_t(size_t, req.len, mtd->erasesize);
if (datbuf_len > 0) { if (datbuf_len > 0) {
datbuf = kmalloc(datbuf_len, GFP_KERNEL); datbuf = kvmalloc(datbuf_len, GFP_KERNEL);
if (!datbuf) if (!datbuf)
return -ENOMEM; return -ENOMEM;
} }
oobbuf_len = min_t(size_t, req.ooblen, mtd->erasesize); oobbuf_len = min_t(size_t, req.ooblen, mtd->erasesize);
if (oobbuf_len > 0) { if (oobbuf_len > 0) {
oobbuf = kmalloc(oobbuf_len, GFP_KERNEL); oobbuf = kvmalloc(oobbuf_len, GFP_KERNEL);
if (!oobbuf) { if (!oobbuf) {
kfree(datbuf); kvfree(datbuf);
return -ENOMEM; return -ENOMEM;
} }
} }
...@@ -679,8 +682,8 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, ...@@ -679,8 +682,8 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
usr_oob += ops.oobretlen; usr_oob += ops.oobretlen;
} }
kfree(datbuf); kvfree(datbuf);
kfree(oobbuf); kvfree(oobbuf);
return ret; return ret;
} }
......
...@@ -546,6 +546,68 @@ static int mtd_nvmem_add(struct mtd_info *mtd) ...@@ -546,6 +546,68 @@ static int mtd_nvmem_add(struct mtd_info *mtd)
return 0; return 0;
} }
static void mtd_check_of_node(struct mtd_info *mtd)
{
struct device_node *partitions, *parent_dn, *mtd_dn = NULL;
const char *pname, *prefix = "partition-";
int plen, mtd_name_len, offset, prefix_len;
struct mtd_info *parent;
bool found = false;
/* Check if MTD already has a device node */
if (dev_of_node(&mtd->dev))
return;
/* Check if a partitions node exist */
if (!mtd_is_partition(mtd))
return;
parent = mtd->parent;
parent_dn = dev_of_node(&parent->dev);
if (!parent_dn)
return;
partitions = of_get_child_by_name(parent_dn, "partitions");
if (!partitions)
goto exit_parent;
prefix_len = strlen(prefix);
mtd_name_len = strlen(mtd->name);
/* Search if a partition is defined with the same name */
for_each_child_of_node(partitions, mtd_dn) {
offset = 0;
/* Skip partition with no/wrong prefix */
if (!of_node_name_prefix(mtd_dn, "partition-"))
continue;
/* Label have priority. Check that first */
if (of_property_read_string(mtd_dn, "label", &pname)) {
of_property_read_string(mtd_dn, "name", &pname);
offset = prefix_len;
}
plen = strlen(pname) - offset;
if (plen == mtd_name_len &&
!strncmp(mtd->name, pname + offset, plen)) {
found = true;
break;
}
}
if (!found)
goto exit_partitions;
/* Set of_node only for nvmem */
if (of_device_is_compatible(mtd_dn, "nvmem-cells"))
mtd_set_of_node(mtd, mtd_dn);
exit_partitions:
of_node_put(partitions);
exit_parent:
of_node_put(parent_dn);
}
/** /**
* add_mtd_device - register an MTD device * add_mtd_device - register an MTD device
* @mtd: pointer to new MTD device info structure * @mtd: pointer to new MTD device info structure
...@@ -658,6 +720,7 @@ int add_mtd_device(struct mtd_info *mtd) ...@@ -658,6 +720,7 @@ int add_mtd_device(struct mtd_info *mtd)
mtd->dev.devt = MTD_DEVT(i); mtd->dev.devt = MTD_DEVT(i);
dev_set_name(&mtd->dev, "mtd%d", i); dev_set_name(&mtd->dev, "mtd%d", i);
dev_set_drvdata(&mtd->dev, mtd); dev_set_drvdata(&mtd->dev, mtd);
mtd_check_of_node(mtd);
of_node_get(mtd_get_of_node(mtd)); of_node_get(mtd_get_of_node(mtd));
error = device_register(&mtd->dev); error = device_register(&mtd->dev);
if (error) if (error)
......
...@@ -347,17 +347,17 @@ static int anfc_select_target(struct nand_chip *chip, int target) ...@@ -347,17 +347,17 @@ static int anfc_select_target(struct nand_chip *chip, int target)
/* Update clock frequency */ /* Update clock frequency */
if (nfc->cur_clk != anand->clk) { if (nfc->cur_clk != anand->clk) {
clk_disable_unprepare(nfc->controller_clk); clk_disable_unprepare(nfc->bus_clk);
ret = clk_set_rate(nfc->controller_clk, anand->clk); ret = clk_set_rate(nfc->bus_clk, anand->clk);
if (ret) { if (ret) {
dev_err(nfc->dev, "Failed to change clock rate\n"); dev_err(nfc->dev, "Failed to change clock rate\n");
return ret; return ret;
} }
ret = clk_prepare_enable(nfc->controller_clk); ret = clk_prepare_enable(nfc->bus_clk);
if (ret) { if (ret) {
dev_err(nfc->dev, dev_err(nfc->dev,
"Failed to re-enable the controller clock\n"); "Failed to re-enable the bus clock\n");
return ret; return ret;
} }
...@@ -1043,7 +1043,13 @@ static int anfc_setup_interface(struct nand_chip *chip, int target, ...@@ -1043,7 +1043,13 @@ static int anfc_setup_interface(struct nand_chip *chip, int target,
DQS_BUFF_SEL_OUT(dqs_mode); DQS_BUFF_SEL_OUT(dqs_mode);
} }
anand->clk = ANFC_XLNX_SDR_DFLT_CORE_CLK; if (nand_interface_is_sdr(conf)) {
anand->clk = ANFC_XLNX_SDR_DFLT_CORE_CLK;
} else {
/* ONFI timings are defined in picoseconds */
anand->clk = div_u64((u64)NSEC_PER_SEC * 1000,
conf->timings.nvddr.tCK_min);
}
/* /*
* Due to a hardware bug in the ZynqMP SoC, SDR timing modes 0-1 work * Due to a hardware bug in the ZynqMP SoC, SDR timing modes 0-1 work
......
...@@ -2629,7 +2629,9 @@ static int atmel_nand_controller_remove(struct platform_device *pdev) ...@@ -2629,7 +2629,9 @@ static int atmel_nand_controller_remove(struct platform_device *pdev)
{ {
struct atmel_nand_controller *nc = platform_get_drvdata(pdev); struct atmel_nand_controller *nc = platform_get_drvdata(pdev);
return nc->caps->ops->remove(nc); WARN_ON(nc->caps->ops->remove(nc));
return 0;
} }
static __maybe_unused int atmel_nand_controller_resume(struct device *dev) static __maybe_unused int atmel_nand_controller_resume(struct device *dev)
......
...@@ -679,8 +679,10 @@ static int cafe_nand_probe(struct pci_dev *pdev, ...@@ -679,8 +679,10 @@ static int cafe_nand_probe(struct pci_dev *pdev,
pci_set_master(pdev); pci_set_master(pdev);
cafe = kzalloc(sizeof(*cafe), GFP_KERNEL); cafe = kzalloc(sizeof(*cafe), GFP_KERNEL);
if (!cafe) if (!cafe) {
return -ENOMEM; err = -ENOMEM;
goto out_disable_device;
}
mtd = nand_to_mtd(&cafe->nand); mtd = nand_to_mtd(&cafe->nand);
mtd->dev.parent = &pdev->dev; mtd->dev.parent = &pdev->dev;
...@@ -801,6 +803,8 @@ static int cafe_nand_probe(struct pci_dev *pdev, ...@@ -801,6 +803,8 @@ static int cafe_nand_probe(struct pci_dev *pdev,
pci_iounmap(pdev, cafe->mmio); pci_iounmap(pdev, cafe->mmio);
out_free_mtd: out_free_mtd:
kfree(cafe); kfree(cafe);
out_disable_device:
pci_disable_device(pdev);
out: out:
return err; return err;
} }
...@@ -822,6 +826,7 @@ static void cafe_nand_remove(struct pci_dev *pdev) ...@@ -822,6 +826,7 @@ static void cafe_nand_remove(struct pci_dev *pdev)
pci_iounmap(pdev, cafe->mmio); pci_iounmap(pdev, cafe->mmio);
dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
kfree(cafe); kfree(cafe);
pci_disable_device(pdev);
} }
static const struct pci_device_id cafe_nand_tbl[] = { static const struct pci_device_id cafe_nand_tbl[] = {
......
...@@ -1293,26 +1293,20 @@ meson_nfc_nand_chip_init(struct device *dev, ...@@ -1293,26 +1293,20 @@ meson_nfc_nand_chip_init(struct device *dev,
return 0; return 0;
} }
static int meson_nfc_nand_chip_cleanup(struct meson_nfc *nfc) static void meson_nfc_nand_chip_cleanup(struct meson_nfc *nfc)
{ {
struct meson_nfc_nand_chip *meson_chip; struct meson_nfc_nand_chip *meson_chip;
struct mtd_info *mtd; struct mtd_info *mtd;
int ret;
while (!list_empty(&nfc->chips)) { while (!list_empty(&nfc->chips)) {
meson_chip = list_first_entry(&nfc->chips, meson_chip = list_first_entry(&nfc->chips,
struct meson_nfc_nand_chip, node); struct meson_nfc_nand_chip, node);
mtd = nand_to_mtd(&meson_chip->nand); mtd = nand_to_mtd(&meson_chip->nand);
ret = mtd_device_unregister(mtd); WARN_ON(mtd_device_unregister(mtd));
if (ret)
return ret;
meson_nfc_free_buffer(&meson_chip->nand);
nand_cleanup(&meson_chip->nand); nand_cleanup(&meson_chip->nand);
list_del(&meson_chip->node); list_del(&meson_chip->node);
} }
return 0;
} }
static int meson_nfc_nand_chips_init(struct device *dev, static int meson_nfc_nand_chips_init(struct device *dev,
...@@ -1445,16 +1439,11 @@ static int meson_nfc_probe(struct platform_device *pdev) ...@@ -1445,16 +1439,11 @@ static int meson_nfc_probe(struct platform_device *pdev)
static int meson_nfc_remove(struct platform_device *pdev) static int meson_nfc_remove(struct platform_device *pdev)
{ {
struct meson_nfc *nfc = platform_get_drvdata(pdev); struct meson_nfc *nfc = platform_get_drvdata(pdev);
int ret;
ret = meson_nfc_nand_chip_cleanup(nfc); meson_nfc_nand_chip_cleanup(nfc);
if (ret)
return ret;
meson_nfc_disable_clk(nfc); meson_nfc_disable_clk(nfc);
platform_set_drvdata(pdev, NULL);
return 0; return 0;
} }
......
...@@ -2278,16 +2278,14 @@ static int omap_nand_remove(struct platform_device *pdev) ...@@ -2278,16 +2278,14 @@ static int omap_nand_remove(struct platform_device *pdev)
struct mtd_info *mtd = platform_get_drvdata(pdev); struct mtd_info *mtd = platform_get_drvdata(pdev);
struct nand_chip *nand_chip = mtd_to_nand(mtd); struct nand_chip *nand_chip = mtd_to_nand(mtd);
struct omap_nand_info *info = mtd_to_omap(mtd); struct omap_nand_info *info = mtd_to_omap(mtd);
int ret;
rawnand_sw_bch_cleanup(nand_chip); rawnand_sw_bch_cleanup(nand_chip);
if (info->dma) if (info->dma)
dma_release_channel(info->dma); dma_release_channel(info->dma);
ret = mtd_device_unregister(mtd); WARN_ON(mtd_device_unregister(mtd));
WARN_ON(ret);
nand_cleanup(nand_chip); nand_cleanup(nand_chip);
return ret; return 0;
} }
/* omap_nand_ids defined in linux/platform_data/mtd-nand-omap2.h */ /* omap_nand_ids defined in linux/platform_data/mtd-nand-omap2.h */
......
...@@ -80,8 +80,10 @@ ...@@ -80,8 +80,10 @@
#define DISABLE_STATUS_AFTER_WRITE 4 #define DISABLE_STATUS_AFTER_WRITE 4
#define CW_PER_PAGE 6 #define CW_PER_PAGE 6
#define UD_SIZE_BYTES 9 #define UD_SIZE_BYTES 9
#define UD_SIZE_BYTES_MASK GENMASK(18, 9)
#define ECC_PARITY_SIZE_BYTES_RS 19 #define ECC_PARITY_SIZE_BYTES_RS 19
#define SPARE_SIZE_BYTES 23 #define SPARE_SIZE_BYTES 23
#define SPARE_SIZE_BYTES_MASK GENMASK(26, 23)
#define NUM_ADDR_CYCLES 27 #define NUM_ADDR_CYCLES 27
#define STATUS_BFR_READ 30 #define STATUS_BFR_READ 30
#define SET_RD_MODE_AFTER_STATUS 31 #define SET_RD_MODE_AFTER_STATUS 31
...@@ -102,6 +104,7 @@ ...@@ -102,6 +104,7 @@
#define ECC_MODE 4 #define ECC_MODE 4
#define ECC_PARITY_SIZE_BYTES_BCH 8 #define ECC_PARITY_SIZE_BYTES_BCH 8
#define ECC_NUM_DATA_BYTES 16 #define ECC_NUM_DATA_BYTES 16
#define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16)
#define ECC_FORCE_CLK_OPEN 30 #define ECC_FORCE_CLK_OPEN 30
/* NAND_DEV_CMD1 bits */ /* NAND_DEV_CMD1 bits */
...@@ -238,6 +241,9 @@ nandc_set_reg(chip, reg, \ ...@@ -238,6 +241,9 @@ nandc_set_reg(chip, reg, \
* @bam_ce - the array of BAM command elements * @bam_ce - the array of BAM command elements
* @cmd_sgl - sgl for NAND BAM command pipe * @cmd_sgl - sgl for NAND BAM command pipe
* @data_sgl - sgl for NAND BAM consumer/producer pipe * @data_sgl - sgl for NAND BAM consumer/producer pipe
* @last_data_desc - last DMA desc in data channel (tx/rx).
* @last_cmd_desc - last DMA desc in command channel.
* @txn_done - completion for NAND transfer.
* @bam_ce_pos - the index in bam_ce which is available for next sgl * @bam_ce_pos - the index in bam_ce which is available for next sgl
* @bam_ce_start - the index in bam_ce which marks the start position ce * @bam_ce_start - the index in bam_ce which marks the start position ce
* for current sgl. It will be used for size calculation * for current sgl. It will be used for size calculation
...@@ -250,14 +256,14 @@ nandc_set_reg(chip, reg, \ ...@@ -250,14 +256,14 @@ nandc_set_reg(chip, reg, \
* @rx_sgl_start - start index in data sgl for rx. * @rx_sgl_start - start index in data sgl for rx.
* @wait_second_completion - wait for second DMA desc completion before making * @wait_second_completion - wait for second DMA desc completion before making
* the NAND transfer completion. * the NAND transfer completion.
* @txn_done - completion for NAND transfer.
* @last_data_desc - last DMA desc in data channel (tx/rx).
* @last_cmd_desc - last DMA desc in command channel.
*/ */
struct bam_transaction { struct bam_transaction {
struct bam_cmd_element *bam_ce; struct bam_cmd_element *bam_ce;
struct scatterlist *cmd_sgl; struct scatterlist *cmd_sgl;
struct scatterlist *data_sgl; struct scatterlist *data_sgl;
struct dma_async_tx_descriptor *last_data_desc;
struct dma_async_tx_descriptor *last_cmd_desc;
struct completion txn_done;
u32 bam_ce_pos; u32 bam_ce_pos;
u32 bam_ce_start; u32 bam_ce_start;
u32 cmd_sgl_pos; u32 cmd_sgl_pos;
...@@ -267,25 +273,23 @@ struct bam_transaction { ...@@ -267,25 +273,23 @@ struct bam_transaction {
u32 rx_sgl_pos; u32 rx_sgl_pos;
u32 rx_sgl_start; u32 rx_sgl_start;
bool wait_second_completion; bool wait_second_completion;
struct completion txn_done;
struct dma_async_tx_descriptor *last_data_desc;
struct dma_async_tx_descriptor *last_cmd_desc;
}; };
/* /*
* This data type corresponds to the nand dma descriptor * This data type corresponds to the nand dma descriptor
* @dma_desc - low level DMA engine descriptor
* @list - list for desc_info * @list - list for desc_info
* @dir - DMA transfer direction *
* @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by
* ADM * ADM
* @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM
* @sgl_cnt - number of SGL in bam_sgl. Only used by BAM * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM
* @dma_desc - low level DMA engine descriptor * @dir - DMA transfer direction
*/ */
struct desc_info { struct desc_info {
struct dma_async_tx_descriptor *dma_desc;
struct list_head node; struct list_head node;
enum dma_data_direction dir;
union { union {
struct scatterlist adm_sgl; struct scatterlist adm_sgl;
struct { struct {
...@@ -293,7 +297,7 @@ struct desc_info { ...@@ -293,7 +297,7 @@ struct desc_info {
int sgl_cnt; int sgl_cnt;
}; };
}; };
struct dma_async_tx_descriptor *dma_desc; enum dma_data_direction dir;
}; };
/* /*
...@@ -337,52 +341,64 @@ struct nandc_regs { ...@@ -337,52 +341,64 @@ struct nandc_regs {
/* /*
* NAND controller data struct * NAND controller data struct
* *
* @controller: base controller structure
* @host_list: list containing all the chips attached to the
* controller
* @dev: parent device * @dev: parent device
*
* @base: MMIO base * @base: MMIO base
* @base_phys: physical base address of controller registers *
* @base_dma: dma base address of controller registers
* @core_clk: controller clock * @core_clk: controller clock
* @aon_clk: another controller clock * @aon_clk: another controller clock
* *
* @regs: a contiguous chunk of memory for DMA register
* writes. contains the register values to be
* written to controller
*
* @props: properties of current NAND controller,
* initialized via DT match data
*
* @controller: base controller structure
* @host_list: list containing all the chips attached to the
* controller
*
* @chan: dma channel * @chan: dma channel
* @cmd_crci: ADM DMA CRCI for command flow control * @cmd_crci: ADM DMA CRCI for command flow control
* @data_crci: ADM DMA CRCI for data flow control * @data_crci: ADM DMA CRCI for data flow control
*
* @desc_list: DMA descriptor list (list of desc_infos) * @desc_list: DMA descriptor list (list of desc_infos)
* *
* @data_buffer: our local DMA buffer for page read/writes, * @data_buffer: our local DMA buffer for page read/writes,
* used when we can't use the buffer provided * used when we can't use the buffer provided
* by upper layers directly * by upper layers directly
* @buf_size/count/start: markers for chip->legacy.read_buf/write_buf
* functions
* @reg_read_buf: local buffer for reading back registers via DMA * @reg_read_buf: local buffer for reading back registers via DMA
*
* @base_phys: physical base address of controller registers
* @base_dma: dma base address of controller registers
* @reg_read_dma: contains dma address for register read buffer * @reg_read_dma: contains dma address for register read buffer
* @reg_read_pos: marker for data read in reg_read_buf
* *
* @regs: a contiguous chunk of memory for DMA register * @buf_size/count/start: markers for chip->legacy.read_buf/write_buf
* writes. contains the register values to be * functions
* written to controller
* @cmd1/vld: some fixed controller register values
* @props: properties of current NAND controller,
* initialized via DT match data
* @max_cwperpage: maximum QPIC codewords required. calculated * @max_cwperpage: maximum QPIC codewords required. calculated
* from all connected NAND devices pagesize * from all connected NAND devices pagesize
*
* @reg_read_pos: marker for data read in reg_read_buf
*
* @cmd1/vld: some fixed controller register values
*/ */
struct qcom_nand_controller { struct qcom_nand_controller {
struct nand_controller controller;
struct list_head host_list;
struct device *dev; struct device *dev;
void __iomem *base; void __iomem *base;
phys_addr_t base_phys;
dma_addr_t base_dma;
struct clk *core_clk; struct clk *core_clk;
struct clk *aon_clk; struct clk *aon_clk;
struct nandc_regs *regs;
struct bam_transaction *bam_txn;
const struct qcom_nandc_props *props;
struct nand_controller controller;
struct list_head host_list;
union { union {
/* will be used only by QPIC for BAM DMA */ /* will be used only by QPIC for BAM DMA */
struct { struct {
...@@ -400,64 +416,89 @@ struct qcom_nand_controller { ...@@ -400,64 +416,89 @@ struct qcom_nand_controller {
}; };
struct list_head desc_list; struct list_head desc_list;
struct bam_transaction *bam_txn;
u8 *data_buffer; u8 *data_buffer;
__le32 *reg_read_buf;
phys_addr_t base_phys;
dma_addr_t base_dma;
dma_addr_t reg_read_dma;
int buf_size; int buf_size;
int buf_count; int buf_count;
int buf_start; int buf_start;
unsigned int max_cwperpage; unsigned int max_cwperpage;
__le32 *reg_read_buf;
dma_addr_t reg_read_dma;
int reg_read_pos; int reg_read_pos;
struct nandc_regs *regs;
u32 cmd1, vld; u32 cmd1, vld;
const struct qcom_nandc_props *props; };
/*
* NAND special boot partitions
*
* @page_offset: offset of the partition where spare data is not protected
* by ECC (value in pages)
* @page_offset: size of the partition where spare data is not protected
* by ECC (value in pages)
*/
struct qcom_nand_boot_partition {
u32 page_offset;
u32 page_size;
}; };
/* /*
* NAND chip structure * NAND chip structure
* *
* @boot_partitions: array of boot partitions where offset and size of the
* boot partitions are stored
*
* @chip: base NAND chip structure * @chip: base NAND chip structure
* @node: list node to add itself to host_list in * @node: list node to add itself to host_list in
* qcom_nand_controller * qcom_nand_controller
* *
* @nr_boot_partitions: count of the boot partitions where spare data is not
* protected by ECC
*
* @cs: chip select value for this chip * @cs: chip select value for this chip
* @cw_size: the number of bytes in a single step/codeword * @cw_size: the number of bytes in a single step/codeword
* of a page, consisting of all data, ecc, spare * of a page, consisting of all data, ecc, spare
* and reserved bytes * and reserved bytes
* @cw_data: the number of bytes within a codeword protected * @cw_data: the number of bytes within a codeword protected
* by ECC * by ECC
* @use_ecc: request the controller to use ECC for the
* upcoming read/write
* @bch_enabled: flag to tell whether BCH ECC mode is used
* @ecc_bytes_hw: ECC bytes used by controller hardware for this * @ecc_bytes_hw: ECC bytes used by controller hardware for this
* chip * chip
* @status: value to be returned if NAND_CMD_STATUS command *
* is executed
* @last_command: keeps track of last command on this chip. used * @last_command: keeps track of last command on this chip. used
* for reading correct status * for reading correct status
* *
* @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for
* ecc/non-ecc mode for the current nand flash * ecc/non-ecc mode for the current nand flash
* device * device
*
* @status: value to be returned if NAND_CMD_STATUS command
* is executed
* @codeword_fixup: keep track of the current layout used by
* the driver for read/write operation.
* @use_ecc: request the controller to use ECC for the
* upcoming read/write
* @bch_enabled: flag to tell whether BCH ECC mode is used
*/ */
struct qcom_nand_host { struct qcom_nand_host {
struct qcom_nand_boot_partition *boot_partitions;
struct nand_chip chip; struct nand_chip chip;
struct list_head node; struct list_head node;
int nr_boot_partitions;
int cs; int cs;
int cw_size; int cw_size;
int cw_data; int cw_data;
bool use_ecc;
bool bch_enabled;
int ecc_bytes_hw; int ecc_bytes_hw;
int spare_bytes; int spare_bytes;
int bbm_size; int bbm_size;
u8 status;
int last_command; int last_command;
u32 cfg0, cfg1; u32 cfg0, cfg1;
...@@ -466,23 +507,30 @@ struct qcom_nand_host { ...@@ -466,23 +507,30 @@ struct qcom_nand_host {
u32 ecc_bch_cfg; u32 ecc_bch_cfg;
u32 clrflashstatus; u32 clrflashstatus;
u32 clrreadstatus; u32 clrreadstatus;
u8 status;
bool codeword_fixup;
bool use_ecc;
bool bch_enabled;
}; };
/* /*
* This data type corresponds to the NAND controller properties which varies * This data type corresponds to the NAND controller properties which varies
* among different NAND controllers. * among different NAND controllers.
* @ecc_modes - ecc mode for NAND * @ecc_modes - ecc mode for NAND
* @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
* @is_bam - whether NAND controller is using BAM * @is_bam - whether NAND controller is using BAM
* @is_qpic - whether NAND CTRL is part of qpic IP * @is_qpic - whether NAND CTRL is part of qpic IP
* @qpic_v2 - flag to indicate QPIC IP version 2 * @qpic_v2 - flag to indicate QPIC IP version 2
* @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset * @use_codeword_fixup - whether NAND has different layout for boot partitions
*/ */
struct qcom_nandc_props { struct qcom_nandc_props {
u32 ecc_modes; u32 ecc_modes;
u32 dev_cmd_reg_start;
bool is_bam; bool is_bam;
bool is_qpic; bool is_qpic;
bool qpic_v2; bool qpic_v2;
u32 dev_cmd_reg_start; bool use_codeword_fixup;
}; };
/* Frees the BAM transaction memory */ /* Frees the BAM transaction memory */
...@@ -1701,7 +1749,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -1701,7 +1749,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip,
data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
oob_size1 = host->bbm_size; oob_size1 = host->bbm_size;
if (qcom_nandc_is_last_cw(ecc, cw)) { if (qcom_nandc_is_last_cw(ecc, cw) && !host->codeword_fixup) {
data_size2 = ecc->size - data_size1 - data_size2 = ecc->size - data_size1 -
((ecc->steps - 1) * 4); ((ecc->steps - 1) * 4);
oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw + oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw +
...@@ -1782,7 +1830,7 @@ check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf, ...@@ -1782,7 +1830,7 @@ check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf,
} }
for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) { for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) {
if (qcom_nandc_is_last_cw(ecc, cw)) { if (qcom_nandc_is_last_cw(ecc, cw) && !host->codeword_fixup) {
data_size = ecc->size - ((ecc->steps - 1) * 4); data_size = ecc->size - ((ecc->steps - 1) * 4);
oob_size = (ecc->steps * 4) + host->ecc_bytes_hw; oob_size = (ecc->steps * 4) + host->ecc_bytes_hw;
} else { } else {
...@@ -1940,7 +1988,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, ...@@ -1940,7 +1988,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
for (i = 0; i < ecc->steps; i++) { for (i = 0; i < ecc->steps; i++) {
int data_size, oob_size; int data_size, oob_size;
if (qcom_nandc_is_last_cw(ecc, i)) { if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) {
data_size = ecc->size - ((ecc->steps - 1) << 2); data_size = ecc->size - ((ecc->steps - 1) << 2);
oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
host->spare_bytes; host->spare_bytes;
...@@ -2037,6 +2085,69 @@ static int copy_last_cw(struct qcom_nand_host *host, int page) ...@@ -2037,6 +2085,69 @@ static int copy_last_cw(struct qcom_nand_host *host, int page)
return ret; return ret;
} }
static bool qcom_nandc_is_boot_partition(struct qcom_nand_host *host, int page)
{
struct qcom_nand_boot_partition *boot_partition;
u32 start, end;
int i;
/*
* Since the frequent access will be to the non-boot partitions like rootfs,
* optimize the page check by:
*
* 1. Checking if the page lies after the last boot partition.
* 2. Checking from the boot partition end.
*/
/* First check the last boot partition */
boot_partition = &host->boot_partitions[host->nr_boot_partitions - 1];
start = boot_partition->page_offset;
end = start + boot_partition->page_size;
/* Page is after the last boot partition end. This is NOT a boot partition */
if (page > end)
return false;
/* Actually check if it's a boot partition */
if (page < end && page >= start)
return true;
/* Check the other boot partitions starting from the second-last partition */
for (i = host->nr_boot_partitions - 2; i >= 0; i--) {
boot_partition = &host->boot_partitions[i];
start = boot_partition->page_offset;
end = start + boot_partition->page_size;
if (page < end && page >= start)
return true;
}
return false;
}
static void qcom_nandc_codeword_fixup(struct qcom_nand_host *host, int page)
{
bool codeword_fixup = qcom_nandc_is_boot_partition(host, page);
/* Skip conf write if we are already in the correct mode */
if (codeword_fixup == host->codeword_fixup)
return;
host->codeword_fixup = codeword_fixup;
host->cw_data = codeword_fixup ? 512 : 516;
host->spare_bytes = host->cw_size - host->ecc_bytes_hw -
host->bbm_size - host->cw_data;
host->cfg0 &= ~(SPARE_SIZE_BYTES_MASK | UD_SIZE_BYTES_MASK);
host->cfg0 |= host->spare_bytes << SPARE_SIZE_BYTES |
host->cw_data << UD_SIZE_BYTES;
host->ecc_bch_cfg &= ~ECC_NUM_DATA_BYTES_MASK;
host->ecc_bch_cfg |= host->cw_data << ECC_NUM_DATA_BYTES;
host->ecc_buf_cfg = (host->cw_data - 1) << NUM_STEPS;
}
/* implements ecc->read_page() */ /* implements ecc->read_page() */
static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf, static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf,
int oob_required, int page) int oob_required, int page)
...@@ -2045,6 +2156,9 @@ static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf, ...@@ -2045,6 +2156,9 @@ static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf,
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
u8 *data_buf, *oob_buf = NULL; u8 *data_buf, *oob_buf = NULL;
if (host->nr_boot_partitions)
qcom_nandc_codeword_fixup(host, page);
nand_read_page_op(chip, page, 0, NULL, 0); nand_read_page_op(chip, page, 0, NULL, 0);
data_buf = buf; data_buf = buf;
oob_buf = oob_required ? chip->oob_poi : NULL; oob_buf = oob_required ? chip->oob_poi : NULL;
...@@ -2064,6 +2178,9 @@ static int qcom_nandc_read_page_raw(struct nand_chip *chip, uint8_t *buf, ...@@ -2064,6 +2178,9 @@ static int qcom_nandc_read_page_raw(struct nand_chip *chip, uint8_t *buf,
int cw, ret; int cw, ret;
u8 *data_buf = buf, *oob_buf = chip->oob_poi; u8 *data_buf = buf, *oob_buf = chip->oob_poi;
if (host->nr_boot_partitions)
qcom_nandc_codeword_fixup(host, page);
for (cw = 0; cw < ecc->steps; cw++) { for (cw = 0; cw < ecc->steps; cw++) {
ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf, ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf,
page, cw); page, cw);
...@@ -2084,6 +2201,9 @@ static int qcom_nandc_read_oob(struct nand_chip *chip, int page) ...@@ -2084,6 +2201,9 @@ static int qcom_nandc_read_oob(struct nand_chip *chip, int page)
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
struct nand_ecc_ctrl *ecc = &chip->ecc; struct nand_ecc_ctrl *ecc = &chip->ecc;
if (host->nr_boot_partitions)
qcom_nandc_codeword_fixup(host, page);
clear_read_regs(nandc); clear_read_regs(nandc);
clear_bam_transaction(nandc); clear_bam_transaction(nandc);
...@@ -2104,6 +2224,9 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf, ...@@ -2104,6 +2224,9 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf,
u8 *data_buf, *oob_buf; u8 *data_buf, *oob_buf;
int i, ret; int i, ret;
if (host->nr_boot_partitions)
qcom_nandc_codeword_fixup(host, page);
nand_prog_page_begin_op(chip, page, 0, NULL, 0); nand_prog_page_begin_op(chip, page, 0, NULL, 0);
clear_read_regs(nandc); clear_read_regs(nandc);
...@@ -2119,7 +2242,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf, ...@@ -2119,7 +2242,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf,
for (i = 0; i < ecc->steps; i++) { for (i = 0; i < ecc->steps; i++) {
int data_size, oob_size; int data_size, oob_size;
if (qcom_nandc_is_last_cw(ecc, i)) { if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) {
data_size = ecc->size - ((ecc->steps - 1) << 2); data_size = ecc->size - ((ecc->steps - 1) << 2);
oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
host->spare_bytes; host->spare_bytes;
...@@ -2176,6 +2299,9 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip, ...@@ -2176,6 +2299,9 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip,
u8 *data_buf, *oob_buf; u8 *data_buf, *oob_buf;
int i, ret; int i, ret;
if (host->nr_boot_partitions)
qcom_nandc_codeword_fixup(host, page);
nand_prog_page_begin_op(chip, page, 0, NULL, 0); nand_prog_page_begin_op(chip, page, 0, NULL, 0);
clear_read_regs(nandc); clear_read_regs(nandc);
clear_bam_transaction(nandc); clear_bam_transaction(nandc);
...@@ -2194,7 +2320,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip, ...@@ -2194,7 +2320,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip,
data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
oob_size1 = host->bbm_size; oob_size1 = host->bbm_size;
if (qcom_nandc_is_last_cw(ecc, i)) { if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) {
data_size2 = ecc->size - data_size1 - data_size2 = ecc->size - data_size1 -
((ecc->steps - 1) << 2); ((ecc->steps - 1) << 2);
oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw + oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw +
...@@ -2254,6 +2380,9 @@ static int qcom_nandc_write_oob(struct nand_chip *chip, int page) ...@@ -2254,6 +2380,9 @@ static int qcom_nandc_write_oob(struct nand_chip *chip, int page)
int data_size, oob_size; int data_size, oob_size;
int ret; int ret;
if (host->nr_boot_partitions)
qcom_nandc_codeword_fixup(host, page);
host->use_ecc = true; host->use_ecc = true;
clear_bam_transaction(nandc); clear_bam_transaction(nandc);
...@@ -2915,6 +3044,74 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc) ...@@ -2915,6 +3044,74 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
static const char * const probes[] = { "cmdlinepart", "ofpart", "qcomsmem", NULL }; static const char * const probes[] = { "cmdlinepart", "ofpart", "qcomsmem", NULL };
static int qcom_nand_host_parse_boot_partitions(struct qcom_nand_controller *nandc,
struct qcom_nand_host *host,
struct device_node *dn)
{
struct nand_chip *chip = &host->chip;
struct mtd_info *mtd = nand_to_mtd(chip);
struct qcom_nand_boot_partition *boot_partition;
struct device *dev = nandc->dev;
int partitions_count, i, j, ret;
if (!of_find_property(dn, "qcom,boot-partitions", NULL))
return 0;
partitions_count = of_property_count_u32_elems(dn, "qcom,boot-partitions");
if (partitions_count <= 0) {
dev_err(dev, "Error parsing boot partition\n");
return partitions_count ? partitions_count : -EINVAL;
}
host->nr_boot_partitions = partitions_count / 2;
host->boot_partitions = devm_kcalloc(dev, host->nr_boot_partitions,
sizeof(*host->boot_partitions), GFP_KERNEL);
if (!host->boot_partitions) {
host->nr_boot_partitions = 0;
return -ENOMEM;
}
for (i = 0, j = 0; i < host->nr_boot_partitions; i++, j += 2) {
boot_partition = &host->boot_partitions[i];
ret = of_property_read_u32_index(dn, "qcom,boot-partitions", j,
&boot_partition->page_offset);
if (ret) {
dev_err(dev, "Error parsing boot partition offset at index %d\n", i);
host->nr_boot_partitions = 0;
return ret;
}
if (boot_partition->page_offset % mtd->writesize) {
dev_err(dev, "Boot partition offset not multiple of writesize at index %i\n",
i);
host->nr_boot_partitions = 0;
return -EINVAL;
}
/* Convert offset to nand pages */
boot_partition->page_offset /= mtd->writesize;
ret = of_property_read_u32_index(dn, "qcom,boot-partitions", j + 1,
&boot_partition->page_size);
if (ret) {
dev_err(dev, "Error parsing boot partition size at index %d\n", i);
host->nr_boot_partitions = 0;
return ret;
}
if (boot_partition->page_size % mtd->writesize) {
dev_err(dev, "Boot partition size not multiple of writesize at index %i\n",
i);
host->nr_boot_partitions = 0;
return -EINVAL;
}
/* Convert size to nand pages */
boot_partition->page_size /= mtd->writesize;
}
return 0;
}
static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc, static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
struct qcom_nand_host *host, struct qcom_nand_host *host,
struct device_node *dn) struct device_node *dn)
...@@ -2972,6 +3169,14 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc, ...@@ -2972,6 +3169,14 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
if (ret) if (ret)
nand_cleanup(chip); nand_cleanup(chip);
if (nandc->props->use_codeword_fixup) {
ret = qcom_nand_host_parse_boot_partitions(nandc, host, dn);
if (ret) {
nand_cleanup(chip);
return ret;
}
}
return ret; return ret;
} }
...@@ -3137,6 +3342,7 @@ static int qcom_nandc_remove(struct platform_device *pdev) ...@@ -3137,6 +3342,7 @@ static int qcom_nandc_remove(struct platform_device *pdev)
static const struct qcom_nandc_props ipq806x_nandc_props = { static const struct qcom_nandc_props ipq806x_nandc_props = {
.ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT), .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT),
.is_bam = false, .is_bam = false,
.use_codeword_fixup = true,
.dev_cmd_reg_start = 0x0, .dev_cmd_reg_start = 0x0,
}; };
......
...@@ -52,7 +52,7 @@ static const struct mtd_ooblayout_ops oob_sm_ops = { ...@@ -52,7 +52,7 @@ static const struct mtd_ooblayout_ops oob_sm_ops = {
.free = oob_sm_ooblayout_free, .free = oob_sm_ooblayout_free,
}; };
/* NOTE: This layout is is not compatabable with SmartMedia, */ /* NOTE: This layout is not compatabable with SmartMedia, */
/* because the 256 byte devices have page depenent oob layout */ /* because the 256 byte devices have page depenent oob layout */
/* However it does preserve the bad block markers */ /* However it does preserve the bad block markers */
/* If you use smftl, it will bypass this and work correctly */ /* If you use smftl, it will bypass this and work correctly */
......
...@@ -1223,11 +1223,8 @@ static int tegra_nand_remove(struct platform_device *pdev) ...@@ -1223,11 +1223,8 @@ static int tegra_nand_remove(struct platform_device *pdev)
struct tegra_nand_controller *ctrl = platform_get_drvdata(pdev); struct tegra_nand_controller *ctrl = platform_get_drvdata(pdev);
struct nand_chip *chip = ctrl->chip; struct nand_chip *chip = ctrl->chip;
struct mtd_info *mtd = nand_to_mtd(chip); struct mtd_info *mtd = nand_to_mtd(chip);
int ret;
ret = mtd_device_unregister(mtd); WARN_ON(mtd_device_unregister(mtd));
if (ret)
return ret;
nand_cleanup(chip); nand_cleanup(chip);
......
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o spinand-objs := core.o ato.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Aidan MacDonald
*
* Author: Aidan MacDonald <aidanmacdonald.0x0@gmail.com>
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/mtd/spinand.h>
#define SPINAND_MFR_ATO 0x9b
static SPINAND_OP_VARIANTS(read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
static SPINAND_OP_VARIANTS(write_cache_variants,
SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
SPINAND_PROG_LOAD(true, 0, NULL, 0));
static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
SPINAND_PROG_LOAD(false, 0, NULL, 0));
static int ato25d1ga_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section > 3)
return -ERANGE;
region->offset = (16 * section) + 8;
region->length = 8;
return 0;
}
static int ato25d1ga_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section > 3)
return -ERANGE;
if (section) {
region->offset = (16 * section);
region->length = 8;
} else {
/* first byte of section 0 is reserved for the BBM */
region->offset = 1;
region->length = 7;
}
return 0;
}
static const struct mtd_ooblayout_ops ato25d1ga_ooblayout = {
.ecc = ato25d1ga_ooblayout_ecc,
.free = ato25d1ga_ooblayout_free,
};
static const struct spinand_info ato_spinand_table[] = {
SPINAND_INFO("ATO25D1GA",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x12),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(1, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&ato25d1ga_ooblayout, NULL)),
};
static const struct spinand_manufacturer_ops ato_spinand_manuf_ops = {
};
const struct spinand_manufacturer ato_spinand_manufacturer = {
.id = SPINAND_MFR_ATO,
.name = "ATO",
.chips = ato_spinand_table,
.nchips = ARRAY_SIZE(ato_spinand_table),
.ops = &ato_spinand_manuf_ops,
};
...@@ -927,6 +927,7 @@ static const struct nand_ops spinand_ops = { ...@@ -927,6 +927,7 @@ static const struct nand_ops spinand_ops = {
}; };
static const struct spinand_manufacturer *spinand_manufacturers[] = { static const struct spinand_manufacturer *spinand_manufacturers[] = {
&ato_spinand_manufacturer,
&gigadevice_spinand_manufacturer, &gigadevice_spinand_manufacturer,
&macronix_spinand_manufacturer, &macronix_spinand_manufacturer,
&micron_spinand_manufacturer, &micron_spinand_manufacturer,
......
...@@ -186,3 +186,12 @@ config MTD_QCOMSMEM_PARTS ...@@ -186,3 +186,12 @@ config MTD_QCOMSMEM_PARTS
help help
This provides support for parsing partitions from Shared Memory (SMEM) This provides support for parsing partitions from Shared Memory (SMEM)
for NAND and SPI flash on Qualcomm platforms. for NAND and SPI flash on Qualcomm platforms.
config MTD_SERCOMM_PARTS
tristate "Sercomm partition table parser"
depends on MTD && RALINK
help
This provides partitions table parser for devices with Sercomm
partition map. This partition table contains real partition
offsets, which may differ from device to device depending on the
number and location of bad blocks on NAND.
...@@ -10,6 +10,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o ...@@ -10,6 +10,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o
obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o
obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
obj-$(CONFIG_MTD_QCOMSMEM_PARTS) += qcomsmempart.o obj-$(CONFIG_MTD_QCOMSMEM_PARTS) += qcomsmempart.o
...@@ -35,12 +35,15 @@ static long long bcm4908_partitions_fw_offset(void) ...@@ -35,12 +35,15 @@ static long long bcm4908_partitions_fw_offset(void)
err = kstrtoul(s + len + 1, 0, &offset); err = kstrtoul(s + len + 1, 0, &offset);
if (err) { if (err) {
pr_err("failed to parse %s\n", s + len + 1); pr_err("failed to parse %s\n", s + len + 1);
of_node_put(root);
return err; return err;
} }
of_node_put(root);
return offset << 10; return offset << 10;
} }
of_node_put(root);
return -ENOENT; return -ENOENT;
} }
......
...@@ -58,6 +58,7 @@ static void parse_redboot_of(struct mtd_info *master) ...@@ -58,6 +58,7 @@ static void parse_redboot_of(struct mtd_info *master)
return; return;
ret = of_property_read_u32(npart, "fis-index-block", &dirblock); ret = of_property_read_u32(npart, "fis-index-block", &dirblock);
of_node_put(npart);
if (ret) if (ret)
return; return;
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* drivers/mtd/scpart.c: Sercomm Partition Parser
*
* Copyright (C) 2018 NOGUCHI Hiroshi
* Copyright (C) 2022 Mikhail Zhilkin
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/module.h>
#define MOD_NAME "scpart"
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) MOD_NAME ": " fmt
#define ID_ALREADY_FOUND 0xffffffffUL
#define MAP_OFFS_IN_BLK 0x800
#define MAP_MIRROR_NUM 2
static const char sc_part_magic[] = {
'S', 'C', 'F', 'L', 'M', 'A', 'P', 'O', 'K', '\0',
};
#define PART_MAGIC_LEN sizeof(sc_part_magic)
/* assumes that all fields are set by CPU native endian */
struct sc_part_desc {
uint32_t part_id;
uint32_t part_offs;
uint32_t part_bytes;
};
static uint32_t scpart_desc_is_valid(struct sc_part_desc *pdesc)
{
return ((pdesc->part_id != 0xffffffffUL) &&
(pdesc->part_offs != 0xffffffffUL) &&
(pdesc->part_bytes != 0xffffffffUL));
}
static int scpart_scan_partmap(struct mtd_info *master, loff_t partmap_offs,
struct sc_part_desc **ppdesc)
{
int cnt = 0;
int res = 0;
int res2;
loff_t offs;
size_t retlen;
struct sc_part_desc *pdesc = NULL;
struct sc_part_desc *tmpdesc;
uint8_t *buf;
buf = kzalloc(master->erasesize, GFP_KERNEL);
if (!buf) {
res = -ENOMEM;
goto out;
}
res2 = mtd_read(master, partmap_offs, master->erasesize, &retlen, buf);
if (res2 || retlen != master->erasesize) {
res = -EIO;
goto free;
}
for (offs = MAP_OFFS_IN_BLK;
offs < master->erasesize - sizeof(*tmpdesc);
offs += sizeof(*tmpdesc)) {
tmpdesc = (struct sc_part_desc *)&buf[offs];
if (!scpart_desc_is_valid(tmpdesc))
break;
cnt++;
}
if (cnt > 0) {
int bytes = cnt * sizeof(*pdesc);
pdesc = kcalloc(cnt, sizeof(*pdesc), GFP_KERNEL);
if (!pdesc) {
res = -ENOMEM;
goto free;
}
memcpy(pdesc, &(buf[MAP_OFFS_IN_BLK]), bytes);
*ppdesc = pdesc;
res = cnt;
}
free:
kfree(buf);
out:
return res;
}
static int scpart_find_partmap(struct mtd_info *master,
struct sc_part_desc **ppdesc)
{
int magic_found = 0;
int res = 0;
int res2;
loff_t offs = 0;
size_t retlen;
uint8_t rdbuf[PART_MAGIC_LEN];
while ((magic_found < MAP_MIRROR_NUM) &&
(offs < master->size) &&
!mtd_block_isbad(master, offs)) {
res2 = mtd_read(master, offs, PART_MAGIC_LEN, &retlen, rdbuf);
if (res2 || retlen != PART_MAGIC_LEN) {
res = -EIO;
goto out;
}
if (!memcmp(rdbuf, sc_part_magic, PART_MAGIC_LEN)) {
pr_debug("Signature found at 0x%llx\n", offs);
magic_found++;
res = scpart_scan_partmap(master, offs, ppdesc);
if (res > 0)
goto out;
}
offs += master->erasesize;
}
out:
if (res > 0)
pr_info("Valid 'SC PART MAP' (%d partitions) found at 0x%llx\n", res, offs);
else
pr_info("No valid 'SC PART MAP' was found\n");
return res;
}
static int scpart_parse(struct mtd_info *master,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
const char *partname;
int n;
int nr_scparts;
int nr_parts = 0;
int res = 0;
struct sc_part_desc *scpart_map = NULL;
struct mtd_partition *parts = NULL;
struct device_node *mtd_node;
struct device_node *ofpart_node;
struct device_node *pp;
mtd_node = mtd_get_of_node(master);
if (!mtd_node) {
res = -ENOENT;
goto out;
}
ofpart_node = of_get_child_by_name(mtd_node, "partitions");
if (!ofpart_node) {
pr_info("%s: 'partitions' subnode not found on %pOF.\n",
master->name, mtd_node);
res = -ENOENT;
goto out;
}
nr_scparts = scpart_find_partmap(master, &scpart_map);
if (nr_scparts <= 0) {
pr_info("No any partitions was found in 'SC PART MAP'.\n");
res = -ENOENT;
goto free;
}
parts = kcalloc(of_get_child_count(ofpart_node), sizeof(*parts),
GFP_KERNEL);
if (!parts) {
res = -ENOMEM;
goto free;
}
for_each_child_of_node(ofpart_node, pp) {
u32 scpart_id;
if (of_property_read_u32(pp, "sercomm,scpart-id", &scpart_id))
continue;
for (n = 0 ; n < nr_scparts ; n++)
if ((scpart_map[n].part_id != ID_ALREADY_FOUND) &&
(scpart_id == scpart_map[n].part_id))
break;
if (n >= nr_scparts)
/* not match */
continue;
/* add the partition found in OF into MTD partition array */
parts[nr_parts].offset = scpart_map[n].part_offs;
parts[nr_parts].size = scpart_map[n].part_bytes;
parts[nr_parts].of_node = pp;
if (!of_property_read_string(pp, "label", &partname))
parts[nr_parts].name = partname;
if (of_property_read_bool(pp, "read-only"))
parts[nr_parts].mask_flags |= MTD_WRITEABLE;
if (of_property_read_bool(pp, "lock"))
parts[nr_parts].mask_flags |= MTD_POWERUP_LOCK;
/* mark as 'done' */
scpart_map[n].part_id = ID_ALREADY_FOUND;
nr_parts++;
}
if (nr_parts > 0) {
*pparts = parts;
res = nr_parts;
} else
pr_info("No partition in OF matches partition ID with 'SC PART MAP'.\n");
of_node_put(pp);
free:
of_node_put(ofpart_node);
kfree(scpart_map);
if (res <= 0)
kfree(parts);
out:
return res;
}
static const struct of_device_id scpart_parser_of_match_table[] = {
{ .compatible = "sercomm,sc-partitions" },
{},
};
MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table);
static struct mtd_part_parser scpart_parser = {
.parse_fn = scpart_parse,
.name = "scpart",
.of_match_table = scpart_parser_of_match_table,
};
module_mtd_part_parser(scpart_parser);
/* mtd parsers will request the module by parser name */
MODULE_ALIAS("scpart");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("NOGUCHI Hiroshi <drvlabo@gmail.com>");
MODULE_AUTHOR("Mikhail Zhilkin <csharper2005@gmail.com>");
MODULE_DESCRIPTION("Sercomm partition parser");
...@@ -1111,9 +1111,9 @@ static void sm_release(struct mtd_blktrans_dev *dev) ...@@ -1111,9 +1111,9 @@ static void sm_release(struct mtd_blktrans_dev *dev)
{ {
struct sm_ftl *ftl = dev->priv; struct sm_ftl *ftl = dev->priv;
mutex_lock(&ftl->mutex);
del_timer_sync(&ftl->timer); del_timer_sync(&ftl->timer);
cancel_work_sync(&ftl->flush_work); cancel_work_sync(&ftl->flush_work);
mutex_lock(&ftl->mutex);
sm_cache_flush(ftl); sm_cache_flush(ftl);
mutex_unlock(&ftl->mutex); mutex_unlock(&ftl->mutex);
} }
......
...@@ -237,7 +237,7 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off, ...@@ -237,7 +237,7 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
reg = readl(host->regbase + FMC_CFG); reg = readl(host->regbase + FMC_CFG);
reg &= ~(FMC_CFG_OP_MODE_MASK | SPI_NOR_ADDR_MODE_MASK); reg &= ~(FMC_CFG_OP_MODE_MASK | SPI_NOR_ADDR_MODE_MASK);
reg |= FMC_CFG_OP_MODE_NORMAL; reg |= FMC_CFG_OP_MODE_NORMAL;
reg |= (nor->addr_width == 4) ? SPI_NOR_ADDR_MODE_4BYTES reg |= (nor->addr_nbytes == 4) ? SPI_NOR_ADDR_MODE_4BYTES
: SPI_NOR_ADDR_MODE_3BYTES; : SPI_NOR_ADDR_MODE_3BYTES;
writel(reg, host->regbase + FMC_CFG); writel(reg, host->regbase + FMC_CFG);
......
...@@ -203,7 +203,7 @@ static ssize_t nxp_spifi_write(struct spi_nor *nor, loff_t to, size_t len, ...@@ -203,7 +203,7 @@ static ssize_t nxp_spifi_write(struct spi_nor *nor, loff_t to, size_t len,
SPIFI_CMD_DATALEN(len) | SPIFI_CMD_DATALEN(len) |
SPIFI_CMD_FIELDFORM_ALL_SERIAL | SPIFI_CMD_FIELDFORM_ALL_SERIAL |
SPIFI_CMD_OPCODE(nor->program_opcode) | SPIFI_CMD_OPCODE(nor->program_opcode) |
SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1); SPIFI_CMD_FRAMEFORM(spifi->nor.addr_nbytes + 1);
writel(cmd, spifi->io_base + SPIFI_CMD); writel(cmd, spifi->io_base + SPIFI_CMD);
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
...@@ -230,7 +230,7 @@ static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs) ...@@ -230,7 +230,7 @@ static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)
cmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL | cmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL |
SPIFI_CMD_OPCODE(nor->erase_opcode) | SPIFI_CMD_OPCODE(nor->erase_opcode) |
SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1); SPIFI_CMD_FRAMEFORM(spifi->nor.addr_nbytes + 1);
writel(cmd, spifi->io_base + SPIFI_CMD); writel(cmd, spifi->io_base + SPIFI_CMD);
return nxp_spifi_wait_for_cmd(spifi); return nxp_spifi_wait_for_cmd(spifi);
...@@ -252,12 +252,12 @@ static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi) ...@@ -252,12 +252,12 @@ static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi)
} }
/* Memory mode supports address length between 1 and 4 */ /* Memory mode supports address length between 1 and 4 */
if (spifi->nor.addr_width < 1 || spifi->nor.addr_width > 4) if (spifi->nor.addr_nbytes < 1 || spifi->nor.addr_nbytes > 4)
return -EINVAL; return -EINVAL;
spifi->mcmd |= SPIFI_CMD_OPCODE(spifi->nor.read_opcode) | spifi->mcmd |= SPIFI_CMD_OPCODE(spifi->nor.read_opcode) |
SPIFI_CMD_INTLEN(spifi->nor.read_dummy / 8) | SPIFI_CMD_INTLEN(spifi->nor.read_dummy / 8) |
SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1); SPIFI_CMD_FRAMEFORM(spifi->nor.addr_nbytes + 1);
return 0; return 0;
} }
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
*/ */
#define CHIP_ERASE_2MB_READY_WAIT_JIFFIES (40UL * HZ) #define CHIP_ERASE_2MB_READY_WAIT_JIFFIES (40UL * HZ)
#define SPI_NOR_MAX_ADDR_WIDTH 4 #define SPI_NOR_MAX_ADDR_NBYTES 4
#define SPI_NOR_SRST_SLEEP_MIN 200 #define SPI_NOR_SRST_SLEEP_MIN 200
#define SPI_NOR_SRST_SLEEP_MAX 400 #define SPI_NOR_SRST_SLEEP_MAX 400
...@@ -177,7 +177,7 @@ int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode, ...@@ -177,7 +177,7 @@ int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode,
static int spi_nor_controller_ops_erase(struct spi_nor *nor, loff_t offs) static int spi_nor_controller_ops_erase(struct spi_nor *nor, loff_t offs)
{ {
if (spi_nor_protocol_is_dtr(nor->write_proto)) if (spi_nor_protocol_is_dtr(nor->reg_proto))
return -EOPNOTSUPP; return -EOPNOTSUPP;
return nor->controller_ops->erase(nor, offs); return nor->controller_ops->erase(nor, offs);
...@@ -198,7 +198,7 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from, ...@@ -198,7 +198,7 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from,
{ {
struct spi_mem_op op = struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),
SPI_MEM_OP_ADDR(nor->addr_width, from, 0), SPI_MEM_OP_ADDR(nor->addr_nbytes, from, 0),
SPI_MEM_OP_DUMMY(nor->read_dummy, 0), SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
SPI_MEM_OP_DATA_IN(len, buf, 0)); SPI_MEM_OP_DATA_IN(len, buf, 0));
bool usebouncebuf; bool usebouncebuf;
...@@ -262,7 +262,7 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to, ...@@ -262,7 +262,7 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to,
{ {
struct spi_mem_op op = struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0),
SPI_MEM_OP_ADDR(nor->addr_width, to, 0), SPI_MEM_OP_ADDR(nor->addr_nbytes, to, 0),
SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(len, buf, 0)); SPI_MEM_OP_DATA_OUT(len, buf, 0));
ssize_t nbytes; ssize_t nbytes;
...@@ -972,7 +972,7 @@ static int spi_nor_erase_chip(struct spi_nor *nor) ...@@ -972,7 +972,7 @@ static int spi_nor_erase_chip(struct spi_nor *nor)
if (nor->spimem) { if (nor->spimem) {
struct spi_mem_op op = SPI_NOR_CHIP_ERASE_OP; struct spi_mem_op op = SPI_NOR_CHIP_ERASE_OP;
spi_nor_spimem_setup_op(nor, &op, nor->write_proto); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
ret = spi_mem_exec_op(nor->spimem, &op); ret = spi_mem_exec_op(nor->spimem, &op);
} else { } else {
...@@ -1113,9 +1113,9 @@ int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) ...@@ -1113,9 +1113,9 @@ int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
if (nor->spimem) { if (nor->spimem) {
struct spi_mem_op op = struct spi_mem_op op =
SPI_NOR_SECTOR_ERASE_OP(nor->erase_opcode, SPI_NOR_SECTOR_ERASE_OP(nor->erase_opcode,
nor->addr_width, addr); nor->addr_nbytes, addr);
spi_nor_spimem_setup_op(nor, &op, nor->write_proto); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
return spi_mem_exec_op(nor->spimem, &op); return spi_mem_exec_op(nor->spimem, &op);
} else if (nor->controller_ops->erase) { } else if (nor->controller_ops->erase) {
...@@ -1126,13 +1126,13 @@ int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) ...@@ -1126,13 +1126,13 @@ int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
* Default implementation, if driver doesn't have a specialized HW * Default implementation, if driver doesn't have a specialized HW
* control * control
*/ */
for (i = nor->addr_width - 1; i >= 0; i--) { for (i = nor->addr_nbytes - 1; i >= 0; i--) {
nor->bouncebuf[i] = addr & 0xff; nor->bouncebuf[i] = addr & 0xff;
addr >>= 8; addr >>= 8;
} }
return spi_nor_controller_ops_write_reg(nor, nor->erase_opcode, return spi_nor_controller_ops_write_reg(nor, nor->erase_opcode,
nor->bouncebuf, nor->addr_width); nor->bouncebuf, nor->addr_nbytes);
} }
/** /**
...@@ -2249,43 +2249,43 @@ static int spi_nor_default_setup(struct spi_nor *nor, ...@@ -2249,43 +2249,43 @@ static int spi_nor_default_setup(struct spi_nor *nor,
return 0; return 0;
} }
static int spi_nor_set_addr_width(struct spi_nor *nor) static int spi_nor_set_addr_nbytes(struct spi_nor *nor)
{ {
if (nor->addr_width) { if (nor->params->addr_nbytes) {
/* already configured from SFDP */ nor->addr_nbytes = nor->params->addr_nbytes;
} else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) { } else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) {
/* /*
* In 8D-8D-8D mode, one byte takes half a cycle to transfer. So * In 8D-8D-8D mode, one byte takes half a cycle to transfer. So
* in this protocol an odd address width cannot be used because * in this protocol an odd addr_nbytes cannot be used because
* then the address phase would only span a cycle and a half. * then the address phase would only span a cycle and a half.
* Half a cycle would be left over. We would then have to start * Half a cycle would be left over. We would then have to start
* the dummy phase in the middle of a cycle and so too the data * the dummy phase in the middle of a cycle and so too the data
* phase, and we will end the transaction with half a cycle left * phase, and we will end the transaction with half a cycle left
* over. * over.
* *
* Force all 8D-8D-8D flashes to use an address width of 4 to * Force all 8D-8D-8D flashes to use an addr_nbytes of 4 to
* avoid this situation. * avoid this situation.
*/ */
nor->addr_width = 4; nor->addr_nbytes = 4;
} else if (nor->info->addr_width) { } else if (nor->info->addr_nbytes) {
nor->addr_width = nor->info->addr_width; nor->addr_nbytes = nor->info->addr_nbytes;
} else { } else {
nor->addr_width = 3; nor->addr_nbytes = 3;
} }
if (nor->addr_width == 3 && nor->params->size > 0x1000000) { if (nor->addr_nbytes == 3 && nor->params->size > 0x1000000) {
/* enable 4-byte addressing if the device exceeds 16MiB */ /* enable 4-byte addressing if the device exceeds 16MiB */
nor->addr_width = 4; nor->addr_nbytes = 4;
} }
if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { if (nor->addr_nbytes > SPI_NOR_MAX_ADDR_NBYTES) {
dev_dbg(nor->dev, "address width is too large: %u\n", dev_dbg(nor->dev, "The number of address bytes is too large: %u\n",
nor->addr_width); nor->addr_nbytes);
return -EINVAL; return -EINVAL;
} }
/* Set 4byte opcodes when possible. */ /* Set 4byte opcodes when possible. */
if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES && if (nor->addr_nbytes == 4 && nor->flags & SNOR_F_4B_OPCODES &&
!(nor->flags & SNOR_F_HAS_4BAIT)) !(nor->flags & SNOR_F_HAS_4BAIT))
spi_nor_set_4byte_opcodes(nor); spi_nor_set_4byte_opcodes(nor);
...@@ -2304,7 +2304,7 @@ static int spi_nor_setup(struct spi_nor *nor, ...@@ -2304,7 +2304,7 @@ static int spi_nor_setup(struct spi_nor *nor,
if (ret) if (ret)
return ret; return ret;
return spi_nor_set_addr_width(nor); return spi_nor_set_addr_nbytes(nor);
} }
/** /**
...@@ -2382,12 +2382,7 @@ static void spi_nor_no_sfdp_init_params(struct spi_nor *nor) ...@@ -2382,12 +2382,7 @@ static void spi_nor_no_sfdp_init_params(struct spi_nor *nor)
*/ */
erase_mask = 0; erase_mask = 0;
i = 0; i = 0;
if (no_sfdp_flags & SECT_4K_PMC) { if (no_sfdp_flags & SECT_4K) {
erase_mask |= BIT(i);
spi_nor_set_erase_type(&map->erase_type[i], 4096u,
SPINOR_OP_BE_4K_PMC);
i++;
} else if (no_sfdp_flags & SECT_4K) {
erase_mask |= BIT(i); erase_mask |= BIT(i);
spi_nor_set_erase_type(&map->erase_type[i], 4096u, spi_nor_set_erase_type(&map->erase_type[i], 4096u,
SPINOR_OP_BE_4K); SPINOR_OP_BE_4K);
...@@ -2497,7 +2492,6 @@ static void spi_nor_sfdp_init_params_deprecated(struct spi_nor *nor) ...@@ -2497,7 +2492,6 @@ static void spi_nor_sfdp_init_params_deprecated(struct spi_nor *nor)
if (spi_nor_parse_sfdp(nor)) { if (spi_nor_parse_sfdp(nor)) {
memcpy(nor->params, &sfdp_params, sizeof(*nor->params)); memcpy(nor->params, &sfdp_params, sizeof(*nor->params));
nor->addr_width = 0;
nor->flags &= ~SNOR_F_4B_OPCODES; nor->flags &= ~SNOR_F_4B_OPCODES;
} }
} }
...@@ -2718,7 +2712,7 @@ static int spi_nor_init(struct spi_nor *nor) ...@@ -2718,7 +2712,7 @@ static int spi_nor_init(struct spi_nor *nor)
nor->flags & SNOR_F_SWP_IS_VOLATILE)) nor->flags & SNOR_F_SWP_IS_VOLATILE))
spi_nor_try_unlock_all(nor); spi_nor_try_unlock_all(nor);
if (nor->addr_width == 4 && if (nor->addr_nbytes == 4 &&
nor->read_proto != SNOR_PROTO_8_8_8_DTR && nor->read_proto != SNOR_PROTO_8_8_8_DTR &&
!(nor->flags & SNOR_F_4B_OPCODES)) { !(nor->flags & SNOR_F_4B_OPCODES)) {
/* /*
...@@ -2730,7 +2724,7 @@ static int spi_nor_init(struct spi_nor *nor) ...@@ -2730,7 +2724,7 @@ static int spi_nor_init(struct spi_nor *nor)
*/ */
WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET, WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET,
"enabling reset hack; may not recover from unexpected reboots\n"); "enabling reset hack; may not recover from unexpected reboots\n");
nor->params->set_4byte_addr_mode(nor, true); return nor->params->set_4byte_addr_mode(nor, true);
} }
return 0; return 0;
...@@ -2845,7 +2839,7 @@ static void spi_nor_put_device(struct mtd_info *mtd) ...@@ -2845,7 +2839,7 @@ static void spi_nor_put_device(struct mtd_info *mtd)
void spi_nor_restore(struct spi_nor *nor) void spi_nor_restore(struct spi_nor *nor)
{ {
/* restore the addressing mode */ /* restore the addressing mode */
if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
nor->flags & SNOR_F_BROKEN_RESET) nor->flags & SNOR_F_BROKEN_RESET)
nor->params->set_4byte_addr_mode(nor, false); nor->params->set_4byte_addr_mode(nor, false);
...@@ -2989,7 +2983,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, ...@@ -2989,7 +2983,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
* - select op codes for (Fast) Read, Page Program and Sector Erase. * - select op codes for (Fast) Read, Page Program and Sector Erase.
* - set the number of dummy cycles (mode cycles + wait states). * - set the number of dummy cycles (mode cycles + wait states).
* - set the SPI protocols for register and memory accesses. * - set the SPI protocols for register and memory accesses.
* - set the address width. * - set the number of address bytes.
*/ */
ret = spi_nor_setup(nor, hwcaps); ret = spi_nor_setup(nor, hwcaps);
if (ret) if (ret)
...@@ -3030,7 +3024,7 @@ static int spi_nor_create_read_dirmap(struct spi_nor *nor) ...@@ -3030,7 +3024,7 @@ static int spi_nor_create_read_dirmap(struct spi_nor *nor)
{ {
struct spi_mem_dirmap_info info = { struct spi_mem_dirmap_info info = {
.op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),
SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0),
SPI_MEM_OP_DUMMY(nor->read_dummy, 0), SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
SPI_MEM_OP_DATA_IN(0, NULL, 0)), SPI_MEM_OP_DATA_IN(0, NULL, 0)),
.offset = 0, .offset = 0,
...@@ -3061,7 +3055,7 @@ static int spi_nor_create_write_dirmap(struct spi_nor *nor) ...@@ -3061,7 +3055,7 @@ static int spi_nor_create_write_dirmap(struct spi_nor *nor)
{ {
struct spi_mem_dirmap_info info = { struct spi_mem_dirmap_info info = {
.op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0),
SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0),
SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(0, NULL, 0)), SPI_MEM_OP_DATA_OUT(0, NULL, 0)),
.offset = 0, .offset = 0,
......
...@@ -84,9 +84,9 @@ ...@@ -84,9 +84,9 @@
SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA) SPI_MEM_OP_NO_DATA)
#define SPI_NOR_SECTOR_ERASE_OP(opcode, addr_width, addr) \ #define SPI_NOR_SECTOR_ERASE_OP(opcode, addr_nbytes, addr) \
SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \ SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \
SPI_MEM_OP_ADDR(addr_width, addr, 0), \ SPI_MEM_OP_ADDR(addr_nbytes, addr, 0), \
SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA) SPI_MEM_OP_NO_DATA)
...@@ -340,6 +340,11 @@ struct spi_nor_otp { ...@@ -340,6 +340,11 @@ struct spi_nor_otp {
* @writesize Minimal writable flash unit size. Defaults to 1. Set to * @writesize Minimal writable flash unit size. Defaults to 1. Set to
* ECC unit size for ECC-ed flashes. * ECC unit size for ECC-ed flashes.
* @page_size: the page size of the SPI NOR flash memory. * @page_size: the page size of the SPI NOR flash memory.
* @addr_nbytes: number of address bytes to send.
* @addr_mode_nbytes: number of address bytes of current address mode. Useful
* when the flash operates with 4B opcodes but needs the
* internal address mode for opcodes that don't have a 4B
* opcode correspondent.
* @rdsr_dummy: dummy cycles needed for Read Status Register command * @rdsr_dummy: dummy cycles needed for Read Status Register command
* in octal DTR mode. * in octal DTR mode.
* @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register * @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register
...@@ -372,6 +377,8 @@ struct spi_nor_flash_parameter { ...@@ -372,6 +377,8 @@ struct spi_nor_flash_parameter {
u64 size; u64 size;
u32 writesize; u32 writesize;
u32 page_size; u32 page_size;
u8 addr_nbytes;
u8 addr_mode_nbytes;
u8 rdsr_dummy; u8 rdsr_dummy;
u8 rdsr_addr_nbytes; u8 rdsr_addr_nbytes;
...@@ -429,7 +436,7 @@ struct spi_nor_fixups { ...@@ -429,7 +436,7 @@ struct spi_nor_fixups {
* isn't necessarily called a "sector" by the vendor. * isn't necessarily called a "sector" by the vendor.
* @n_sectors: the number of sectors. * @n_sectors: the number of sectors.
* @page_size: the flash's page size. * @page_size: the flash's page size.
* @addr_width: the flash's address width. * @addr_nbytes: number of address bytes to send.
* *
* @parse_sfdp: true when flash supports SFDP tables. The false value has no * @parse_sfdp: true when flash supports SFDP tables. The false value has no
* meaning. If one wants to skip the SFDP tables, one should * meaning. If one wants to skip the SFDP tables, one should
...@@ -457,7 +464,6 @@ struct spi_nor_fixups { ...@@ -457,7 +464,6 @@ struct spi_nor_fixups {
* flags are used together with the SPI_NOR_SKIP_SFDP flag. * flags are used together with the SPI_NOR_SKIP_SFDP flag.
* SPI_NOR_SKIP_SFDP: skip parsing of SFDP tables. * SPI_NOR_SKIP_SFDP: skip parsing of SFDP tables.
* SECT_4K: SPINOR_OP_BE_4K works uniformly. * SECT_4K: SPINOR_OP_BE_4K works uniformly.
* SECT_4K_PMC: SPINOR_OP_BE_4K_PMC works uniformly.
* SPI_NOR_DUAL_READ: flash supports Dual Read. * SPI_NOR_DUAL_READ: flash supports Dual Read.
* SPI_NOR_QUAD_READ: flash supports Quad Read. * SPI_NOR_QUAD_READ: flash supports Quad Read.
* SPI_NOR_OCTAL_READ: flash supports Octal Read. * SPI_NOR_OCTAL_READ: flash supports Octal Read.
...@@ -488,7 +494,7 @@ struct flash_info { ...@@ -488,7 +494,7 @@ struct flash_info {
unsigned sector_size; unsigned sector_size;
u16 n_sectors; u16 n_sectors;
u16 page_size; u16 page_size;
u16 addr_width; u8 addr_nbytes;
bool parse_sfdp; bool parse_sfdp;
u16 flags; u16 flags;
...@@ -505,7 +511,6 @@ struct flash_info { ...@@ -505,7 +511,6 @@ struct flash_info {
u8 no_sfdp_flags; u8 no_sfdp_flags;
#define SPI_NOR_SKIP_SFDP BIT(0) #define SPI_NOR_SKIP_SFDP BIT(0)
#define SECT_4K BIT(1) #define SECT_4K BIT(1)
#define SECT_4K_PMC BIT(2)
#define SPI_NOR_DUAL_READ BIT(3) #define SPI_NOR_DUAL_READ BIT(3)
#define SPI_NOR_QUAD_READ BIT(4) #define SPI_NOR_QUAD_READ BIT(4)
#define SPI_NOR_OCTAL_READ BIT(5) #define SPI_NOR_OCTAL_READ BIT(5)
...@@ -550,11 +555,11 @@ struct flash_info { ...@@ -550,11 +555,11 @@ struct flash_info {
.n_sectors = (_n_sectors), \ .n_sectors = (_n_sectors), \
.page_size = 256, \ .page_size = 256, \
#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width) \ #define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_nbytes) \
.sector_size = (_sector_size), \ .sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \ .n_sectors = (_n_sectors), \
.page_size = (_page_size), \ .page_size = (_page_size), \
.addr_width = (_addr_width), \ .addr_nbytes = (_addr_nbytes), \
.flags = SPI_NOR_NO_ERASE | SPI_NOR_NO_FR, \ .flags = SPI_NOR_NO_ERASE | SPI_NOR_NO_FR, \
#define OTP_INFO(_len, _n_regions, _base, _offset) \ #define OTP_INFO(_len, _n_regions, _base, _offset) \
......
...@@ -86,7 +86,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data) ...@@ -86,7 +86,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data)
seq_printf(s, "size\t\t%s\n", buf); seq_printf(s, "size\t\t%s\n", buf);
seq_printf(s, "write size\t%u\n", params->writesize); seq_printf(s, "write size\t%u\n", params->writesize);
seq_printf(s, "page size\t%u\n", params->page_size); seq_printf(s, "page size\t%u\n", params->page_size);
seq_printf(s, "address width\t%u\n", nor->addr_width); seq_printf(s, "address nbytes\t%u\n", nor->addr_nbytes);
seq_puts(s, "flags\t\t"); seq_puts(s, "flags\t\t");
spi_nor_print_flags(s, nor->flags, snor_f_names, sizeof(snor_f_names)); spi_nor_print_flags(s, nor->flags, snor_f_names, sizeof(snor_f_names));
......
...@@ -13,7 +13,7 @@ static const struct flash_info esmt_nor_parts[] = { ...@@ -13,7 +13,7 @@ static const struct flash_info esmt_nor_parts[] = {
{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64) { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
NO_SFDP_FLAGS(SECT_4K) }, NO_SFDP_FLAGS(SECT_4K) },
{ "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64) { "f25l32qa-2s", INFO(0x8c4116, 0, 64 * 1024, 64)
FLAGS(SPI_NOR_HAS_LOCK) FLAGS(SPI_NOR_HAS_LOCK)
NO_SFDP_FLAGS(SECT_4K) }, NO_SFDP_FLAGS(SECT_4K) },
{ "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128) { "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128)
......
...@@ -14,13 +14,13 @@ is25lp256_post_bfpt_fixups(struct spi_nor *nor, ...@@ -14,13 +14,13 @@ is25lp256_post_bfpt_fixups(struct spi_nor *nor,
const struct sfdp_bfpt *bfpt) const struct sfdp_bfpt *bfpt)
{ {
/* /*
* IS25LP256 supports 4B opcodes, but the BFPT advertises a * IS25LP256 supports 4B opcodes, but the BFPT advertises
* BFPT_DWORD1_ADDRESS_BYTES_3_ONLY address width. * BFPT_DWORD1_ADDRESS_BYTES_3_ONLY.
* Overwrite the address width advertised by the BFPT. * Overwrite the number of address bytes advertised by the BFPT.
*/ */
if ((bfpt->dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) == if ((bfpt->dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) ==
BFPT_DWORD1_ADDRESS_BYTES_3_ONLY) BFPT_DWORD1_ADDRESS_BYTES_3_ONLY)
nor->addr_width = 4; nor->params->addr_nbytes = 4;
return 0; return 0;
} }
...@@ -29,6 +29,21 @@ static const struct spi_nor_fixups is25lp256_fixups = { ...@@ -29,6 +29,21 @@ static const struct spi_nor_fixups is25lp256_fixups = {
.post_bfpt = is25lp256_post_bfpt_fixups, .post_bfpt = is25lp256_post_bfpt_fixups,
}; };
static void pm25lv_nor_late_init(struct spi_nor *nor)
{
struct spi_nor_erase_map *map = &nor->params->erase_map;
int i;
/* The PM25LV series has a different 4k sector erase opcode */
for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++)
if (map->erase_type[i].size == 4096)
map->erase_type[i].opcode = SPINOR_OP_BE_4K_PMC;
}
static const struct spi_nor_fixups pm25lv_nor_fixups = {
.late_init = pm25lv_nor_late_init,
};
static const struct flash_info issi_nor_parts[] = { static const struct flash_info issi_nor_parts[] = {
/* ISSI */ /* ISSI */
{ "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2) { "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2)
...@@ -62,9 +77,13 @@ static const struct flash_info issi_nor_parts[] = { ...@@ -62,9 +77,13 @@ static const struct flash_info issi_nor_parts[] = {
/* PMC */ /* PMC */
{ "pm25lv512", INFO(0, 0, 32 * 1024, 2) { "pm25lv512", INFO(0, 0, 32 * 1024, 2)
NO_SFDP_FLAGS(SECT_4K_PMC) }, NO_SFDP_FLAGS(SECT_4K)
.fixups = &pm25lv_nor_fixups
},
{ "pm25lv010", INFO(0, 0, 32 * 1024, 4) { "pm25lv010", INFO(0, 0, 32 * 1024, 4)
NO_SFDP_FLAGS(SECT_4K_PMC) }, NO_SFDP_FLAGS(SECT_4K)
.fixups = &pm25lv_nor_fixups
},
{ "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64) { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64)
NO_SFDP_FLAGS(SECT_4K) }, NO_SFDP_FLAGS(SECT_4K) },
}; };
......
...@@ -399,8 +399,16 @@ static int micron_st_nor_ready(struct spi_nor *nor) ...@@ -399,8 +399,16 @@ static int micron_st_nor_ready(struct spi_nor *nor)
return sr_ready; return sr_ready;
ret = micron_st_nor_read_fsr(nor, nor->bouncebuf); ret = micron_st_nor_read_fsr(nor, nor->bouncebuf);
if (ret) if (ret) {
return ret; /*
* Some controllers, such as Intel SPI, do not support low
* level operations such as reading the flag status
* register. They only expose small amount of high level
* operations to the software. If this is the case we use
* only the status register value.
*/
return ret == -EOPNOTSUPP ? sr_ready : ret;
}
if (nor->bouncebuf[0] & (FSR_E_ERR | FSR_P_ERR)) { if (nor->bouncebuf[0] & (FSR_E_ERR | FSR_P_ERR)) {
if (nor->bouncebuf[0] & FSR_E_ERR) if (nor->bouncebuf[0] & FSR_E_ERR)
......
...@@ -35,13 +35,13 @@ ...@@ -35,13 +35,13 @@
*/ */
int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf)
{ {
u8 addr_width, read_opcode, read_dummy; u8 addr_nbytes, read_opcode, read_dummy;
struct spi_mem_dirmap_desc *rdesc; struct spi_mem_dirmap_desc *rdesc;
enum spi_nor_protocol read_proto; enum spi_nor_protocol read_proto;
int ret; int ret;
read_opcode = nor->read_opcode; read_opcode = nor->read_opcode;
addr_width = nor->addr_width; addr_nbytes = nor->addr_nbytes;
read_dummy = nor->read_dummy; read_dummy = nor->read_dummy;
read_proto = nor->read_proto; read_proto = nor->read_proto;
rdesc = nor->dirmap.rdesc; rdesc = nor->dirmap.rdesc;
...@@ -54,7 +54,7 @@ int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) ...@@ -54,7 +54,7 @@ int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf)
ret = spi_nor_read_data(nor, addr, len, buf); ret = spi_nor_read_data(nor, addr, len, buf);
nor->read_opcode = read_opcode; nor->read_opcode = read_opcode;
nor->addr_width = addr_width; nor->addr_nbytes = addr_nbytes;
nor->read_dummy = read_dummy; nor->read_dummy = read_dummy;
nor->read_proto = read_proto; nor->read_proto = read_proto;
nor->dirmap.rdesc = rdesc; nor->dirmap.rdesc = rdesc;
...@@ -85,11 +85,11 @@ int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, ...@@ -85,11 +85,11 @@ int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len,
{ {
enum spi_nor_protocol write_proto; enum spi_nor_protocol write_proto;
struct spi_mem_dirmap_desc *wdesc; struct spi_mem_dirmap_desc *wdesc;
u8 addr_width, program_opcode; u8 addr_nbytes, program_opcode;
int ret, written; int ret, written;
program_opcode = nor->program_opcode; program_opcode = nor->program_opcode;
addr_width = nor->addr_width; addr_nbytes = nor->addr_nbytes;
write_proto = nor->write_proto; write_proto = nor->write_proto;
wdesc = nor->dirmap.wdesc; wdesc = nor->dirmap.wdesc;
...@@ -113,7 +113,7 @@ int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, ...@@ -113,7 +113,7 @@ int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len,
out: out:
nor->program_opcode = program_opcode; nor->program_opcode = program_opcode;
nor->addr_width = addr_width; nor->addr_nbytes = addr_nbytes;
nor->write_proto = write_proto; nor->write_proto = write_proto;
nor->dirmap.wdesc = wdesc; nor->dirmap.wdesc = wdesc;
......
...@@ -134,7 +134,7 @@ struct sfdp_4bait { ...@@ -134,7 +134,7 @@ struct sfdp_4bait {
/** /**
* spi_nor_read_raw() - raw read of serial flash memory. read_opcode, * spi_nor_read_raw() - raw read of serial flash memory. read_opcode,
* addr_width and read_dummy members of the struct spi_nor * addr_nbytes and read_dummy members of the struct spi_nor
* should be previously * should be previously
* set. * set.
* @nor: pointer to a 'struct spi_nor' * @nor: pointer to a 'struct spi_nor'
...@@ -178,21 +178,21 @@ static int spi_nor_read_raw(struct spi_nor *nor, u32 addr, size_t len, u8 *buf) ...@@ -178,21 +178,21 @@ static int spi_nor_read_raw(struct spi_nor *nor, u32 addr, size_t len, u8 *buf)
static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr, static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr,
size_t len, void *buf) size_t len, void *buf)
{ {
u8 addr_width, read_opcode, read_dummy; u8 addr_nbytes, read_opcode, read_dummy;
int ret; int ret;
read_opcode = nor->read_opcode; read_opcode = nor->read_opcode;
addr_width = nor->addr_width; addr_nbytes = nor->addr_nbytes;
read_dummy = nor->read_dummy; read_dummy = nor->read_dummy;
nor->read_opcode = SPINOR_OP_RDSFDP; nor->read_opcode = SPINOR_OP_RDSFDP;
nor->addr_width = 3; nor->addr_nbytes = 3;
nor->read_dummy = 8; nor->read_dummy = 8;
ret = spi_nor_read_raw(nor, addr, len, buf); ret = spi_nor_read_raw(nor, addr, len, buf);
nor->read_opcode = read_opcode; nor->read_opcode = read_opcode;
nor->addr_width = addr_width; nor->addr_nbytes = addr_nbytes;
nor->read_dummy = read_dummy; nor->read_dummy = read_dummy;
return ret; return ret;
...@@ -462,11 +462,13 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, ...@@ -462,11 +462,13 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) { switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) {
case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY: case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY:
case BFPT_DWORD1_ADDRESS_BYTES_3_OR_4: case BFPT_DWORD1_ADDRESS_BYTES_3_OR_4:
nor->addr_width = 3; params->addr_nbytes = 3;
params->addr_mode_nbytes = 3;
break; break;
case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY: case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY:
nor->addr_width = 4; params->addr_nbytes = 4;
params->addr_mode_nbytes = 4;
break; break;
default: default:
...@@ -637,12 +639,12 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, ...@@ -637,12 +639,12 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
} }
/** /**
* spi_nor_smpt_addr_width() - return the address width used in the * spi_nor_smpt_addr_nbytes() - return the number of address bytes used in the
* configuration detection command. * configuration detection command.
* @nor: pointer to a 'struct spi_nor' * @nor: pointer to a 'struct spi_nor'
* @settings: configuration detection command descriptor, dword1 * @settings: configuration detection command descriptor, dword1
*/ */
static u8 spi_nor_smpt_addr_width(const struct spi_nor *nor, const u32 settings) static u8 spi_nor_smpt_addr_nbytes(const struct spi_nor *nor, const u32 settings)
{ {
switch (settings & SMPT_CMD_ADDRESS_LEN_MASK) { switch (settings & SMPT_CMD_ADDRESS_LEN_MASK) {
case SMPT_CMD_ADDRESS_LEN_0: case SMPT_CMD_ADDRESS_LEN_0:
...@@ -653,7 +655,7 @@ static u8 spi_nor_smpt_addr_width(const struct spi_nor *nor, const u32 settings) ...@@ -653,7 +655,7 @@ static u8 spi_nor_smpt_addr_width(const struct spi_nor *nor, const u32 settings)
return 4; return 4;
case SMPT_CMD_ADDRESS_LEN_USE_CURRENT: case SMPT_CMD_ADDRESS_LEN_USE_CURRENT:
default: default:
return nor->addr_width; return nor->params->addr_mode_nbytes;
} }
} }
...@@ -690,7 +692,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, ...@@ -690,7 +692,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt,
u32 addr; u32 addr;
int err; int err;
u8 i; u8 i;
u8 addr_width, read_opcode, read_dummy; u8 addr_nbytes, read_opcode, read_dummy;
u8 read_data_mask, map_id; u8 read_data_mask, map_id;
/* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */ /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */
...@@ -698,7 +700,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, ...@@ -698,7 +700,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt,
if (!buf) if (!buf)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
addr_width = nor->addr_width; addr_nbytes = nor->addr_nbytes;
read_dummy = nor->read_dummy; read_dummy = nor->read_dummy;
read_opcode = nor->read_opcode; read_opcode = nor->read_opcode;
...@@ -709,7 +711,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, ...@@ -709,7 +711,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt,
break; break;
read_data_mask = SMPT_CMD_READ_DATA(smpt[i]); read_data_mask = SMPT_CMD_READ_DATA(smpt[i]);
nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]); nor->addr_nbytes = spi_nor_smpt_addr_nbytes(nor, smpt[i]);
nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]); nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]);
nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]); nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]);
addr = smpt[i + 1]; addr = smpt[i + 1];
...@@ -756,7 +758,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, ...@@ -756,7 +758,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt,
/* fall through */ /* fall through */
out: out:
kfree(buf); kfree(buf);
nor->addr_width = addr_width; nor->addr_nbytes = addr_nbytes;
nor->read_dummy = read_dummy; nor->read_dummy = read_dummy;
nor->read_opcode = read_opcode; nor->read_opcode = read_opcode;
return ret; return ret;
...@@ -1044,7 +1046,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, ...@@ -1044,7 +1046,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
/* /*
* We need at least one 4-byte op code per read, program and erase * We need at least one 4-byte op code per read, program and erase
* operation; the .read(), .write() and .erase() hooks share the * operation; the .read(), .write() and .erase() hooks share the
* nor->addr_width value. * nor->addr_nbytes value.
*/ */
if (!read_hwcaps || !pp_hwcaps || !erase_mask) if (!read_hwcaps || !pp_hwcaps || !erase_mask)
goto out; goto out;
...@@ -1098,7 +1100,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, ...@@ -1098,7 +1100,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
* Spansion memory. However this quirk is no longer needed with new * Spansion memory. However this quirk is no longer needed with new
* SFDP compliant memories. * SFDP compliant memories.
*/ */
nor->addr_width = 4; params->addr_nbytes = 4;
nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT; nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT;
/* fall through */ /* fall through */
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */ #define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */
#define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */ #define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */
#define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */ #define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */
#define SPINOR_REG_CYPRESS_CFR1V 0x00800002
#define SPINOR_REG_CYPRESS_CFR1V_QUAD_EN BIT(1) /* Quad Enable */
#define SPINOR_REG_CYPRESS_CFR2V 0x00800003 #define SPINOR_REG_CYPRESS_CFR2V 0x00800003
#define SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24 0xb #define SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24 0xb
#define SPINOR_REG_CYPRESS_CFR3V 0x00800004 #define SPINOR_REG_CYPRESS_CFR3V 0x00800004
...@@ -113,6 +115,150 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) ...@@ -113,6 +115,150 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor)
return 0; return 0;
} }
/**
* cypress_nor_quad_enable_volatile() - enable Quad I/O mode in volatile
* register.
* @nor: pointer to a 'struct spi_nor'
*
* It is recommended to update volatile registers in the field application due
* to a risk of the non-volatile registers corruption by power interrupt. This
* function sets Quad Enable bit in CFR1 volatile. If users set the Quad Enable
* bit in the CFR1 non-volatile in advance (typically by a Flash programmer
* before mounting Flash on PCB), the Quad Enable bit in the CFR1 volatile is
* also set during Flash power-up.
*
* Return: 0 on success, -errno otherwise.
*/
static int cypress_nor_quad_enable_volatile(struct spi_nor *nor)
{
struct spi_mem_op op;
u8 addr_mode_nbytes = nor->params->addr_mode_nbytes;
u8 cfr1v_written;
int ret;
op = (struct spi_mem_op)
CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes,
SPINOR_REG_CYPRESS_CFR1V,
nor->bouncebuf);
ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR1V_QUAD_EN)
return 0;
/* Update the Quad Enable bit. */
nor->bouncebuf[0] |= SPINOR_REG_CYPRESS_CFR1V_QUAD_EN;
op = (struct spi_mem_op)
CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes,
SPINOR_REG_CYPRESS_CFR1V, 1,
nor->bouncebuf);
ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
cfr1v_written = nor->bouncebuf[0];
/* Read back and check it. */
op = (struct spi_mem_op)
CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes,
SPINOR_REG_CYPRESS_CFR1V,
nor->bouncebuf);
ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
if (nor->bouncebuf[0] != cfr1v_written) {
dev_err(nor->dev, "CFR1: Read back test failed\n");
return -EIO;
}
return 0;
}
/**
* cypress_nor_set_page_size() - Set page size which corresponds to the flash
* configuration.
* @nor: pointer to a 'struct spi_nor'
*
* The BFPT table advertises a 512B or 256B page size depending on part but the
* page size is actually configurable (with the default being 256B). Read from
* CFR3V[4] and set the correct size.
*
* Return: 0 on success, -errno otherwise.
*/
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,
nor->bouncebuf);
int ret;
ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3V_PGSZ)
nor->params->page_size = 512;
else
nor->params->page_size = 256;
return 0;
}
static int
s25hx_t_post_bfpt_fixup(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt)
{
/* Replace Quad Enable with volatile version */
nor->params->quad_enable = cypress_nor_quad_enable_volatile;
return cypress_nor_set_page_size(nor);
}
static void s25hx_t_post_sfdp_fixup(struct spi_nor *nor)
{
struct spi_nor_erase_type *erase_type =
nor->params->erase_map.erase_type;
unsigned int i;
/*
* In some parts, 3byte erase opcodes are advertised by 4BAIT.
* Convert them to 4byte erase opcodes.
*/
for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
switch (erase_type[i].opcode) {
case SPINOR_OP_SE:
erase_type[i].opcode = SPINOR_OP_SE_4B;
break;
case SPINOR_OP_BE_4K:
erase_type[i].opcode = SPINOR_OP_BE_4K_4B;
break;
default:
break;
}
}
}
static void s25hx_t_late_init(struct spi_nor *nor)
{
struct spi_nor_flash_parameter *params = nor->params;
/* Fast Read 4B requires mode cycles */
params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8;
/* The writesize should be ECC data unit size */
params->writesize = 16;
}
static struct spi_nor_fixups s25hx_t_fixups = {
.post_bfpt = s25hx_t_post_bfpt_fixup,
.post_sfdp = s25hx_t_post_sfdp_fixup,
.late_init = s25hx_t_late_init,
};
/** /**
* cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes. * cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes.
* @nor: pointer to a 'struct spi_nor' * @nor: pointer to a 'struct spi_nor'
...@@ -167,28 +313,7 @@ static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor, ...@@ -167,28 +313,7 @@ static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header, const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt) const struct sfdp_bfpt *bfpt)
{ {
/* return cypress_nor_set_page_size(nor);
* The BFPT table advertises a 512B page size but the page size is
* actually configurable (with the default being 256B). Read from
* CFR3V[4] and set the correct size.
*/
struct spi_mem_op op =
CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR3V,
nor->bouncebuf);
int ret;
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
ret = spi_mem_exec_op(nor->spimem, &op);
if (ret)
return ret;
if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3V_PGSZ)
nor->params->page_size = 512;
else
nor->params->page_size = 256;
return 0;
} }
static const struct spi_nor_fixups s28hs512t_fixups = { static const struct spi_nor_fixups s28hs512t_fixups = {
...@@ -310,6 +435,22 @@ static const struct flash_info spansion_nor_parts[] = { ...@@ -310,6 +435,22 @@ static const struct flash_info spansion_nor_parts[] = {
{ "s25fl256l", INFO(0x016019, 0, 64 * 1024, 512) { "s25fl256l", INFO(0x016019, 0, 64 * 1024, 512)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, FIXUP_FLAGS(SPI_NOR_4B_OPCODES) },
{ "s25hl512t", INFO6(0x342a1a, 0x0f0390, 256 * 1024, 256)
PARSE_SFDP
MFR_FLAGS(USE_CLSR)
.fixups = &s25hx_t_fixups },
{ "s25hl01gt", INFO6(0x342a1b, 0x0f0390, 256 * 1024, 512)
PARSE_SFDP
MFR_FLAGS(USE_CLSR)
.fixups = &s25hx_t_fixups },
{ "s25hs512t", INFO6(0x342b1a, 0x0f0390, 256 * 1024, 256)
PARSE_SFDP
MFR_FLAGS(USE_CLSR)
.fixups = &s25hx_t_fixups },
{ "s25hs01gt", INFO6(0x342b1b, 0x0f0390, 256 * 1024, 512)
PARSE_SFDP
MFR_FLAGS(USE_CLSR)
.fixups = &s25hx_t_fixups },
{ "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1) { "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1)
FLAGS(SPI_NOR_NO_ERASE) }, FLAGS(SPI_NOR_NO_ERASE) },
{ "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256) { "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256)
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
.sector_size = (8 * (_page_size)), \ .sector_size = (8 * (_page_size)), \
.n_sectors = (_n_sectors), \ .n_sectors = (_n_sectors), \
.page_size = (_page_size), \ .page_size = (_page_size), \
.addr_width = 3, \ .addr_nbytes = 3, \
.flags = SPI_NOR_NO_FR .flags = SPI_NOR_NO_FR
/* Xilinx S3AN share MFR with Atmel SPI NOR */ /* Xilinx S3AN share MFR with Atmel SPI NOR */
......
...@@ -89,9 +89,7 @@ int hyperbus_register_device(struct hyperbus_device *hbdev); ...@@ -89,9 +89,7 @@ int hyperbus_register_device(struct hyperbus_device *hbdev);
/** /**
* hyperbus_unregister_device - deregister HyperBus slave memory device * hyperbus_unregister_device - deregister HyperBus slave memory device
* @hbdev: hyperbus_device to be unregistered * @hbdev: hyperbus_device to be unregistered
*
* Return: 0 for success, others for failure.
*/ */
int hyperbus_unregister_device(struct hyperbus_device *hbdev); void hyperbus_unregister_device(struct hyperbus_device *hbdev);
#endif /* __LINUX_MTD_HYPERBUS_H__ */ #endif /* __LINUX_MTD_HYPERBUS_H__ */
...@@ -351,7 +351,7 @@ struct spi_nor_flash_parameter; ...@@ -351,7 +351,7 @@ struct spi_nor_flash_parameter;
* @bouncebuf_size: size of the bounce buffer * @bouncebuf_size: size of the bounce buffer
* @info: SPI NOR part JEDEC MFR ID and other info * @info: SPI NOR part JEDEC MFR ID and other info
* @manufacturer: SPI NOR manufacturer * @manufacturer: SPI NOR manufacturer
* @addr_width: number of address bytes * @addr_nbytes: number of address bytes
* @erase_opcode: the opcode for erasing a sector * @erase_opcode: the opcode for erasing a sector
* @read_opcode: the read opcode * @read_opcode: the read opcode
* @read_dummy: the dummy needed by the read operation * @read_dummy: the dummy needed by the read operation
...@@ -381,7 +381,7 @@ struct spi_nor { ...@@ -381,7 +381,7 @@ struct spi_nor {
size_t bouncebuf_size; size_t bouncebuf_size;
const struct flash_info *info; const struct flash_info *info;
const struct spi_nor_manufacturer *manufacturer; const struct spi_nor_manufacturer *manufacturer;
u8 addr_width; u8 addr_nbytes;
u8 erase_opcode; u8 erase_opcode;
u8 read_opcode; u8 read_opcode;
u8 read_dummy; u8 read_dummy;
......
...@@ -260,6 +260,7 @@ struct spinand_manufacturer { ...@@ -260,6 +260,7 @@ struct spinand_manufacturer {
}; };
/* SPI NAND manufacturers */ /* SPI NAND manufacturers */
extern const struct spinand_manufacturer ato_spinand_manufacturer;
extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
extern const struct spinand_manufacturer macronix_spinand_manufacturer; extern const struct spinand_manufacturer macronix_spinand_manufacturer;
extern const struct spinand_manufacturer micron_spinand_manufacturer; extern const struct spinand_manufacturer micron_spinand_manufacturer;
......
...@@ -69,8 +69,8 @@ enum { ...@@ -69,8 +69,8 @@ enum {
* struct mtd_write_req - data structure for requesting a write operation * struct mtd_write_req - data structure for requesting a write operation
* *
* @start: start address * @start: start address
* @len: length of data buffer * @len: length of data buffer (only lower 32 bits are used)
* @ooblen: length of OOB buffer * @ooblen: length of OOB buffer (only lower 32 bits are used)
* @usr_data: user-provided data buffer * @usr_data: user-provided data buffer
* @usr_oob: user-provided OOB buffer * @usr_oob: user-provided OOB buffer
* @mode: MTD mode (see "MTD operation modes") * @mode: MTD mode (see "MTD operation modes")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment