mirror of
https://gitee.com/bianbu-linux/linux-6.6
synced 2025-04-24 14:07:52 -04:00
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: 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. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEdgfidid8lnn52cLTZvlZhesYu8EFAmLthF4ACgkQZvlZhesY u8FCkQ//QNJE67mHMWakQRAgjrJ7S8XAbr3BhWLAo/jKxWq0dp+UmPbR+ioia9bv wCgrKhHySdsHfyIsuSe+6oSfs0cqGxOo9o8ldW0fygcgX015/FQu72KEDuIQssl8 fK2x9IPiwB9a4gwJq8K8DdIrmQtzi4pu3L+xjylrhlJ+7P7pcjGsUSY8kIuW0FI9 1MD8SawOdtoch8STORnRop9XgPbFMJGx3/Oa7eUm9l6YDbKdtO+b2PJELfReXaHy zSTtvG+uP7SOSG7vEVgap76GaXrOklmsiPN62dp+Rk20aqU6DzpGFTdlG8TMMrMc x3CG4SyRbtCrb9UhZf5V/LMWMVZrRyuXUxTdNmFCAnTjONXq0UG6HEb4lrOxc7s7 Az286Ycc2DXCwDrqa50LWAw2nP8JKoRQiMU4Q/9f1UIpIpA7wSrwwtzX/q5LqbEd H+q6UOClw92nJZcimLbdyjo9Pauj8Cus76nuasSxSVIC1sWMKg2Y/vOnCgD8SQMj ISqO/3A7Dr2xUvKJmM7H3sfyyUWehnYjD/nNc1TOjWzOxAQOlOpMdY8+5I/9Pc0Y q9NiCNFiBht14Fc/klWsg8xtNMpPAi7pua1F04meK6Z//9uUbiiN5el/snEQ1uCF 8o3ILFVpCgHIv2kmjGBBwxzXA9W6V1z/qTundQHJoSRc9DS8VAQ= =eBd9 -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
74cae210a3
50 changed files with 1110 additions and 252 deletions
|
@ -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: |
|
||||||
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:
|
||||||
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:
|
||||||
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:
|
||||||
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:
|
||||||
- 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:
|
||||||
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/
|
||||||
|
|
||||||
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[] = {
|
||||||
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 = {
|
||||||
|
|
||||||
.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)
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
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)
|
||||||
* 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)
|
||||||
(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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
}
|
}
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
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)
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
/* 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,
|
||||||
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)
|
||||||
{
|
{
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
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,
|
||||||
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 @@ err_clk:
|
||||||
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)
|
||||||
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 @@
|
||||||
#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 @@
|
||||||
#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, \
|
||||||
* @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, \
|
||||||
* @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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
/*
|
/*
|
||||||
* NAND controller data struct
|
* NAND controller data struct
|
||||||
*
|
*
|
||||||
|
* @dev: parent device
|
||||||
|
*
|
||||||
|
* @base: MMIO base
|
||||||
|
*
|
||||||
|
* @core_clk: 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
|
* @controller: base controller structure
|
||||||
* @host_list: list containing all the chips attached to the
|
* @host_list: list containing all the chips attached to the
|
||||||
* controller
|
* controller
|
||||||
* @dev: parent device
|
|
||||||
* @base: MMIO base
|
|
||||||
* @base_phys: physical base address of controller registers
|
|
||||||
* @base_dma: dma base address of controller registers
|
|
||||||
* @core_clk: controller clock
|
|
||||||
* @aon_clk: another controller clock
|
|
||||||
*
|
*
|
||||||
* @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
|
||||||
|
* @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
|
||||||
|
*
|
||||||
* @buf_size/count/start: markers for chip->legacy.read_buf/write_buf
|
* @buf_size/count/start: markers for chip->legacy.read_buf/write_buf
|
||||||
* functions
|
* functions
|
||||||
* @reg_read_buf: local buffer for reading back registers via DMA
|
|
||||||
* @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
|
|
||||||
* writes. contains the register values to be
|
|
||||||
* 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 {
|
||||||
};
|
};
|
||||||
|
|
||||||
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 {
|
||||||
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,
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
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)
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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,
|
||||||
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)
|
||||||
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 = {
|
||||||
.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)
|
||||||
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);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
# 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
|
||||||
|
|
86
drivers/mtd/nand/spi/ato.c
Normal file
86
drivers/mtd/nand/spi/ato.c
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
// 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 = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct spinand_manufacturer *spinand_manufacturers[] = {
|
static const struct spinand_manufacturer *spinand_manufacturers[] = {
|
||||||
|
&ato_spinand_manufacturer,
|
||||||
&gigadevice_spinand_manufacturer,
|
&gigadevice_spinand_manufacturer,
|
||||||
¯onix_spinand_manufacturer,
|
¯onix_spinand_manufacturer,
|
||||||
µn_spinand_manufacturer,
|
µn_spinand_manufacturer,
|
||||||
|
|
|
@ -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
|
||||||
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)
|
||||||
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)
|
||||||
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;
|
||||||
|
|
||||||
|
|
249
drivers/mtd/parsers/scpart.c
Normal file
249
drivers/mtd/parsers/scpart.c
Normal file
|
@ -0,0 +1,249 @@
|
||||||
|
// 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)
|
||||||
{
|
{
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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 @@
|
||||||
*/
|
*/
|
||||||
#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,
|
||||||
|
|
||||||
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,
|
||||||
{
|
{
|
||||||
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,
|
||||||
{
|
{
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
* 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,
|
||||||
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,
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
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,
|
||||||
* - 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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
{
|
{
|
||||||
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 @@
|
||||||
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 {
|
||||||
* @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 {
|
||||||
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 {
|
||||||
* 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 {
|
||||||
* 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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
.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)
|
||||||
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[] = {
|
||||||
{ "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,
|
||||||
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 = {
|
||||||
.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[] = {
|
||||||
|
|
||||||
/* 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)
|
||||||
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 @@
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
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,
|
||||||
{
|
{
|
||||||
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,
|
||||||
|
|
||||||
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 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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)
|
||||||
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,
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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)
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
/* 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,
|
||||||
/*
|
/*
|
||||||
* 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,
|
||||||
* 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 @@
|
||||||
#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)
|
||||||
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,
|
||||||
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[] = {
|
||||||
{ "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 @@
|
||||||
.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);
|
||||||
/**
|
/**
|
||||||
* 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;
|
||||||
* @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 {
|
||||||
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 {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* 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 {
|
||||||
* 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")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue