mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00: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
@ -37,6 +37,4 @@ examples:
|
||||
compatible = "fsl,imx27-nand";
|
||||
reg = <0xd8000000 0x1000>;
|
||||
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
|
||||
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:
|
||||
- Rafał Miłecki <rafal@milecki.pl>
|
||||
|
||||
@ -41,7 +52,12 @@ properties:
|
||||
immune to paired-pages corruptions
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- reg
|
||||
if:
|
||||
not:
|
||||
required: [ reg ]
|
||||
then:
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: '^partition-.*$'
|
||||
|
||||
additionalProperties: true
|
||||
|
@ -19,6 +19,10 @@ properties:
|
||||
compatible:
|
||||
const: qcom,smem-part
|
||||
|
||||
patternProperties:
|
||||
"^partition-[0-9a-z]+$":
|
||||
$ref: partition.yaml#
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
@ -31,3 +35,26 @@ examples:
|
||||
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: 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:
|
||||
- compatible
|
||||
- reg
|
||||
@ -135,6 +160,8 @@ examples:
|
||||
nand-ecc-strength = <4>;
|
||||
nand-bus-width = <8>;
|
||||
|
||||
qcom,boot-partitions = <0x0 0x58a0000>;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
|
@ -19130,7 +19130,7 @@ F: drivers/pinctrl/spear/
|
||||
|
||||
SPI NOR SUBSYSTEM
|
||||
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>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
S: Maintained
|
||||
|
@ -112,6 +112,13 @@ static const struct of_device_id dataflash_dt_ids[] = {
|
||||
MODULE_DEVICE_TABLE(of, dataflash_dt_ids);
|
||||
#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,
|
||||
.remove = dataflash_remove,
|
||||
.id_table = dataflash_spi_ids,
|
||||
|
||||
/* 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));
|
||||
|
||||
/* 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[] = {
|
||||
|
@ -1045,13 +1045,9 @@ static int spear_smi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spear_smi *dev;
|
||||
struct spear_snor_flash *flash;
|
||||
int ret, i;
|
||||
int i;
|
||||
|
||||
dev = platform_get_drvdata(pdev);
|
||||
if (!dev) {
|
||||
dev_err(&pdev->dev, "dev is null\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* clean up for all nor flash */
|
||||
for (i = 0; i < dev->num_flashes; i++) {
|
||||
@ -1060,9 +1056,7 @@ static int spear_smi_remove(struct platform_device *pdev)
|
||||
continue;
|
||||
|
||||
/* clean up mtd stuff */
|
||||
ret = mtd_device_unregister(&flash->mtd);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "error removing mtd\n");
|
||||
WARN_ON(mtd_device_unregister(&flash->mtd));
|
||||
}
|
||||
|
||||
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
|
||||
* device flags.
|
||||
*/
|
||||
if (info->config) {
|
||||
if (info->config)
|
||||
ret = info->config(fsm);
|
||||
if (ret)
|
||||
goto err_clk_unprepare;
|
||||
} else {
|
||||
else
|
||||
ret = stfsm_prepare_rwe_seqs_default(fsm);
|
||||
if (ret)
|
||||
goto err_clk_unprepare;
|
||||
}
|
||||
if (ret)
|
||||
goto err_clk_unprepare;
|
||||
|
||||
fsm->mtd.name = info->name;
|
||||
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),
|
||||
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:
|
||||
clk_disable_unprepare(fsm->clk);
|
||||
clk_disable_unprepare(fsm->clk);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2126,9 +2125,11 @@ static int stfsm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct stfsm *fsm = platform_get_drvdata(pdev);
|
||||
|
||||
WARN_ON(mtd_device_unregister(&fsm->mtd));
|
||||
|
||||
clk_disable_unprepare(fsm->clk);
|
||||
|
||||
return mtd_device_unregister(&fsm->mtd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#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_device_priv *dev_priv = priv->hbdev.priv;
|
||||
int ret;
|
||||
|
||||
ret = hyperbus_unregister_device(&priv->hbdev);
|
||||
hyperbus_unregister_device(&priv->hbdev);
|
||||
|
||||
if (priv->mux_ctrl)
|
||||
mux_control_deselect(priv->mux_ctrl);
|
||||
|
||||
if (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[] = {
|
||||
|
@ -126,16 +126,12 @@ int hyperbus_register_device(struct hyperbus_device *hbdev)
|
||||
}
|
||||
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) {
|
||||
ret = mtd_device_unregister(hbdev->mtd);
|
||||
WARN_ON(mtd_device_unregister(hbdev->mtd));
|
||||
map_destroy(hbdev->mtd);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
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);
|
||||
if (error)
|
||||
return error;
|
||||
goto out_disable_rpm;
|
||||
|
||||
hyperbus->hbdev.map.size = hyperbus->rpc.size;
|
||||
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);
|
||||
error = hyperbus_register_device(&hyperbus->hbdev);
|
||||
if (error)
|
||||
rpcif_disable_rpm(&hyperbus->rpc);
|
||||
goto out_disable_rpm;
|
||||
|
||||
return 0;
|
||||
|
||||
out_disable_rpm:
|
||||
rpcif_disable_rpm(&hyperbus->rpc);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int rpcif_hb_remove(struct platform_device *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);
|
||||
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 */
|
||||
|
@ -66,18 +66,12 @@ static int physmap_flash_remove(struct platform_device *dev)
|
||||
{
|
||||
struct physmap_flash_info *info;
|
||||
struct physmap_flash_data *physmap_data;
|
||||
int i, err = 0;
|
||||
int i;
|
||||
|
||||
info = platform_get_drvdata(dev);
|
||||
if (!info) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (info->cmtd) {
|
||||
err = mtd_device_unregister(info->cmtd);
|
||||
if (err)
|
||||
goto out;
|
||||
WARN_ON(mtd_device_unregister(info->cmtd));
|
||||
|
||||
if (info->cmtd != info->mtds[0])
|
||||
mtd_concat_destroy(info->cmtd);
|
||||
@ -92,10 +86,9 @@ static int physmap_flash_remove(struct platform_device *dev)
|
||||
if (physmap_data && physmap_data->exit)
|
||||
physmap_data->exit(dev);
|
||||
|
||||
out:
|
||||
pm_runtime_put(&dev->dev);
|
||||
pm_runtime_disable(&dev->dev);
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
ebi_base = of_iomap(ebi, 0);
|
||||
of_node_put(ebi);
|
||||
if (!ebi_base)
|
||||
return -ENODEV;
|
||||
|
||||
@ -207,6 +208,7 @@ int of_flash_probe_versatile(struct platform_device *pdev,
|
||||
|
||||
versatile_flashprot = (enum versatile_flashprot)devid->data;
|
||||
rmap = syscon_node_to_regmap(sysnp);
|
||||
of_node_put(sysnp);
|
||||
if (IS_ERR(rmap))
|
||||
return PTR_ERR(rmap);
|
||||
|
||||
|
@ -615,21 +615,24 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
|
||||
if (!usr_oob)
|
||||
req.ooblen = 0;
|
||||
|
||||
req.len &= 0xffffffff;
|
||||
req.ooblen &= 0xffffffff;
|
||||
|
||||
if (req.start + req.len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
datbuf_len = min_t(size_t, req.len, mtd->erasesize);
|
||||
if (datbuf_len > 0) {
|
||||
datbuf = kmalloc(datbuf_len, GFP_KERNEL);
|
||||
datbuf = kvmalloc(datbuf_len, GFP_KERNEL);
|
||||
if (!datbuf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
oobbuf_len = min_t(size_t, req.ooblen, mtd->erasesize);
|
||||
if (oobbuf_len > 0) {
|
||||
oobbuf = kmalloc(oobbuf_len, GFP_KERNEL);
|
||||
oobbuf = kvmalloc(oobbuf_len, GFP_KERNEL);
|
||||
if (!oobbuf) {
|
||||
kfree(datbuf);
|
||||
kvfree(datbuf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
@ -679,8 +682,8 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
|
||||
usr_oob += ops.oobretlen;
|
||||
}
|
||||
|
||||
kfree(datbuf);
|
||||
kfree(oobbuf);
|
||||
kvfree(datbuf);
|
||||
kvfree(oobbuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -546,6 +546,68 @@ static int mtd_nvmem_add(struct mtd_info *mtd)
|
||||
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
|
||||
* @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);
|
||||
dev_set_name(&mtd->dev, "mtd%d", i);
|
||||
dev_set_drvdata(&mtd->dev, mtd);
|
||||
mtd_check_of_node(mtd);
|
||||
of_node_get(mtd_get_of_node(mtd));
|
||||
error = device_register(&mtd->dev);
|
||||
if (error)
|
||||
|
@ -347,17 +347,17 @@ static int anfc_select_target(struct nand_chip *chip, int target)
|
||||
|
||||
/* Update clock frequency */
|
||||
if (nfc->cur_clk != anand->clk) {
|
||||
clk_disable_unprepare(nfc->controller_clk);
|
||||
ret = clk_set_rate(nfc->controller_clk, anand->clk);
|
||||
clk_disable_unprepare(nfc->bus_clk);
|
||||
ret = clk_set_rate(nfc->bus_clk, anand->clk);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev, "Failed to change clock rate\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(nfc->controller_clk);
|
||||
ret = clk_prepare_enable(nfc->bus_clk);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev,
|
||||
"Failed to re-enable the controller clock\n");
|
||||
"Failed to re-enable the bus clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1043,7 +1043,13 @@ static int anfc_setup_interface(struct nand_chip *chip, int target,
|
||||
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
|
||||
|
@ -2629,7 +2629,9 @@ static int atmel_nand_controller_remove(struct platform_device *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)
|
||||
|
@ -679,8 +679,10 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
||||
pci_set_master(pdev);
|
||||
|
||||
cafe = kzalloc(sizeof(*cafe), GFP_KERNEL);
|
||||
if (!cafe)
|
||||
return -ENOMEM;
|
||||
if (!cafe) {
|
||||
err = -ENOMEM;
|
||||
goto out_disable_device;
|
||||
}
|
||||
|
||||
mtd = nand_to_mtd(&cafe->nand);
|
||||
mtd->dev.parent = &pdev->dev;
|
||||
@ -801,6 +803,8 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
||||
pci_iounmap(pdev, cafe->mmio);
|
||||
out_free_mtd:
|
||||
kfree(cafe);
|
||||
out_disable_device:
|
||||
pci_disable_device(pdev);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
@ -822,6 +826,7 @@ static void cafe_nand_remove(struct pci_dev *pdev)
|
||||
pci_iounmap(pdev, cafe->mmio);
|
||||
dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
|
||||
kfree(cafe);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id cafe_nand_tbl[] = {
|
||||
|
@ -1293,26 +1293,20 @@ meson_nfc_nand_chip_init(struct device *dev,
|
||||
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 mtd_info *mtd;
|
||||
int ret;
|
||||
|
||||
while (!list_empty(&nfc->chips)) {
|
||||
meson_chip = list_first_entry(&nfc->chips,
|
||||
struct meson_nfc_nand_chip, node);
|
||||
mtd = nand_to_mtd(&meson_chip->nand);
|
||||
ret = mtd_device_unregister(mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
WARN_ON(mtd_device_unregister(mtd));
|
||||
|
||||
meson_nfc_free_buffer(&meson_chip->nand);
|
||||
nand_cleanup(&meson_chip->nand);
|
||||
list_del(&meson_chip->node);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct meson_nfc *nfc = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
ret = meson_nfc_nand_chip_cleanup(nfc);
|
||||
if (ret)
|
||||
return ret;
|
||||
meson_nfc_nand_chip_cleanup(nfc);
|
||||
|
||||
meson_nfc_disable_clk(nfc);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2278,16 +2278,14 @@ static int omap_nand_remove(struct platform_device *pdev)
|
||||
struct mtd_info *mtd = platform_get_drvdata(pdev);
|
||||
struct nand_chip *nand_chip = mtd_to_nand(mtd);
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
int ret;
|
||||
|
||||
rawnand_sw_bch_cleanup(nand_chip);
|
||||
|
||||
if (info->dma)
|
||||
dma_release_channel(info->dma);
|
||||
ret = mtd_device_unregister(mtd);
|
||||
WARN_ON(ret);
|
||||
WARN_ON(mtd_device_unregister(mtd));
|
||||
nand_cleanup(nand_chip);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* omap_nand_ids defined in linux/platform_data/mtd-nand-omap2.h */
|
||||
|
@ -80,8 +80,10 @@
|
||||
#define DISABLE_STATUS_AFTER_WRITE 4
|
||||
#define CW_PER_PAGE 6
|
||||
#define UD_SIZE_BYTES 9
|
||||
#define UD_SIZE_BYTES_MASK GENMASK(18, 9)
|
||||
#define ECC_PARITY_SIZE_BYTES_RS 19
|
||||
#define SPARE_SIZE_BYTES 23
|
||||
#define SPARE_SIZE_BYTES_MASK GENMASK(26, 23)
|
||||
#define NUM_ADDR_CYCLES 27
|
||||
#define STATUS_BFR_READ 30
|
||||
#define SET_RD_MODE_AFTER_STATUS 31
|
||||
@ -102,6 +104,7 @@
|
||||
#define ECC_MODE 4
|
||||
#define ECC_PARITY_SIZE_BYTES_BCH 8
|
||||
#define ECC_NUM_DATA_BYTES 16
|
||||
#define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16)
|
||||
#define ECC_FORCE_CLK_OPEN 30
|
||||
|
||||
/* NAND_DEV_CMD1 bits */
|
||||
@ -238,6 +241,9 @@ nandc_set_reg(chip, reg, \
|
||||
* @bam_ce - the array of BAM command elements
|
||||
* @cmd_sgl - sgl for NAND BAM command 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_start - the index in bam_ce which marks the start position ce
|
||||
* 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.
|
||||
* @wait_second_completion - wait for second DMA desc completion before making
|
||||
* 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_cmd_element *bam_ce;
|
||||
struct scatterlist *cmd_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_start;
|
||||
u32 cmd_sgl_pos;
|
||||
@ -267,25 +273,23 @@ struct bam_transaction {
|
||||
u32 rx_sgl_pos;
|
||||
u32 rx_sgl_start;
|
||||
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
|
||||
* @dma_desc - low level DMA engine descriptor
|
||||
* @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
|
||||
* @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
|
||||
* @dma_desc - low level DMA engine descriptor
|
||||
* @dir - DMA transfer direction
|
||||
*/
|
||||
struct desc_info {
|
||||
struct dma_async_tx_descriptor *dma_desc;
|
||||
struct list_head node;
|
||||
|
||||
enum dma_data_direction dir;
|
||||
union {
|
||||
struct scatterlist adm_sgl;
|
||||
struct {
|
||||
@ -293,7 +297,7 @@ struct desc_info {
|
||||
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
|
||||
*
|
||||
* @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
|
||||
* @host_list: list containing all the chips attached to the
|
||||
* 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
|
||||
* @cmd_crci: ADM DMA CRCI for command flow control
|
||||
* @data_crci: ADM DMA CRCI for data flow control
|
||||
*
|
||||
* @desc_list: DMA descriptor list (list of desc_infos)
|
||||
*
|
||||
* @data_buffer: our local DMA buffer for page read/writes,
|
||||
* used when we can't use the buffer provided
|
||||
* 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
|
||||
* 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
|
||||
* 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 nand_controller controller;
|
||||
struct list_head host_list;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
void __iomem *base;
|
||||
phys_addr_t base_phys;
|
||||
dma_addr_t base_dma;
|
||||
|
||||
struct clk *core_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 {
|
||||
/* will be used only by QPIC for BAM DMA */
|
||||
struct {
|
||||
@ -400,64 +416,89 @@ struct qcom_nand_controller {
|
||||
};
|
||||
|
||||
struct list_head desc_list;
|
||||
struct bam_transaction *bam_txn;
|
||||
|
||||
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_count;
|
||||
int buf_start;
|
||||
unsigned int max_cwperpage;
|
||||
|
||||
__le32 *reg_read_buf;
|
||||
dma_addr_t reg_read_dma;
|
||||
int reg_read_pos;
|
||||
|
||||
struct nandc_regs *regs;
|
||||
|
||||
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
|
||||
*
|
||||
* @boot_partitions: array of boot partitions where offset and size of the
|
||||
* boot partitions are stored
|
||||
*
|
||||
* @chip: base NAND chip structure
|
||||
* @node: list node to add itself to host_list in
|
||||
* 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
|
||||
* @cw_size: the number of bytes in a single step/codeword
|
||||
* of a page, consisting of all data, ecc, spare
|
||||
* and reserved bytes
|
||||
* @cw_data: the number of bytes within a codeword protected
|
||||
* 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
|
||||
* chip
|
||||
* @status: value to be returned if NAND_CMD_STATUS command
|
||||
* is executed
|
||||
*
|
||||
* @last_command: keeps track of last command on this chip. used
|
||||
* for reading correct status
|
||||
*
|
||||
* @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for
|
||||
* ecc/non-ecc mode for the current nand flash
|
||||
* 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_boot_partition *boot_partitions;
|
||||
|
||||
struct nand_chip chip;
|
||||
struct list_head node;
|
||||
|
||||
int nr_boot_partitions;
|
||||
|
||||
int cs;
|
||||
int cw_size;
|
||||
int cw_data;
|
||||
bool use_ecc;
|
||||
bool bch_enabled;
|
||||
int ecc_bytes_hw;
|
||||
int spare_bytes;
|
||||
int bbm_size;
|
||||
u8 status;
|
||||
|
||||
int last_command;
|
||||
|
||||
u32 cfg0, cfg1;
|
||||
@ -466,23 +507,30 @@ struct qcom_nand_host {
|
||||
u32 ecc_bch_cfg;
|
||||
u32 clrflashstatus;
|
||||
u32 clrreadstatus;
|
||||
|
||||
u8 status;
|
||||
bool codeword_fixup;
|
||||
bool use_ecc;
|
||||
bool bch_enabled;
|
||||
};
|
||||
|
||||
/*
|
||||
* This data type corresponds to the NAND controller properties which varies
|
||||
* among different NAND controllers.
|
||||
* @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_qpic - whether NAND CTRL is part of qpic IP
|
||||
* @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 {
|
||||
u32 ecc_modes;
|
||||
u32 dev_cmd_reg_start;
|
||||
bool is_bam;
|
||||
bool is_qpic;
|
||||
bool qpic_v2;
|
||||
u32 dev_cmd_reg_start;
|
||||
bool use_codeword_fixup;
|
||||
};
|
||||
|
||||
/* 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);
|
||||
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 -
|
||||
((ecc->steps - 1) * 4);
|
||||
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) {
|
||||
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);
|
||||
oob_size = (ecc->steps * 4) + host->ecc_bytes_hw;
|
||||
} 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++) {
|
||||
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);
|
||||
oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
|
||||
host->spare_bytes;
|
||||
@ -2037,6 +2085,69 @@ static int copy_last_cw(struct qcom_nand_host *host, int page)
|
||||
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() */
|
||||
static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf,
|
||||
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);
|
||||
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);
|
||||
data_buf = buf;
|
||||
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;
|
||||
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++) {
|
||||
ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf,
|
||||
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 nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
|
||||
if (host->nr_boot_partitions)
|
||||
qcom_nandc_codeword_fixup(host, page);
|
||||
|
||||
clear_read_regs(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;
|
||||
int i, ret;
|
||||
|
||||
if (host->nr_boot_partitions)
|
||||
qcom_nandc_codeword_fixup(host, page);
|
||||
|
||||
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
|
||||
|
||||
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++) {
|
||||
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);
|
||||
oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
|
||||
host->spare_bytes;
|
||||
@ -2176,6 +2299,9 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip,
|
||||
u8 *data_buf, *oob_buf;
|
||||
int i, ret;
|
||||
|
||||
if (host->nr_boot_partitions)
|
||||
qcom_nandc_codeword_fixup(host, page);
|
||||
|
||||
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
|
||||
clear_read_regs(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);
|
||||
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 -
|
||||
((ecc->steps - 1) << 2);
|
||||
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 ret;
|
||||
|
||||
if (host->nr_boot_partitions)
|
||||
qcom_nandc_codeword_fixup(host, page);
|
||||
|
||||
host->use_ecc = true;
|
||||
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 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,
|
||||
struct qcom_nand_host *host,
|
||||
struct device_node *dn)
|
||||
@ -2972,6 +3169,14 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
|
||||
if (ret)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -3137,6 +3342,7 @@ static int qcom_nandc_remove(struct platform_device *pdev)
|
||||
static const struct qcom_nandc_props ipq806x_nandc_props = {
|
||||
.ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT),
|
||||
.is_bam = false,
|
||||
.use_codeword_fixup = true,
|
||||
.dev_cmd_reg_start = 0x0,
|
||||
};
|
||||
|
||||
|
@ -52,7 +52,7 @@ static const struct mtd_ooblayout_ops oob_sm_ops = {
|
||||
.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 */
|
||||
/* However it does preserve the bad block markers */
|
||||
/* 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 nand_chip *chip = ctrl->chip;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int ret;
|
||||
|
||||
ret = mtd_device_unregister(mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
WARN_ON(mtd_device_unregister(mtd));
|
||||
|
||||
nand_cleanup(chip);
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
# 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
|
||||
|
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[] = {
|
||||
&ato_spinand_manufacturer,
|
||||
&gigadevice_spinand_manufacturer,
|
||||
¯onix_spinand_manufacturer,
|
||||
µn_spinand_manufacturer,
|
||||
|
@ -186,3 +186,12 @@ config MTD_QCOMSMEM_PARTS
|
||||
help
|
||||
This provides support for parsing partitions from Shared Memory (SMEM)
|
||||
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_AFS_PARTS) += afs.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_REDBOOT_PARTS) += redboot.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);
|
||||
if (err) {
|
||||
pr_err("failed to parse %s\n", s + len + 1);
|
||||
of_node_put(root);
|
||||
return err;
|
||||
}
|
||||
|
||||
of_node_put(root);
|
||||
return offset << 10;
|
||||
}
|
||||
|
||||
of_node_put(root);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,7 @@ static void parse_redboot_of(struct mtd_info *master)
|
||||
return;
|
||||
|
||||
ret = of_property_read_u32(npart, "fis-index-block", &dirblock);
|
||||
of_node_put(npart);
|
||||
if (ret)
|
||||
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;
|
||||
|
||||
mutex_lock(&ftl->mutex);
|
||||
del_timer_sync(&ftl->timer);
|
||||
cancel_work_sync(&ftl->flush_work);
|
||||
mutex_lock(&ftl->mutex);
|
||||
sm_cache_flush(ftl);
|
||||
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 &= ~(FMC_CFG_OP_MODE_MASK | SPI_NOR_ADDR_MODE_MASK);
|
||||
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;
|
||||
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_FIELDFORM_ALL_SERIAL |
|
||||
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);
|
||||
|
||||
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 |
|
||||
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);
|
||||
|
||||
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 */
|
||||
if (spifi->nor.addr_width < 1 || spifi->nor.addr_width > 4)
|
||||
if (spifi->nor.addr_nbytes < 1 || spifi->nor.addr_nbytes > 4)
|
||||
return -EINVAL;
|
||||
|
||||
spifi->mcmd |= SPIFI_CMD_OPCODE(spifi->nor.read_opcode) |
|
||||
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;
|
||||
}
|
||||
|
@ -38,7 +38,7 @@
|
||||
*/
|
||||
#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_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)
|
||||
{
|
||||
if (spi_nor_protocol_is_dtr(nor->write_proto))
|
||||
if (spi_nor_protocol_is_dtr(nor->reg_proto))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
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 =
|
||||
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_DATA_IN(len, buf, 0));
|
||||
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 =
|
||||
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_DATA_OUT(len, buf, 0));
|
||||
ssize_t nbytes;
|
||||
@ -972,7 +972,7 @@ static int spi_nor_erase_chip(struct spi_nor *nor)
|
||||
if (nor->spimem) {
|
||||
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);
|
||||
} else {
|
||||
@ -1113,9 +1113,9 @@ int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
|
||||
if (nor->spimem) {
|
||||
struct spi_mem_op op =
|
||||
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);
|
||||
} 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
|
||||
* control
|
||||
*/
|
||||
for (i = nor->addr_width - 1; i >= 0; i--) {
|
||||
for (i = nor->addr_nbytes - 1; i >= 0; i--) {
|
||||
nor->bouncebuf[i] = addr & 0xff;
|
||||
addr >>= 8;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
/* already configured from SFDP */
|
||||
if (nor->params->addr_nbytes) {
|
||||
nor->addr_nbytes = nor->params->addr_nbytes;
|
||||
} 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 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.
|
||||
* 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
|
||||
* phase, and we will end the transaction with half a cycle left
|
||||
* 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.
|
||||
*/
|
||||
nor->addr_width = 4;
|
||||
} else if (nor->info->addr_width) {
|
||||
nor->addr_width = nor->info->addr_width;
|
||||
nor->addr_nbytes = 4;
|
||||
} else if (nor->info->addr_nbytes) {
|
||||
nor->addr_nbytes = nor->info->addr_nbytes;
|
||||
} 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 */
|
||||
nor->addr_width = 4;
|
||||
nor->addr_nbytes = 4;
|
||||
}
|
||||
|
||||
if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) {
|
||||
dev_dbg(nor->dev, "address width is too large: %u\n",
|
||||
nor->addr_width);
|
||||
if (nor->addr_nbytes > SPI_NOR_MAX_ADDR_NBYTES) {
|
||||
dev_dbg(nor->dev, "The number of address bytes is too large: %u\n",
|
||||
nor->addr_nbytes);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 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))
|
||||
spi_nor_set_4byte_opcodes(nor);
|
||||
|
||||
@ -2304,7 +2304,7 @@ static int spi_nor_setup(struct spi_nor *nor,
|
||||
if (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;
|
||||
i = 0;
|
||||
if (no_sfdp_flags & SECT_4K_PMC) {
|
||||
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) {
|
||||
if (no_sfdp_flags & SECT_4K) {
|
||||
erase_mask |= BIT(i);
|
||||
spi_nor_set_erase_type(&map->erase_type[i], 4096u,
|
||||
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)) {
|
||||
memcpy(nor->params, &sfdp_params, sizeof(*nor->params));
|
||||
nor->addr_width = 0;
|
||||
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))
|
||||
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->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,
|
||||
"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;
|
||||
@ -2845,7 +2839,7 @@ static void spi_nor_put_device(struct mtd_info *mtd)
|
||||
void spi_nor_restore(struct spi_nor *nor)
|
||||
{
|
||||
/* 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->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.
|
||||
* - set the number of dummy cycles (mode cycles + wait states).
|
||||
* - 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);
|
||||
if (ret)
|
||||
@ -3030,7 +3024,7 @@ static int spi_nor_create_read_dirmap(struct spi_nor *nor)
|
||||
{
|
||||
struct spi_mem_dirmap_info info = {
|
||||
.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_DATA_IN(0, NULL, 0)),
|
||||
.offset = 0,
|
||||
@ -3061,7 +3055,7 @@ static int spi_nor_create_write_dirmap(struct spi_nor *nor)
|
||||
{
|
||||
struct spi_mem_dirmap_info info = {
|
||||
.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_DATA_OUT(0, NULL, 0)),
|
||||
.offset = 0,
|
||||
|
@ -84,9 +84,9 @@
|
||||
SPI_MEM_OP_NO_DUMMY, \
|
||||
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_ADDR(addr_width, addr, 0), \
|
||||
SPI_MEM_OP_ADDR(addr_nbytes, addr, 0), \
|
||||
SPI_MEM_OP_NO_DUMMY, \
|
||||
SPI_MEM_OP_NO_DATA)
|
||||
|
||||
@ -340,6 +340,11 @@ struct spi_nor_otp {
|
||||
* @writesize Minimal writable flash unit size. Defaults to 1. Set to
|
||||
* ECC unit size for ECC-ed flashes.
|
||||
* @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
|
||||
* in octal DTR mode.
|
||||
* @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register
|
||||
@ -372,6 +377,8 @@ struct spi_nor_flash_parameter {
|
||||
u64 size;
|
||||
u32 writesize;
|
||||
u32 page_size;
|
||||
u8 addr_nbytes;
|
||||
u8 addr_mode_nbytes;
|
||||
u8 rdsr_dummy;
|
||||
u8 rdsr_addr_nbytes;
|
||||
|
||||
@ -429,7 +436,7 @@ struct spi_nor_fixups {
|
||||
* isn't necessarily called a "sector" by the vendor.
|
||||
* @n_sectors: the number of sectors.
|
||||
* @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
|
||||
* 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.
|
||||
* SPI_NOR_SKIP_SFDP: skip parsing of SFDP tables.
|
||||
* 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_QUAD_READ: flash supports Quad Read.
|
||||
* SPI_NOR_OCTAL_READ: flash supports Octal Read.
|
||||
@ -488,7 +494,7 @@ struct flash_info {
|
||||
unsigned sector_size;
|
||||
u16 n_sectors;
|
||||
u16 page_size;
|
||||
u16 addr_width;
|
||||
u8 addr_nbytes;
|
||||
|
||||
bool parse_sfdp;
|
||||
u16 flags;
|
||||
@ -505,7 +511,6 @@ struct flash_info {
|
||||
u8 no_sfdp_flags;
|
||||
#define SPI_NOR_SKIP_SFDP BIT(0)
|
||||
#define SECT_4K BIT(1)
|
||||
#define SECT_4K_PMC BIT(2)
|
||||
#define SPI_NOR_DUAL_READ BIT(3)
|
||||
#define SPI_NOR_QUAD_READ BIT(4)
|
||||
#define SPI_NOR_OCTAL_READ BIT(5)
|
||||
@ -550,11 +555,11 @@ struct flash_info {
|
||||
.n_sectors = (_n_sectors), \
|
||||
.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), \
|
||||
.n_sectors = (_n_sectors), \
|
||||
.page_size = (_page_size), \
|
||||
.addr_width = (_addr_width), \
|
||||
.addr_nbytes = (_addr_nbytes), \
|
||||
.flags = SPI_NOR_NO_ERASE | SPI_NOR_NO_FR, \
|
||||
|
||||
#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, "write size\t%u\n", params->writesize);
|
||||
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");
|
||||
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)
|
||||
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
|
||||
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)
|
||||
NO_SFDP_FLAGS(SECT_4K) },
|
||||
{ "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128)
|
||||
|
@ -14,13 +14,13 @@ is25lp256_post_bfpt_fixups(struct spi_nor *nor,
|
||||
const struct sfdp_bfpt *bfpt)
|
||||
{
|
||||
/*
|
||||
* IS25LP256 supports 4B opcodes, but the BFPT advertises a
|
||||
* BFPT_DWORD1_ADDRESS_BYTES_3_ONLY address width.
|
||||
* Overwrite the address width advertised by the BFPT.
|
||||
* IS25LP256 supports 4B opcodes, but the BFPT advertises
|
||||
* BFPT_DWORD1_ADDRESS_BYTES_3_ONLY.
|
||||
* Overwrite the number of address bytes advertised by the BFPT.
|
||||
*/
|
||||
if ((bfpt->dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) ==
|
||||
BFPT_DWORD1_ADDRESS_BYTES_3_ONLY)
|
||||
nor->addr_width = 4;
|
||||
nor->params->addr_nbytes = 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -29,6 +29,21 @@ static const struct spi_nor_fixups is25lp256_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[] = {
|
||||
/* ISSI */
|
||||
{ "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2)
|
||||
@ -62,9 +77,13 @@ static const struct flash_info issi_nor_parts[] = {
|
||||
|
||||
/* PMC */
|
||||
{ "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)
|
||||
NO_SFDP_FLAGS(SECT_4K_PMC) },
|
||||
NO_SFDP_FLAGS(SECT_4K)
|
||||
.fixups = &pm25lv_nor_fixups
|
||||
},
|
||||
{ "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64)
|
||||
NO_SFDP_FLAGS(SECT_4K) },
|
||||
};
|
||||
|
@ -399,8 +399,16 @@ static int micron_st_nor_ready(struct spi_nor *nor)
|
||||
return sr_ready;
|
||||
|
||||
ret = micron_st_nor_read_fsr(nor, nor->bouncebuf);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (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)
|
||||
|
@ -35,13 +35,13 @@
|
||||
*/
|
||||
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;
|
||||
enum spi_nor_protocol read_proto;
|
||||
int ret;
|
||||
|
||||
read_opcode = nor->read_opcode;
|
||||
addr_width = nor->addr_width;
|
||||
addr_nbytes = nor->addr_nbytes;
|
||||
read_dummy = nor->read_dummy;
|
||||
read_proto = nor->read_proto;
|
||||
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);
|
||||
|
||||
nor->read_opcode = read_opcode;
|
||||
nor->addr_width = addr_width;
|
||||
nor->addr_nbytes = addr_nbytes;
|
||||
nor->read_dummy = read_dummy;
|
||||
nor->read_proto = read_proto;
|
||||
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;
|
||||
struct spi_mem_dirmap_desc *wdesc;
|
||||
u8 addr_width, program_opcode;
|
||||
u8 addr_nbytes, program_opcode;
|
||||
int ret, written;
|
||||
|
||||
program_opcode = nor->program_opcode;
|
||||
addr_width = nor->addr_width;
|
||||
addr_nbytes = nor->addr_nbytes;
|
||||
write_proto = nor->write_proto;
|
||||
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:
|
||||
nor->program_opcode = program_opcode;
|
||||
nor->addr_width = addr_width;
|
||||
nor->addr_nbytes = addr_nbytes;
|
||||
nor->write_proto = write_proto;
|
||||
nor->dirmap.wdesc = wdesc;
|
||||
|
||||
|
@ -134,7 +134,7 @@ struct sfdp_4bait {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* set.
|
||||
* @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,
|
||||
size_t len, void *buf)
|
||||
{
|
||||
u8 addr_width, read_opcode, read_dummy;
|
||||
u8 addr_nbytes, read_opcode, read_dummy;
|
||||
int ret;
|
||||
|
||||
read_opcode = nor->read_opcode;
|
||||
addr_width = nor->addr_width;
|
||||
addr_nbytes = nor->addr_nbytes;
|
||||
read_dummy = nor->read_dummy;
|
||||
|
||||
nor->read_opcode = SPINOR_OP_RDSFDP;
|
||||
nor->addr_width = 3;
|
||||
nor->addr_nbytes = 3;
|
||||
nor->read_dummy = 8;
|
||||
|
||||
ret = spi_nor_read_raw(nor, addr, len, buf);
|
||||
|
||||
nor->read_opcode = read_opcode;
|
||||
nor->addr_width = addr_width;
|
||||
nor->addr_nbytes = addr_nbytes;
|
||||
nor->read_dummy = read_dummy;
|
||||
|
||||
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) {
|
||||
case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY:
|
||||
case BFPT_DWORD1_ADDRESS_BYTES_3_OR_4:
|
||||
nor->addr_width = 3;
|
||||
params->addr_nbytes = 3;
|
||||
params->addr_mode_nbytes = 3;
|
||||
break;
|
||||
|
||||
case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY:
|
||||
nor->addr_width = 4;
|
||||
params->addr_nbytes = 4;
|
||||
params->addr_mode_nbytes = 4;
|
||||
break;
|
||||
|
||||
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.
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
* @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) {
|
||||
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;
|
||||
case SMPT_CMD_ADDRESS_LEN_USE_CURRENT:
|
||||
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;
|
||||
int err;
|
||||
u8 i;
|
||||
u8 addr_width, read_opcode, read_dummy;
|
||||
u8 addr_nbytes, read_opcode, read_dummy;
|
||||
u8 read_data_mask, map_id;
|
||||
|
||||
/* 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)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
addr_width = nor->addr_width;
|
||||
addr_nbytes = nor->addr_nbytes;
|
||||
read_dummy = nor->read_dummy;
|
||||
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;
|
||||
|
||||
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_opcode = SMPT_CMD_OPCODE(smpt[i]);
|
||||
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 */
|
||||
out:
|
||||
kfree(buf);
|
||||
nor->addr_width = addr_width;
|
||||
nor->addr_nbytes = addr_nbytes;
|
||||
nor->read_dummy = read_dummy;
|
||||
nor->read_opcode = read_opcode;
|
||||
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
|
||||
* operation; the .read(), .write() and .erase() hooks share the
|
||||
* nor->addr_width value.
|
||||
* nor->addr_nbytes value.
|
||||
*/
|
||||
if (!read_hwcaps || !pp_hwcaps || !erase_mask)
|
||||
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
|
||||
* SFDP compliant memories.
|
||||
*/
|
||||
nor->addr_width = 4;
|
||||
params->addr_nbytes = 4;
|
||||
nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT;
|
||||
|
||||
/* fall through */
|
||||
|
@ -14,6 +14,8 @@
|
||||
#define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */
|
||||
#define SPINOR_OP_RD_ANY_REG 0x65 /* Read 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_MEMLAT_11_24 0xb
|
||||
#define SPINOR_REG_CYPRESS_CFR3V 0x00800004
|
||||
@ -113,6 +115,150 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor)
|
||||
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.
|
||||
* @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_bfpt *bfpt)
|
||||
{
|
||||
/*
|
||||
* 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;
|
||||
return cypress_nor_set_page_size(nor);
|
||||
}
|
||||
|
||||
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)
|
||||
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
|
||||
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)
|
||||
FLAGS(SPI_NOR_NO_ERASE) },
|
||||
{ "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256)
|
||||
|
@ -31,7 +31,7 @@
|
||||
.sector_size = (8 * (_page_size)), \
|
||||
.n_sectors = (_n_sectors), \
|
||||
.page_size = (_page_size), \
|
||||
.addr_width = 3, \
|
||||
.addr_nbytes = 3, \
|
||||
.flags = SPI_NOR_NO_FR
|
||||
|
||||
/* 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
|
||||
* @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__ */
|
||||
|
@ -351,7 +351,7 @@ struct spi_nor_flash_parameter;
|
||||
* @bouncebuf_size: size of the bounce buffer
|
||||
* @info: SPI NOR part JEDEC MFR ID and other info
|
||||
* @manufacturer: SPI NOR manufacturer
|
||||
* @addr_width: number of address bytes
|
||||
* @addr_nbytes: number of address bytes
|
||||
* @erase_opcode: the opcode for erasing a sector
|
||||
* @read_opcode: the read opcode
|
||||
* @read_dummy: the dummy needed by the read operation
|
||||
@ -381,7 +381,7 @@ struct spi_nor {
|
||||
size_t bouncebuf_size;
|
||||
const struct flash_info *info;
|
||||
const struct spi_nor_manufacturer *manufacturer;
|
||||
u8 addr_width;
|
||||
u8 addr_nbytes;
|
||||
u8 erase_opcode;
|
||||
u8 read_opcode;
|
||||
u8 read_dummy;
|
||||
|
@ -260,6 +260,7 @@ struct spinand_manufacturer {
|
||||
};
|
||||
|
||||
/* SPI NAND manufacturers */
|
||||
extern const struct spinand_manufacturer ato_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer macronix_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
|
||||
*
|
||||
* @start: start address
|
||||
* @len: length of data buffer
|
||||
* @ooblen: length of OOB buffer
|
||||
* @len: length of data buffer (only lower 32 bits are used)
|
||||
* @ooblen: length of OOB buffer (only lower 32 bits are used)
|
||||
* @usr_data: user-provided data buffer
|
||||
* @usr_oob: user-provided OOB buffer
|
||||
* @mode: MTD mode (see "MTD operation modes")
|
||||
|
Loading…
Reference in New Issue
Block a user