mirror of https://github.com/torvalds/linux.git
Core MTD changes:
- We must ignore error -ENOENT from parsers on subpartitions which is a
legitimate return value.
- PM support is added to the intel-dg driver.
Raw NAND changes:
- The major change in this MR will be the support for the Allwinner H616
NAND controller, which lead to numerous changes and cleanups in the
driver.
- Another notable change on this driver is the use of
field_get()/field_prep(), but since the global support for this
helpers is going to be merged in the same release as we start using
these helpers, it implies undefining them in the first place to avoid
warnings. Depending on the merging order (Yuri's bitmap branch or
mtd/next), a temporary warning may arise.
- Marvell drivers layout handling changes have also landed, they fix
previous definitions and abuses that have been made previously, which
implied to relax the ECC parameters validation in the core a bit.
- The Cadence NAND controller driver gets NV-DDR interface support.
SPI NAND changes:
- Support for FudanMicro FM25S01BI3 and ESMT F50L1G41LC is added.
SPI NOR changes:
- Fix SMPT parsing for S25FS-S flash family. They report variable dummy
cycles for reads. This results in the default of 0 being used. This
works for other Infineon chips, but not for the S25FS-S family. They
need 8 dummy cycles. Add fixup hooks to specify that. Also add fixup
hooks to fix incorrect map ID data in SFDP.
- Add support for a bunch of Winbond flashes. Their block protection
information is not discoverable, so they need to have an entry in the
flash tables to describe that.
- Some cleanups for Micron flash support.
- Add support for Micron mt35xu01gbba.
- Some SPI controllers like the Intel one on the PCI bus do not support
the read CR opcode (0x35). Do not use the opcode if the controller
does not support it.
Aside from these main changes, there is the usual load of API updates,
kdoc fixes, potential memory leaks fixes, etc.
-----BEGIN PGP SIGNATURE-----
iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmkq8aMACgkQJWrqGEe9
VoRxNQgAti0loJ+uUcjWwvpGkpC6/g1W1j+qMWdBUiiG8F+la23A8vT/HEAna0sD
H9fl8ZeDZYG4YQR3OWBbOs6b7DHdor6QEd08szM35mgJkDT40qyGka1nnwd1KPjS
4+xRPY0Lyx2V612mB8f6yXhvH5GOpskg5JLS65lzjSbyTb8BMCk6uu87eAVViDuw
Q8dOpN/LgbXp5B1H/23CM6Lvgj+Ok2ETLND7l2jW9yE8SSA04J+7Lth4uNSW4k/J
1ZKcaooPBaj7paPxz/C9f3VyxoLN3+YHMjdvt5rI17bwKfsR8PbdVpvdgIqpv1yn
UG84IfsOHP8JDzUHSyKE+9DNgz4Bew==
=8uoN
-----END PGP SIGNATURE-----
Merge tag 'mtd/for-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux
Pull mtd updates from Miquel Raynal:
"Core MTD changes:
- We must ignore error -ENOENT from parsers on subpartitions which is
a legitimate return value
- PM support is added to the intel-dg driver
Raw NAND changes:
- The major change in this is the support for the Allwinner H616 NAND
controller, which lead to numerous changes and cleanups in the
driver.
- Another notable change in this driver is the use of field_get() and
field_prep(), but since the global support for this helpers is
going to be merged in the same release as we start using these
helpers, we undefine them in the first place to avoid warnings.
- Marvell drivers layout handling changes have also landed, they fix
previous definitions and abuses that have been made previously,
which implied to relax the ECC parameters validation in the core a
bit.
- The Cadence NAND controller driver gets NV-DDR interface support.
SPI NAND changes:
- Support for FudanMicro FM25S01BI3 and ESMT F50L1G41LC is added.
SPI NOR changes:
- Fix SMPT parsing for S25FS-S flash family. They report variable
dummy cycles for reads. This results in the default of 0 being
used. This works for other Infineon chips, but not for the S25FS-S
family. They need 8 dummy cycles. Add fixup hooks to specify that.
Also add fixup hooks to fix incorrect map ID data in SFDP.
- Add support for a bunch of Winbond flashes. Their block protection
information is not discoverable, so they need to have an entry in
the flash tables to describe that.
- Some cleanups for Micron flash support.
- Add support for Micron mt35xu01gbba.
- Some SPI controllers like the Intel one on the PCI bus do not
support the read CR opcode (0x35). Do not use the opcode if the
controller does not support it.
Aside from these main changes, there is the usual load of API updates,
kdoc fixes, potential memory leaks fixes, etc"
* tag 'mtd/for-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (51 commits)
mtd: sm_ftl: Fix typo in comment in sm_read_lba
mtd: sm_ftl: Replace deprecated strncpy with sysfs_emit in sm_attr_show
mtd: lpddr_cmds: fix signed shifts in lpddr_cmds
mtd: docg3: fix kernel-doc warnings
mtd: spinand: add support for FudanMicro FM25S01BI3
mtd: rawnand: renesas: Handle devm_pm_runtime_enable() errors
mtd: nand: realtek-ecc: Fix Kconfig dependencies
mtd: rawnand: sunxi: #undef field_{get,prep}() before local definition
mailmap: update Pratyush Yadav's email address
mtd: spi-nor: core: Check read CR support
mtd: spi-nor: micron-st: add TODO for fixing mt35xu02gcba
mtd: spi-nor: micron-st: add mt35xu01gbba support
mtd: spi-nor: micron-st: use SFDP of mt35xu512aba
mtd: spi-nor: micron-st: move set_octal_dtr to late_init()
mtd: spi-nor: micron-st: rename the die_late_init functions
mtd: spinand: esmt: add support for F50L1G41LC
mtd: rawnand: lpc32xx_slc: Convert to use devm_gpiod_get_optional()
mtd: mtdpart: ignore error -ENOENT from parsers on subpartitions
mtd: maps: pcmciamtd: fix potential memory leak in pcmciamtd_detach()
mtd: spi-nor: spansion: SMPT fixups for S25FS-S
...
This commit is contained in:
commit
edd2b9832d
1
.mailmap
1
.mailmap
|
|
@ -638,6 +638,7 @@ Peter Oruba <peter.oruba@amd.com>
|
|||
Peter Oruba <peter@oruba.de>
|
||||
Pierre-Louis Bossart <pierre-louis.bossart@linux.dev> <pierre-louis.bossart@linux.intel.com>
|
||||
Pratyush Anand <pratyush.anand@gmail.com> <pratyush.anand@st.com>
|
||||
Pratyush Yadav <pratyush@kernel.org> <ptyadav@amazon.de>
|
||||
Praveen BP <praveenbp@ti.com>
|
||||
Pradeep Kumar Chitrapu <quic_pradeepc@quicinc.com> <pradeepc@codeaurora.org>
|
||||
Prasad Sodagudi <quic_psodagud@quicinc.com> <psodagud@codeaurora.org>
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
|
|||
|
||||
title: Allwinner A10 NAND Controller
|
||||
|
||||
allOf:
|
||||
- $ref: nand-controller.yaml
|
||||
|
||||
maintainers:
|
||||
- Chen-Yu Tsai <wens@csie.org>
|
||||
- Maxime Ripard <mripard@kernel.org>
|
||||
|
|
@ -18,6 +15,8 @@ properties:
|
|||
enum:
|
||||
- allwinner,sun4i-a10-nand
|
||||
- allwinner,sun8i-a23-nand-controller
|
||||
- allwinner,sun50i-h616-nand-controller
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
|
|
@ -25,14 +24,20 @@ properties:
|
|||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 2
|
||||
items:
|
||||
- description: Bus Clock
|
||||
- description: Module Clock
|
||||
- description: ECC Clock
|
||||
- description: MBus Clock
|
||||
|
||||
clock-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: ahb
|
||||
- const: mod
|
||||
- const: ecc
|
||||
- const: mbus
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
|
@ -85,6 +90,36 @@ required:
|
|||
|
||||
unevaluatedProperties: false
|
||||
|
||||
allOf:
|
||||
- $ref: nand-controller.yaml
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- allwinner,sun4i-a10-nand
|
||||
- allwinner,sun8i-a23-nand-controller
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 2
|
||||
clock-names:
|
||||
maxItems: 2
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- allwinner,sun50i-h616-nand-controller
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 4
|
||||
clock-names:
|
||||
minItems: 4
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
|
|
|||
|
|
@ -69,6 +69,16 @@ properties:
|
|||
minItems: 1
|
||||
maxItems: 8
|
||||
|
||||
clocks:
|
||||
description: |
|
||||
Chips may need clocks to be enabled for themselves or for transparent
|
||||
bridges.
|
||||
|
||||
power-domains:
|
||||
description: |
|
||||
Chips may need power domains to be enabled for themselves or for
|
||||
transparent bridges.
|
||||
|
||||
bank-width:
|
||||
description: Width (in bytes) of the bank. Equal to the device width times
|
||||
the number of interleaved chips.
|
||||
|
|
|
|||
|
|
@ -274,11 +274,11 @@ struct docg3_cascade {
|
|||
* @cascade: the cascade this device belongs to
|
||||
* @device_id: number of the cascaded DoCG3 device (0, 1, 2 or 3)
|
||||
* @if_cfg: if true, reads are on 16bits, else reads are on 8bits
|
||||
|
||||
* @reliable: if 0, docg3 in normal mode, if 1 docg3 in fast mode, if 2 in
|
||||
* reliable mode
|
||||
* Fast mode implies more errors than normal mode.
|
||||
* Reliable mode implies that page 2*n and 2*n+1 are clones.
|
||||
* @max_block: maximum block number for this device
|
||||
* @bbt: bad block table cache
|
||||
* @oob_write_ofs: offset of the MTD where this OOB should belong (ie. in next
|
||||
* page_write)
|
||||
|
|
|
|||
|
|
@ -15,14 +15,18 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define INTEL_DG_NVM_RPM_TIMEOUT_MS 500
|
||||
|
||||
struct intel_dg_nvm {
|
||||
struct kref refcnt;
|
||||
struct mtd_info mtd;
|
||||
struct device *dev;
|
||||
struct mutex lock; /* region access lock */
|
||||
void __iomem *base;
|
||||
void __iomem *base2;
|
||||
|
|
@ -421,6 +425,8 @@ static int intel_dg_nvm_init(struct intel_dg_nvm *nvm, struct device *device,
|
|||
unsigned int i, n;
|
||||
int ret;
|
||||
|
||||
nvm->dev = device;
|
||||
|
||||
/* clean error register, previous errors are ignored */
|
||||
idg_nvm_error(nvm);
|
||||
|
||||
|
|
@ -498,6 +504,7 @@ static int intel_dg_mtd_erase(struct mtd_info *mtd, struct erase_info *info)
|
|||
size_t len;
|
||||
u8 region;
|
||||
u64 addr;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!nvm))
|
||||
return -EINVAL;
|
||||
|
|
@ -512,20 +519,29 @@ static int intel_dg_mtd_erase(struct mtd_info *mtd, struct erase_info *info)
|
|||
total_len = info->len;
|
||||
addr = info->addr;
|
||||
|
||||
ret = pm_runtime_resume_and_get(nvm->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&mtd->dev, "rpm: get failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
guard(mutex)(&nvm->lock);
|
||||
|
||||
while (total_len > 0) {
|
||||
if (!IS_ALIGNED(addr, SZ_4K) || !IS_ALIGNED(total_len, SZ_4K)) {
|
||||
dev_err(&mtd->dev, "unaligned erase %llx %zx\n", addr, total_len);
|
||||
info->fail_addr = addr;
|
||||
return -ERANGE;
|
||||
ret = -ERANGE;
|
||||
break;
|
||||
}
|
||||
|
||||
idx = idg_nvm_get_region(nvm, addr);
|
||||
if (idx >= nvm->nregions) {
|
||||
dev_err(&mtd->dev, "out of range");
|
||||
info->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
|
||||
return -ERANGE;
|
||||
ret = -ERANGE;
|
||||
break;
|
||||
}
|
||||
|
||||
from = addr - nvm->regions[idx].offset;
|
||||
|
|
@ -541,14 +557,16 @@ static int intel_dg_mtd_erase(struct mtd_info *mtd, struct erase_info *info)
|
|||
if (bytes < 0) {
|
||||
dev_dbg(&mtd->dev, "erase failed with %zd\n", bytes);
|
||||
info->fail_addr += nvm->regions[idx].offset;
|
||||
return bytes;
|
||||
ret = bytes;
|
||||
break;
|
||||
}
|
||||
|
||||
addr += len;
|
||||
total_len -= len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
pm_runtime_put_autosuspend(nvm->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int intel_dg_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
|
|
@ -577,17 +595,24 @@ static int intel_dg_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
if (len > nvm->regions[idx].size - from)
|
||||
len = nvm->regions[idx].size - from;
|
||||
|
||||
ret = pm_runtime_resume_and_get(nvm->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&mtd->dev, "rpm: get failed %zd\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
guard(mutex)(&nvm->lock);
|
||||
|
||||
ret = idg_read(nvm, region, from, len, buf);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&mtd->dev, "read failed with %zd\n", ret);
|
||||
return ret;
|
||||
} else {
|
||||
*retlen = ret;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
*retlen = ret;
|
||||
|
||||
return 0;
|
||||
pm_runtime_put_autosuspend(nvm->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int intel_dg_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
|
|
@ -616,17 +641,24 @@ static int intel_dg_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
if (len > nvm->regions[idx].size - to)
|
||||
len = nvm->regions[idx].size - to;
|
||||
|
||||
ret = pm_runtime_resume_and_get(nvm->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&mtd->dev, "rpm: get failed %zd\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
guard(mutex)(&nvm->lock);
|
||||
|
||||
ret = idg_write(nvm, region, to, len, buf);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&mtd->dev, "write failed with %zd\n", ret);
|
||||
return ret;
|
||||
} else {
|
||||
*retlen = ret;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
*retlen = ret;
|
||||
|
||||
return 0;
|
||||
pm_runtime_put_autosuspend(nvm->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void intel_dg_nvm_release(struct kref *kref)
|
||||
|
|
@ -753,6 +785,21 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,
|
|||
}
|
||||
nvm->nregions = n; /* in case where kasprintf fail */
|
||||
|
||||
ret = devm_pm_runtime_enable(device);
|
||||
if (ret < 0) {
|
||||
dev_err(device, "rpm: enable failed %d\n", ret);
|
||||
goto err_norpm;
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(device, INTEL_DG_NVM_RPM_TIMEOUT_MS);
|
||||
pm_runtime_use_autosuspend(device);
|
||||
|
||||
ret = pm_runtime_resume_and_get(device);
|
||||
if (ret < 0) {
|
||||
dev_err(device, "rpm: get failed %d\n", ret);
|
||||
goto err_norpm;
|
||||
}
|
||||
|
||||
nvm->base = devm_ioremap_resource(device, &invm->bar);
|
||||
if (IS_ERR(nvm->base)) {
|
||||
ret = PTR_ERR(nvm->base);
|
||||
|
|
@ -781,9 +828,12 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,
|
|||
|
||||
dev_set_drvdata(&aux_dev->dev, nvm);
|
||||
|
||||
pm_runtime_put(device);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
pm_runtime_put(device);
|
||||
err_norpm:
|
||||
kref_put(&nvm->refcnt, intel_dg_nvm_release);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ struct mtd_info *lpddr_cmdset(struct map_info *map)
|
|||
mutex_init(&shared[i].lock);
|
||||
for (j = 0; j < lpddr->qinfo->HWPartsNum; j++) {
|
||||
*chip = lpddr->chips[i];
|
||||
chip->start += j << lpddr->chipshift;
|
||||
chip->start += (unsigned long)j << lpddr->chipshift;
|
||||
chip->oldstate = chip->state = FL_READY;
|
||||
chip->priv = &shared[i];
|
||||
/* those should be reset too since
|
||||
|
|
@ -559,7 +559,7 @@ static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len,
|
|||
break;
|
||||
|
||||
if ((len + ofs - 1) >> lpddr->chipshift)
|
||||
thislen = (1<<lpddr->chipshift) - ofs;
|
||||
thislen = (1UL << lpddr->chipshift) - ofs;
|
||||
else
|
||||
thislen = len;
|
||||
/* get the chip */
|
||||
|
|
@ -575,7 +575,7 @@ static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len,
|
|||
len -= thislen;
|
||||
|
||||
ofs = 0;
|
||||
last_end += 1 << lpddr->chipshift;
|
||||
last_end += 1UL << lpddr->chipshift;
|
||||
chipnum++;
|
||||
chip = &lpddr->chips[chipnum];
|
||||
}
|
||||
|
|
@ -601,7 +601,7 @@ static int lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len)
|
|||
break;
|
||||
|
||||
if ((len + ofs - 1) >> lpddr->chipshift)
|
||||
thislen = (1<<lpddr->chipshift) - ofs;
|
||||
thislen = (1UL << lpddr->chipshift) - ofs;
|
||||
else
|
||||
thislen = len;
|
||||
|
||||
|
|
|
|||
|
|
@ -665,6 +665,7 @@ static void pcmciamtd_detach(struct pcmcia_device *link)
|
|||
}
|
||||
|
||||
pcmciamtd_release(link);
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -425,9 +425,12 @@ int add_mtd_partitions(struct mtd_info *parent,
|
|||
|
||||
mtd_add_partition_attrs(child);
|
||||
|
||||
/* Look for subpartitions */
|
||||
/* Look for subpartitions (skip if no maching parser found) */
|
||||
ret = parse_mtd_partitions(child, parts[i].types, NULL);
|
||||
if (ret < 0) {
|
||||
if (ret < 0 && ret == -ENOENT) {
|
||||
pr_debug("Skip parsing subpartitions: %d\n", ret);
|
||||
continue;
|
||||
} else if (ret < 0) {
|
||||
pr_err("Failed to parse subpartitions: %d\n", ret);
|
||||
goto err_del_partitions;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,6 +199,7 @@
|
|||
|
||||
/* Common settings. */
|
||||
#define COMMON_SET 0x1008
|
||||
#define OPR_MODE_NVDDR BIT(0)
|
||||
/* 16 bit device connected to the NAND Flash interface. */
|
||||
#define COMMON_SET_DEVICE_16BIT BIT(8)
|
||||
|
||||
|
|
@ -211,12 +212,20 @@
|
|||
#define SKIP_BYTES_OFFSET_VALUE GENMASK(23, 0)
|
||||
|
||||
/* Timings configuration. */
|
||||
#define TOGGLE_TIMINGS_0 0x1014
|
||||
#define TOGGLE_TIMINGS_1 0x1018
|
||||
|
||||
#define ASYNC_TOGGLE_TIMINGS 0x101c
|
||||
#define ASYNC_TOGGLE_TIMINGS_TRH GENMASK(28, 24)
|
||||
#define ASYNC_TOGGLE_TIMINGS_TRP GENMASK(20, 16)
|
||||
#define ASYNC_TOGGLE_TIMINGS_TWH GENMASK(12, 8)
|
||||
#define ASYNC_TOGGLE_TIMINGS_TWP GENMASK(4, 0)
|
||||
|
||||
#define SYNC_TIMINGS 0x1020
|
||||
#define SYNC_TCKWR GENMASK(21, 16)
|
||||
#define SYNC_TWRCK GENMASK(13, 8)
|
||||
#define SYNC_TCAD GENMASK(5, 0)
|
||||
|
||||
#define TIMINGS0 0x1024
|
||||
#define TIMINGS0_TADL GENMASK(31, 24)
|
||||
#define TIMINGS0_TCCS GENMASK(23, 16)
|
||||
|
|
@ -226,6 +235,7 @@
|
|||
#define TIMINGS1 0x1028
|
||||
#define TIMINGS1_TRHZ GENMASK(31, 24)
|
||||
#define TIMINGS1_TWB GENMASK(23, 16)
|
||||
#define TIMINGS1_TCWAW GENMASK(15, 8)
|
||||
#define TIMINGS1_TVDLY GENMASK(7, 0)
|
||||
|
||||
#define TIMINGS2 0x102c
|
||||
|
|
@ -243,14 +253,23 @@
|
|||
|
||||
/* Register controlling DQ related timing. */
|
||||
#define PHY_DQ_TIMING 0x2000
|
||||
#define PHY_DQ_TIMING_OE_END GENMASK(2, 0)
|
||||
#define PHY_DQ_TIMING_OE_START GENMASK(6, 4)
|
||||
#define PHY_DQ_TIMING_TSEL_END GENMASK(11, 8)
|
||||
#define PHY_DQ_TIMING_TSEL_START GENMASK(15, 12)
|
||||
|
||||
/* Register controlling DSQ related timing. */
|
||||
#define PHY_DQS_TIMING 0x2004
|
||||
#define PHY_DQS_TIMING_DQS_SEL_OE_END GENMASK(3, 0)
|
||||
#define PHY_DQS_TIMING_DQS_SEL_OE_START GENMASK(7, 4)
|
||||
#define PHY_DQS_TIMING_DQS_SEL_TSEL_END GENMASK(11, 8)
|
||||
#define PHY_DQS_TIMING_PHONY_DQS_SEL BIT(16)
|
||||
#define PHY_DQS_TIMING_USE_PHONY_DQS BIT(20)
|
||||
|
||||
/* Register controlling the gate and loopback control related timing. */
|
||||
#define PHY_GATE_LPBK_CTRL 0x2008
|
||||
#define PHY_GATE_LPBK_CTRL_GATE_CFG GENMASK(3, 0)
|
||||
#define PHY_GATE_LPBK_CTRL_GATE_CFG_CLOSE GENMASK(5, 4)
|
||||
#define PHY_GATE_LPBK_CTRL_RDS GENMASK(24, 19)
|
||||
|
||||
/* Register holds the control for the master DLL logic. */
|
||||
|
|
@ -260,6 +279,12 @@
|
|||
/* Register holds the control for the slave DLL logic. */
|
||||
#define PHY_DLL_SLAVE_CTRL 0x2010
|
||||
|
||||
/* Register controls the DQS related timing. */
|
||||
#define PHY_IE_TIMING 0x2014
|
||||
#define PHY_IE_TIMING_DQS_IE_START GENMASK(10, 8)
|
||||
#define PHY_IE_TIMING_DQ_IE_START GENMASK(18, 16)
|
||||
#define PHY_IE_TIMING_IE_ALWAYS_ON BIT(20)
|
||||
|
||||
/* This register handles the global control settings for the PHY. */
|
||||
#define PHY_CTRL 0x2080
|
||||
#define PHY_CTRL_SDR_DQS BIT(14)
|
||||
|
|
@ -375,15 +400,41 @@
|
|||
#define BCH_MAX_NUM_CORR_CAPS 8
|
||||
#define BCH_MAX_NUM_SECTOR_SIZES 2
|
||||
|
||||
/* NVDDR mode specific parameters and register values based on cadence specs */
|
||||
#define NVDDR_PHY_RD_DELAY 29
|
||||
#define NVDDR_PHY_RD_DELAY_MAX 31
|
||||
#define NVDDR_GATE_CFG_OPT 14
|
||||
#define NVDDR_GATE_CFG_STD 7
|
||||
#define NVDDR_GATE_CFG_MAX 15
|
||||
#define NVDDR_DATA_SEL_OE_START 1
|
||||
#define NVDDR_DATA_SEL_OE_START_MAX 7
|
||||
#define NVDDR_DATA_SEL_OE_END 6
|
||||
#define NVDDR_DATA_SEL_OE_END_MIN 4
|
||||
#define NVDDR_DATA_SEL_OE_END_MAX 15
|
||||
#define NVDDR_RS_HIGH_WAIT_CNT 7
|
||||
#define NVDDR_RS_IDLE_CNT 7
|
||||
#define NVDDR_TCWAW_DELAY 250000
|
||||
#define NVDDR_TVDLY_DELAY 500000
|
||||
#define NVDDR_TOGGLE_TIMINGS_0 0x00000301
|
||||
#define NVDDR_TOGGLE_TIMINGS_1 0x0a060102
|
||||
#define NVDDR_ASYNC_TOGGLE_TIMINGS 0
|
||||
#define NVDDR_PHY_CTRL 0x00004000
|
||||
#define NVDDR_PHY_TSEL 0
|
||||
#define NVDDR_PHY_DLL_MASTER_CTRL 0x00140004
|
||||
#define NVDDR_PHY_DLL_SLAVE_CTRL 0x00003c3c
|
||||
|
||||
struct cadence_nand_timings {
|
||||
u32 async_toggle_timings;
|
||||
u32 sync_timings;
|
||||
u32 timings0;
|
||||
u32 timings1;
|
||||
u32 timings2;
|
||||
u32 dll_phy_ctrl;
|
||||
u32 phy_ctrl;
|
||||
u32 phy_dq_timing;
|
||||
u32 phy_dqs_timing;
|
||||
u32 phy_gate_lpbk_ctrl;
|
||||
u32 phy_ie_timing;
|
||||
};
|
||||
|
||||
/* Command DMA descriptor. */
|
||||
|
|
@ -2345,11 +2396,9 @@ static inline u32 calc_tdvw(u32 trp_cnt, u32 clk_period, u32 trhoh_min,
|
|||
return (trp_cnt + 1) * clk_period + trhoh_min - trea_max;
|
||||
}
|
||||
|
||||
static int
|
||||
cadence_nand_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf)
|
||||
static int cadence_nand_setup_sdr_interface(struct nand_chip *chip,
|
||||
const struct nand_sdr_timings *sdr)
|
||||
{
|
||||
const struct nand_sdr_timings *sdr;
|
||||
struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller);
|
||||
struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
|
||||
struct cadence_nand_timings *t = &cdns_chip->timings;
|
||||
|
|
@ -2370,13 +2419,8 @@ cadence_nand_setup_interface(struct nand_chip *chip, int chipnr,
|
|||
u32 dll_phy_dqs_timing = 0, phony_dqs_timing = 0, rd_del_sel = 0;
|
||||
u32 sampling_point;
|
||||
|
||||
sdr = nand_get_sdr_timings(conf);
|
||||
if (IS_ERR(sdr))
|
||||
return PTR_ERR(sdr);
|
||||
|
||||
memset(t, 0, sizeof(*t));
|
||||
/* Sampling point calculation. */
|
||||
|
||||
if (cdns_ctrl->caps2.is_phy_type_dll)
|
||||
phony_dqs_mod = 2;
|
||||
else
|
||||
|
|
@ -2633,10 +2677,221 @@ cadence_nand_setup_interface(struct nand_chip *chip, int chipnr,
|
|||
PHY_DLL_MASTER_CTRL_BYPASS_MODE);
|
||||
dev_dbg(cdns_ctrl->dev, "PHY_DLL_SLAVE_CTRL_REG_SDR\t%x\n", 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cadence_nand_setup_nvddr_interface(struct nand_chip *chip,
|
||||
const struct nand_nvddr_timings *nvddr)
|
||||
{
|
||||
struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller);
|
||||
struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
|
||||
struct cadence_nand_timings *t = &cdns_chip->timings;
|
||||
u32 board_delay = cdns_ctrl->board_delay;
|
||||
u32 clk_period = DIV_ROUND_DOWN_ULL(1000000000000ULL,
|
||||
cdns_ctrl->nf_clk_rate);
|
||||
u32 ddr_clk_ctrl_period = clk_period * 2;
|
||||
u32 if_skew = cdns_ctrl->caps1->if_skew;
|
||||
u32 tceh_cnt, tcs_cnt, tadl_cnt, tccs_cnt;
|
||||
u32 twrck_cnt, tcad_cnt, tckwr_cnt = 0;
|
||||
u32 tfeat_cnt, trhz_cnt, tvdly_cnt, tcwaw_cnt;
|
||||
u32 trhw_cnt, twb_cnt, twhr_cnt;
|
||||
u32 oe_start, oe_end, oe_end_dqsd;
|
||||
u32 rd_del_sel = 0;
|
||||
u32 dqs_driven_by_device, dqs_toogle_by_device, gate_open_delay;
|
||||
u32 dll_phy_gate_open_delay, gate_close_delay, ie_start;
|
||||
u32 dll_phy_rd_delay;
|
||||
u32 reg;
|
||||
|
||||
memset(t, 0, sizeof(*t));
|
||||
twrck_cnt = calc_cycl(nvddr->tWRCK_min, ddr_clk_ctrl_period);
|
||||
tcad_cnt = calc_cycl(nvddr->tCAD_min, ddr_clk_ctrl_period);
|
||||
|
||||
reg = FIELD_PREP(SYNC_TWRCK, twrck_cnt);
|
||||
reg |= FIELD_PREP(SYNC_TCAD, tcad_cnt);
|
||||
t->sync_timings = reg;
|
||||
dev_dbg(cdns_ctrl->dev, "SYNC_TIMINGS_NVDDR\t%08x\n", reg);
|
||||
|
||||
tadl_cnt = calc_cycl((nvddr->tADL_min + if_skew), ddr_clk_ctrl_period);
|
||||
tccs_cnt = calc_cycl((nvddr->tCCS_min + if_skew), ddr_clk_ctrl_period);
|
||||
twhr_cnt = calc_cycl((nvddr->tWHR_min + if_skew), ddr_clk_ctrl_period);
|
||||
trhw_cnt = calc_cycl((nvddr->tRHW_min + if_skew), ddr_clk_ctrl_period);
|
||||
reg = FIELD_PREP(TIMINGS0_TADL, tadl_cnt);
|
||||
reg |= FIELD_PREP(TIMINGS0_TCCS, tccs_cnt);
|
||||
reg |= FIELD_PREP(TIMINGS0_TWHR, twhr_cnt);
|
||||
reg |= FIELD_PREP(TIMINGS0_TRHW, trhw_cnt);
|
||||
t->timings0 = reg;
|
||||
dev_dbg(cdns_ctrl->dev, "TIMINGS0_NVDDR\t%08x\n", reg);
|
||||
|
||||
twb_cnt = calc_cycl((nvddr->tWB_max + board_delay),
|
||||
ddr_clk_ctrl_period);
|
||||
/*
|
||||
* Because of the two stage syncflop the value must be increased by 3
|
||||
* first value is related with sync, second value is related
|
||||
* with output if delay.
|
||||
*/
|
||||
twb_cnt = twb_cnt + 3 + 5;
|
||||
tvdly_cnt = calc_cycl(NVDDR_TVDLY_DELAY + if_skew, ddr_clk_ctrl_period);
|
||||
tcwaw_cnt = calc_cycl(NVDDR_TCWAW_DELAY, ddr_clk_ctrl_period);
|
||||
trhz_cnt = 1;
|
||||
reg = FIELD_PREP(TIMINGS1_TWB, twb_cnt);
|
||||
reg |= FIELD_PREP(TIMINGS1_TVDLY, tvdly_cnt);
|
||||
reg |= FIELD_PREP(TIMINGS1_TRHZ, trhz_cnt);
|
||||
reg |= FIELD_PREP(TIMINGS1_TCWAW, tcwaw_cnt);
|
||||
t->timings1 = reg;
|
||||
dev_dbg(cdns_ctrl->dev, "TIMINGS1_NVDDR\t%08x\n", reg);
|
||||
|
||||
tfeat_cnt = calc_cycl(nvddr->tFEAT_max, ddr_clk_ctrl_period);
|
||||
if (tfeat_cnt < twb_cnt)
|
||||
tfeat_cnt = twb_cnt;
|
||||
|
||||
tceh_cnt = calc_cycl(nvddr->tCEH_min, ddr_clk_ctrl_period);
|
||||
tcs_cnt = calc_cycl((nvddr->tCS_min + if_skew), ddr_clk_ctrl_period);
|
||||
reg = FIELD_PREP(TIMINGS2_TFEAT, tfeat_cnt);
|
||||
reg |= FIELD_PREP(TIMINGS2_CS_HOLD_TIME, tceh_cnt);
|
||||
reg |= FIELD_PREP(TIMINGS2_CS_SETUP_TIME, tcs_cnt);
|
||||
t->timings2 = reg;
|
||||
dev_dbg(cdns_ctrl->dev, "TIMINGS2_NVDDR\t%08x\n", reg);
|
||||
|
||||
reg = FIELD_PREP(DLL_PHY_CTRL_RS_HIGH_WAIT_CNT, NVDDR_RS_HIGH_WAIT_CNT);
|
||||
reg |= FIELD_PREP(DLL_PHY_CTRL_RS_IDLE_CNT, NVDDR_RS_IDLE_CNT);
|
||||
t->dll_phy_ctrl = reg;
|
||||
dev_dbg(cdns_ctrl->dev, "DLL_PHY_CTRL_NVDDR\t%08x\n", reg);
|
||||
|
||||
reg = PHY_CTRL_SDR_DQS;
|
||||
t->phy_ctrl = reg;
|
||||
dev_dbg(cdns_ctrl->dev, "PHY_CTRL_REG_NVDDR\t%08x\n", reg);
|
||||
|
||||
dqs_driven_by_device = (nvddr->tDQSD_max + board_delay) / 1000 +
|
||||
if_skew;
|
||||
dqs_toogle_by_device = (nvddr->tDQSCK_max + board_delay) / 1000 -
|
||||
if_skew;
|
||||
gate_open_delay = dqs_toogle_by_device / (clk_period / 1000);
|
||||
if (dqs_toogle_by_device > clk_period / 1000) {
|
||||
if (gate_open_delay > NVDDR_GATE_CFG_OPT)
|
||||
dll_phy_gate_open_delay = NVDDR_GATE_CFG_MAX;
|
||||
else
|
||||
dll_phy_gate_open_delay = gate_open_delay + 1;
|
||||
gate_close_delay = 0;
|
||||
} else {
|
||||
twrck_cnt = calc_cycl(dqs_driven_by_device * 1000, clk_period);
|
||||
dll_phy_gate_open_delay = 1;
|
||||
gate_close_delay = 0;
|
||||
|
||||
reg = FIELD_PREP(SYNC_TCKWR, tckwr_cnt);
|
||||
reg |= FIELD_PREP(SYNC_TWRCK, twrck_cnt);
|
||||
reg |= FIELD_PREP(SYNC_TCAD, tcad_cnt);
|
||||
t->sync_timings = reg;
|
||||
dev_dbg(cdns_ctrl->dev, "SYNC_TIMINGS_NVDDR\t%08x\n", reg);
|
||||
}
|
||||
|
||||
if (dll_phy_gate_open_delay > NVDDR_GATE_CFG_STD)
|
||||
ie_start = NVDDR_GATE_CFG_STD;
|
||||
else
|
||||
ie_start = dll_phy_gate_open_delay;
|
||||
|
||||
dll_phy_rd_delay = ((nvddr->tDQSCK_max + board_delay) +
|
||||
(clk_period / 2)) / clk_period;
|
||||
if (dll_phy_rd_delay <= NVDDR_PHY_RD_DELAY)
|
||||
rd_del_sel = dll_phy_rd_delay + 2;
|
||||
else
|
||||
rd_del_sel = NVDDR_PHY_RD_DELAY_MAX;
|
||||
|
||||
reg = FIELD_PREP(PHY_GATE_LPBK_CTRL_GATE_CFG, dll_phy_gate_open_delay);
|
||||
reg |= FIELD_PREP(PHY_GATE_LPBK_CTRL_GATE_CFG_CLOSE, gate_close_delay);
|
||||
reg |= FIELD_PREP(PHY_GATE_LPBK_CTRL_RDS, rd_del_sel);
|
||||
t->phy_gate_lpbk_ctrl = reg;
|
||||
dev_dbg(cdns_ctrl->dev, "PHY_GATE_LPBK_CTRL_REG_NVDDR\t%08x\n", reg);
|
||||
|
||||
oe_end_dqsd = ((nvddr->tDQSD_max / 1000) / ((clk_period / 2) / 1000))
|
||||
+ NVDDR_DATA_SEL_OE_END_MIN;
|
||||
oe_end = (NVDDR_DATA_SEL_OE_END_MIN + oe_end_dqsd) / 2;
|
||||
if (oe_end > NVDDR_DATA_SEL_OE_END_MAX)
|
||||
oe_end = NVDDR_DATA_SEL_OE_END_MAX;
|
||||
|
||||
oe_start = ((nvddr->tDQSHZ_max / 1000) / ((clk_period / 2) / 1000)) + 1;
|
||||
if (oe_start > NVDDR_DATA_SEL_OE_START_MAX)
|
||||
oe_start = NVDDR_DATA_SEL_OE_START_MAX;
|
||||
|
||||
reg = FIELD_PREP(PHY_DQ_TIMING_OE_END, NVDDR_DATA_SEL_OE_END);
|
||||
reg |= FIELD_PREP(PHY_DQ_TIMING_OE_START, NVDDR_DATA_SEL_OE_START);
|
||||
reg |= FIELD_PREP(PHY_DQ_TIMING_TSEL_END, NVDDR_DATA_SEL_OE_END);
|
||||
reg |= FIELD_PREP(PHY_DQ_TIMING_TSEL_START, NVDDR_DATA_SEL_OE_START);
|
||||
t->phy_dq_timing = reg;
|
||||
dev_dbg(cdns_ctrl->dev, "PHY_DQ_TIMING_REG_NVDDR\t%08x\n", reg);
|
||||
|
||||
reg = FIELD_PREP(PHY_DQS_TIMING_DQS_SEL_OE_END, oe_end);
|
||||
reg |= FIELD_PREP(PHY_DQS_TIMING_DQS_SEL_OE_START, oe_start);
|
||||
reg |= FIELD_PREP(PHY_DQS_TIMING_DQS_SEL_TSEL_END, oe_end);
|
||||
t->phy_dqs_timing = reg;
|
||||
dev_dbg(cdns_ctrl->dev, "PHY_DQS_TIMING_REG_NVDDR\t%08x\n", reg);
|
||||
|
||||
reg = FIELD_PREP(PHY_IE_TIMING_DQS_IE_START, ie_start);
|
||||
reg |= FIELD_PREP(PHY_IE_TIMING_DQ_IE_START, ie_start);
|
||||
reg |= FIELD_PREP(PHY_IE_TIMING_IE_ALWAYS_ON, 0);
|
||||
t->phy_ie_timing = reg;
|
||||
dev_dbg(cdns_ctrl->dev, "PHY_IE_TIMING_REG_NVDDR\t%08x\n", reg);
|
||||
|
||||
reg = readl_relaxed(cdns_ctrl->reg + DLL_PHY_CTRL);
|
||||
reg &= ~(DLL_PHY_CTRL_DLL_RST_N |
|
||||
DLL_PHY_CTRL_EXTENDED_RD_MODE |
|
||||
DLL_PHY_CTRL_EXTENDED_WR_MODE);
|
||||
writel_relaxed(reg, cdns_ctrl->reg + DLL_PHY_CTRL);
|
||||
writel_relaxed(OPR_MODE_NVDDR, cdns_ctrl->reg + COMMON_SET);
|
||||
writel_relaxed(NVDDR_TOGGLE_TIMINGS_0,
|
||||
cdns_ctrl->reg + TOGGLE_TIMINGS_0);
|
||||
writel_relaxed(NVDDR_TOGGLE_TIMINGS_1,
|
||||
cdns_ctrl->reg + TOGGLE_TIMINGS_1);
|
||||
writel_relaxed(NVDDR_ASYNC_TOGGLE_TIMINGS,
|
||||
cdns_ctrl->reg + ASYNC_TOGGLE_TIMINGS);
|
||||
writel_relaxed(t->sync_timings, cdns_ctrl->reg + SYNC_TIMINGS);
|
||||
writel_relaxed(t->timings0, cdns_ctrl->reg + TIMINGS0);
|
||||
writel_relaxed(t->timings1, cdns_ctrl->reg + TIMINGS1);
|
||||
writel_relaxed(t->timings2, cdns_ctrl->reg + TIMINGS2);
|
||||
writel_relaxed(t->dll_phy_ctrl, cdns_ctrl->reg + DLL_PHY_CTRL);
|
||||
writel_relaxed(t->phy_ctrl, cdns_ctrl->reg + PHY_CTRL);
|
||||
writel_relaxed(NVDDR_PHY_TSEL, cdns_ctrl->reg + PHY_TSEL);
|
||||
writel_relaxed(t->phy_dq_timing, cdns_ctrl->reg + PHY_DQ_TIMING);
|
||||
writel_relaxed(t->phy_dqs_timing, cdns_ctrl->reg + PHY_DQS_TIMING);
|
||||
writel_relaxed(t->phy_gate_lpbk_ctrl,
|
||||
cdns_ctrl->reg + PHY_GATE_LPBK_CTRL);
|
||||
writel_relaxed(NVDDR_PHY_DLL_MASTER_CTRL,
|
||||
cdns_ctrl->reg + PHY_DLL_MASTER_CTRL);
|
||||
writel_relaxed(NVDDR_PHY_DLL_SLAVE_CTRL,
|
||||
cdns_ctrl->reg + PHY_DLL_SLAVE_CTRL);
|
||||
writel_relaxed(t->phy_ie_timing, cdns_ctrl->reg + PHY_IE_TIMING);
|
||||
writel_relaxed((reg | DLL_PHY_CTRL_DLL_RST_N),
|
||||
cdns_ctrl->reg + DLL_PHY_CTRL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cadence_nand_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_interface_config *conf)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (chipnr < 0)
|
||||
return ret;
|
||||
|
||||
if (nand_interface_is_sdr(conf)) {
|
||||
const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf);
|
||||
|
||||
if (IS_ERR(sdr))
|
||||
return PTR_ERR(sdr);
|
||||
|
||||
ret = cadence_nand_setup_sdr_interface(chip, sdr);
|
||||
} else {
|
||||
const struct nand_nvddr_timings *nvddr = nand_get_nvddr_timings(conf);
|
||||
|
||||
if (IS_ERR(nvddr))
|
||||
return PTR_ERR(nvddr);
|
||||
|
||||
ret = cadence_nand_setup_nvddr_interface(chip, nvddr);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cadence_nand_attach_chip(struct nand_chip *chip)
|
||||
{
|
||||
struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller);
|
||||
|
|
|
|||
|
|
@ -191,7 +191,6 @@ static int gpmi_init(struct gpmi_nand_data *this)
|
|||
r->gpmi_regs + HW_GPMI_CTRL1_SET);
|
||||
|
||||
err_out:
|
||||
pm_runtime_mark_last_busy(this->dev);
|
||||
pm_runtime_put_autosuspend(this->dev);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -761,7 +760,6 @@ static int bch_set_geometry(struct gpmi_nand_data *this)
|
|||
|
||||
ret = 0;
|
||||
err_out:
|
||||
pm_runtime_mark_last_busy(this->dev);
|
||||
pm_runtime_put_autosuspend(this->dev);
|
||||
|
||||
return ret;
|
||||
|
|
@ -2667,7 +2665,6 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip,
|
|||
this->bch = false;
|
||||
|
||||
out_pm:
|
||||
pm_runtime_mark_last_busy(this->dev);
|
||||
pm_runtime_put_autosuspend(this->dev);
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -854,7 +854,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Start with WP disabled, if available */
|
||||
host->wp_gpio = gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW);
|
||||
host->wp_gpio = devm_gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW);
|
||||
res = PTR_ERR_OR_ZERO(host->wp_gpio);
|
||||
if (res) {
|
||||
if (res != -EPROBE_DEFER)
|
||||
|
|
|
|||
|
|
@ -290,13 +290,16 @@ static const struct marvell_hw_ecc_layout marvell_nfc_layouts[] = {
|
|||
MARVELL_LAYOUT( 2048, 512, 4, 1, 1, 2048, 32, 30, 0, 0, 0),
|
||||
MARVELL_LAYOUT( 2048, 512, 8, 2, 1, 1024, 0, 30,1024,32, 30),
|
||||
MARVELL_LAYOUT( 2048, 512, 8, 2, 1, 1024, 0, 30,1024,64, 30),
|
||||
MARVELL_LAYOUT( 2048, 512, 16, 4, 4, 512, 0, 30, 0, 32, 30),
|
||||
MARVELL_LAYOUT( 2048, 512, 12, 3, 2, 704, 0, 30,640, 0, 30),
|
||||
MARVELL_LAYOUT( 2048, 512, 16, 5, 4, 512, 0, 30, 0, 32, 30),
|
||||
MARVELL_LAYOUT( 4096, 512, 4, 2, 2, 2048, 32, 30, 0, 0, 0),
|
||||
MARVELL_LAYOUT( 4096, 512, 8, 4, 4, 1024, 0, 30, 0, 64, 30),
|
||||
MARVELL_LAYOUT( 4096, 512, 16, 8, 8, 512, 0, 30, 0, 32, 30),
|
||||
MARVELL_LAYOUT( 4096, 512, 8, 5, 4, 1024, 0, 30, 0, 64, 30),
|
||||
MARVELL_LAYOUT( 4096, 512, 12, 6, 5, 704, 0, 30,576, 32, 30),
|
||||
MARVELL_LAYOUT( 4096, 512, 16, 9, 8, 512, 0, 30, 0, 32, 30),
|
||||
MARVELL_LAYOUT( 8192, 512, 4, 4, 4, 2048, 0, 30, 0, 0, 0),
|
||||
MARVELL_LAYOUT( 8192, 512, 8, 8, 8, 1024, 0, 30, 0, 160, 30),
|
||||
MARVELL_LAYOUT( 8192, 512, 16, 16, 16, 512, 0, 30, 0, 32, 30),
|
||||
MARVELL_LAYOUT( 8192, 512, 8, 9, 8, 1024, 0, 30, 0, 160, 30),
|
||||
MARVELL_LAYOUT( 8192, 512, 12, 12, 11, 704, 0, 30,448, 64, 30),
|
||||
MARVELL_LAYOUT( 8192, 512, 16, 17, 16, 512, 0, 30, 0, 32, 30),
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6338,11 +6338,14 @@ static int nand_scan_tail(struct nand_chip *chip)
|
|||
ecc->steps = mtd->writesize / ecc->size;
|
||||
if (!base->ecc.ctx.nsteps)
|
||||
base->ecc.ctx.nsteps = ecc->steps;
|
||||
if (ecc->steps * ecc->size != mtd->writesize) {
|
||||
WARN(1, "Invalid ECC parameters\n");
|
||||
ret = -EINVAL;
|
||||
goto err_nand_manuf_cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Validity check: Warn if ECC parameters are not compatible with page size.
|
||||
* Due to the custom handling of ECC blocks in certain controllers the check
|
||||
* may result in an expected failure.
|
||||
*/
|
||||
if (ecc->steps * ecc->size != mtd->writesize)
|
||||
pr_warn("ECC parameters may be invalid in reference to underlying NAND chip\n");
|
||||
|
||||
if (!ecc->total) {
|
||||
ecc->total = ecc->steps * ecc->bytes;
|
||||
|
|
|
|||
|
|
@ -1336,7 +1336,10 @@ static int rnandc_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(rnandc->regs))
|
||||
return PTR_ERR(rnandc->regs);
|
||||
|
||||
devm_pm_runtime_enable(&pdev->dev);
|
||||
ret = devm_pm_runtime_enable(&pdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,12 @@
|
|||
#include <linux/iopoll.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
/* non compile-time field get/prep */
|
||||
#undef field_get
|
||||
#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
|
||||
#undef field_prep
|
||||
#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
|
||||
|
||||
#define NFC_REG_CTL 0x0000
|
||||
#define NFC_REG_ST 0x0004
|
||||
#define NFC_REG_INT 0x0008
|
||||
|
|
@ -45,13 +51,40 @@
|
|||
#define NFC_REG_A23_IO_DATA 0x0300
|
||||
#define NFC_REG_ECC_CTL 0x0034
|
||||
#define NFC_REG_ECC_ST 0x0038
|
||||
#define NFC_REG_DEBUG 0x003C
|
||||
#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3)
|
||||
#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4))
|
||||
#define NFC_REG_SPARE_AREA 0x00A0
|
||||
#define NFC_REG_PAT_ID 0x00A4
|
||||
#define NFC_REG_H6_PAT_FOUND 0x003C
|
||||
#define NFC_REG_A10_ECC_ERR_CNT 0x0040
|
||||
#define NFC_REG_H6_ECC_ERR_CNT 0x0050
|
||||
#define NFC_REG_ECC_ERR_CNT(nfc, x) ((nfc->caps->reg_ecc_err_cnt + (x)) & ~0x3)
|
||||
#define NFC_REG_H6_RDATA_CTL 0x0044
|
||||
#define NFC_REG_H6_RDATA_0 0x0048
|
||||
#define NFC_REG_H6_RDATA_1 0x004C
|
||||
#define NFC_REG_A10_USER_DATA 0x0050
|
||||
#define NFC_REG_H6_USER_DATA 0x0080
|
||||
#define NFC_REG_USER_DATA(nfc, x) (nfc->caps->reg_user_data + ((x) * 4))
|
||||
#define NFC_REG_H6_USER_DATA_LEN 0x0070
|
||||
/* A USER_DATA_LEN register can hold the length of 8 USER_DATA registers */
|
||||
#define NFC_REG_USER_DATA_LEN_CAPACITY 8
|
||||
#define NFC_REG_USER_DATA_LEN(nfc, step) \
|
||||
(nfc->caps->reg_user_data_len + \
|
||||
((step) / NFC_REG_USER_DATA_LEN_CAPACITY) * 4)
|
||||
#define NFC_REG_SPARE_AREA(nfc) (nfc->caps->reg_spare_area)
|
||||
#define NFC_REG_A10_SPARE_AREA 0x00A0
|
||||
#define NFC_REG_PAT_ID(nfc) (nfc->caps->reg_pat_id)
|
||||
#define NFC_REG_A10_PAT_ID 0x00A4
|
||||
#define NFC_REG_MDMA_ADDR 0x00C0
|
||||
#define NFC_REG_MDMA_CNT 0x00C4
|
||||
#define NFC_REG_H6_EFNAND_STATUS 0x0110
|
||||
#define NFC_REG_H6_SPARE_AREA 0x0114
|
||||
#define NFC_REG_H6_PAT_ID 0x0118
|
||||
#define NFC_REG_H6_DDR2_SPEC_CTL 0x011C
|
||||
#define NFC_REG_H6_NDMA_MODE_CTL 0x0120
|
||||
#define NFC_REG_H6_MDMA_DLBA_REG 0x0200
|
||||
#define NFC_REG_H6_MDMA_STA 0x0204
|
||||
#define NFC_REG_H6_MDMA_INT_MAS 0x0208
|
||||
#define NFC_REG_H6_MDMA_DESC_ADDR 0x020C
|
||||
#define NFC_REG_H6_MDMA_BUF_ADDR 0x0210
|
||||
#define NFC_REG_H6_MDMA_CNT 0x0214
|
||||
|
||||
#define NFC_RAM0_BASE 0x0400
|
||||
#define NFC_RAM1_BASE 0x0800
|
||||
|
||||
|
|
@ -63,6 +96,7 @@
|
|||
#define NFC_BUS_WIDTH_16 (1 << 2)
|
||||
#define NFC_RB_SEL_MSK BIT(3)
|
||||
#define NFC_RB_SEL(x) ((x) << 3)
|
||||
/* CE_SEL BIT 27 is meant to be used for GPIO chipselect */
|
||||
#define NFC_CE_SEL_MSK GENMASK(26, 24)
|
||||
#define NFC_CE_SEL(x) ((x) << 24)
|
||||
#define NFC_CE_CTL BIT(6)
|
||||
|
|
@ -81,6 +115,9 @@
|
|||
#define NFC_STA BIT(4)
|
||||
#define NFC_NATCH_INT_FLAG BIT(5)
|
||||
#define NFC_RB_STATE(x) BIT(x + 8)
|
||||
#define NFC_RB_STATE_MSK GENMASK(11, 8)
|
||||
#define NDFC_RDATA_STA_1 BIT(12)
|
||||
#define NDFC_RDATA_STA_0 BIT(13)
|
||||
|
||||
/* define bit use in NFC_INT */
|
||||
#define NFC_B2R_INT_ENABLE BIT(0)
|
||||
|
|
@ -92,6 +129,7 @@
|
|||
|
||||
/* define bit use in NFC_TIMING_CTL */
|
||||
#define NFC_TIMING_CTL_EDO BIT(8)
|
||||
#define NFC_TIMING_CTL_E_EDO BIT(9)
|
||||
|
||||
/* define NFC_TIMING_CFG register layout */
|
||||
#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \
|
||||
|
|
@ -99,9 +137,15 @@
|
|||
(((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \
|
||||
(((tCAD) & 0x7) << 8))
|
||||
|
||||
#define NFC_TIMING_CFG2(tCDQSS, tSC, tCLHZ, tCSS, tWC) \
|
||||
((((tCDQSS) & 0x1) << 11) | (((tSC) & 0x3) << 12) | \
|
||||
(((tCLHZ) & 0x3) << 14) | (((tCSS) & 0x3) << 16) | \
|
||||
(((tWC) & 0x3) << 18))
|
||||
|
||||
/* define bit use in NFC_CMD */
|
||||
#define NFC_CMD_LOW_BYTE_MSK GENMASK(7, 0)
|
||||
#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8)
|
||||
#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8) /* 15-10 reserved on H6 */
|
||||
#define NFC_CMD_ADR_NUM_MSK GENMASK(9, 8)
|
||||
#define NFC_CMD(x) (x)
|
||||
#define NFC_ADR_NUM_MSK GENMASK(18, 16)
|
||||
#define NFC_ADR_NUM(x) (((x) - 1) << 16)
|
||||
|
|
@ -114,6 +158,7 @@
|
|||
#define NFC_SEQ BIT(25)
|
||||
#define NFC_DATA_SWAP_METHOD BIT(26)
|
||||
#define NFC_ROW_AUTO_INC BIT(27)
|
||||
#define NFC_H6_SEND_RND_CMD2 BIT(27)
|
||||
#define NFC_SEND_CMD3 BIT(28)
|
||||
#define NFC_SEND_CMD4 BIT(29)
|
||||
#define NFC_CMD_TYPE_MSK GENMASK(31, 30)
|
||||
|
|
@ -125,6 +170,7 @@
|
|||
#define NFC_READ_CMD_MSK GENMASK(7, 0)
|
||||
#define NFC_RND_READ_CMD0_MSK GENMASK(15, 8)
|
||||
#define NFC_RND_READ_CMD1_MSK GENMASK(23, 16)
|
||||
#define NFC_RND_READ_CMD2_MSK GENMASK(31, 24)
|
||||
|
||||
/* define bit use in NFC_WCMD_SET */
|
||||
#define NFC_PROGRAM_CMD_MSK GENMASK(7, 0)
|
||||
|
|
@ -138,25 +184,46 @@
|
|||
#define NFC_ECC_EXCEPTION BIT(4)
|
||||
#define NFC_ECC_BLOCK_SIZE_MSK BIT(5)
|
||||
#define NFC_ECC_BLOCK_512 BIT(5)
|
||||
#define NFC_RANDOM_EN BIT(9)
|
||||
#define NFC_RANDOM_DIRECTION BIT(10)
|
||||
#define NFC_ECC_MODE_MSK GENMASK(15, 12)
|
||||
#define NFC_ECC_MODE(x) ((x) << 12)
|
||||
#define NFC_RANDOM_EN(nfc) (nfc->caps->random_en_mask)
|
||||
#define NFC_RANDOM_DIRECTION(nfc) (nfc->caps->random_dir_mask)
|
||||
#define NFC_ECC_MODE_MSK(nfc) (nfc->caps->ecc_mode_mask)
|
||||
#define NFC_ECC_MODE(nfc, x) field_prep(NFC_ECC_MODE_MSK(nfc), (x))
|
||||
/* RANDOM_PAGE_SIZE: 0: ECC block size 1: page size */
|
||||
#define NFC_A23_RANDOM_PAGE_SIZE BIT(11)
|
||||
#define NFC_H6_RANDOM_PAGE_SIZE BIT(7)
|
||||
#define NFC_RANDOM_SEED_MSK GENMASK(30, 16)
|
||||
#define NFC_RANDOM_SEED(x) ((x) << 16)
|
||||
|
||||
/* define bit use in NFC_ECC_ST */
|
||||
#define NFC_ECC_ERR(x) BIT(x)
|
||||
#define NFC_ECC_ERR_MSK GENMASK(15, 0)
|
||||
#define NFC_ECC_PAT_FOUND(x) BIT(x + 16)
|
||||
#define NFC_ECC_ERR_MSK(nfc) (nfc->caps->ecc_err_mask)
|
||||
|
||||
/*
|
||||
* define bit use in NFC_REG_PAT_FOUND
|
||||
* For A10/A23, NFC_REG_PAT_FOUND == NFC_ECC_ST register
|
||||
*/
|
||||
#define NFC_ECC_PAT_FOUND_MSK(nfc) (nfc->caps->pat_found_mask)
|
||||
|
||||
#define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff)
|
||||
|
||||
#define NFC_USER_DATA_LEN_MSK(step) \
|
||||
(0xf << (((step) % NFC_REG_USER_DATA_LEN_CAPACITY) * 4))
|
||||
|
||||
#define NFC_DEFAULT_TIMEOUT_MS 1000
|
||||
|
||||
#define NFC_SRAM_SIZE 1024
|
||||
|
||||
#define NFC_MAX_CS 7
|
||||
|
||||
/*
|
||||
* On A10/A23, this is the size of the NDFC User Data Register, containing the
|
||||
* mandatory user data bytes following the ECC for each ECC step.
|
||||
* Thus, for each ECC step, we need the ECC bytes + USER_DATA_SZ.
|
||||
* Those bits are currently unsused, and kept as default value 0xffffffff.
|
||||
*
|
||||
* On H6/H616, this size became configurable, from 0 bytes to 32, via the
|
||||
* USER_DATA_LEN registers.
|
||||
*/
|
||||
#define USER_DATA_SZ 4
|
||||
|
||||
/**
|
||||
* struct sunxi_nand_chip_sel - stores information related to NAND Chip Select
|
||||
*
|
||||
|
|
@ -211,13 +278,57 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
|
|||
*
|
||||
* @has_mdma: Use mbus dma mode, otherwise general dma
|
||||
* through MBUS on A23/A33 needs extra configuration.
|
||||
* @has_ecc_block_512: If the ECC can handle 512B or only 1024B chuncks
|
||||
* @has_ecc_clk: If the controller needs an ECC clock.
|
||||
* @has_mbus_clk: If the controller needs a mbus clock.
|
||||
* @reg_io_data: I/O data register
|
||||
* @reg_ecc_err_cnt: ECC error counter register
|
||||
* @reg_user_data: User data register
|
||||
* @reg_user_data_len: User data length register
|
||||
* @reg_spare_area: Spare Area Register
|
||||
* @reg_pat_id: Pattern ID Register
|
||||
* @reg_pat_found: Data Pattern Status Register
|
||||
* @random_en_mask: RANDOM_EN mask in NFC_ECC_CTL register
|
||||
* @random_dir_mask: RANDOM_DIRECTION mask in NFC_ECC_CTL register
|
||||
* @ecc_mode_mask: ECC_MODE mask in NFC_ECC_CTL register
|
||||
* @ecc_err_mask: NFC_ECC_ERR mask in NFC_ECC_ST register
|
||||
* @pat_found_mask: ECC_PAT_FOUND mask in NFC_REG_PAT_FOUND register
|
||||
* @dma_maxburst: DMA maxburst
|
||||
* @ecc_strengths: Available ECC strengths array
|
||||
* @nstrengths: Size of @ecc_strengths
|
||||
* @max_ecc_steps: Maximum supported steps for ECC, this is also the
|
||||
* number of user data registers
|
||||
* @user_data_len_tab: Table of lenghts supported by USER_DATA_LEN register
|
||||
* The table index is the value to set in NFC_USER_DATA_LEN
|
||||
* registers, and the corresponding value is the number of
|
||||
* bytes to write
|
||||
* @nuser_data_tab: Size of @user_data_len_tab
|
||||
* @sram_size: Size of the NAND controller SRAM
|
||||
*/
|
||||
struct sunxi_nfc_caps {
|
||||
bool has_mdma;
|
||||
bool has_ecc_block_512;
|
||||
bool has_ecc_clk;
|
||||
bool has_mbus_clk;
|
||||
unsigned int reg_io_data;
|
||||
unsigned int reg_ecc_err_cnt;
|
||||
unsigned int reg_user_data;
|
||||
unsigned int reg_user_data_len;
|
||||
unsigned int reg_spare_area;
|
||||
unsigned int reg_pat_id;
|
||||
unsigned int reg_pat_found;
|
||||
unsigned int random_en_mask;
|
||||
unsigned int random_dir_mask;
|
||||
unsigned int ecc_mode_mask;
|
||||
unsigned int ecc_err_mask;
|
||||
unsigned int pat_found_mask;
|
||||
unsigned int dma_maxburst;
|
||||
const u8 *ecc_strengths;
|
||||
unsigned int nstrengths;
|
||||
const u8 *user_data_len_tab;
|
||||
unsigned int nuser_data_tab;
|
||||
unsigned int max_ecc_steps;
|
||||
int sram_size;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -228,6 +339,8 @@ struct sunxi_nfc_caps {
|
|||
* @regs: NAND controller registers
|
||||
* @ahb_clk: NAND controller AHB clock
|
||||
* @mod_clk: NAND controller mod clock
|
||||
* @ecc_clk: NAND controller ECC clock
|
||||
* @mbus_clk: NAND controller MBUS clock
|
||||
* @reset: NAND controller reset line
|
||||
* @assigned_cs: bitmask describing already assigned CS lines
|
||||
* @clk_rate: NAND controller current clock rate
|
||||
|
|
@ -243,6 +356,8 @@ struct sunxi_nfc {
|
|||
void __iomem *regs;
|
||||
struct clk *ahb_clk;
|
||||
struct clk *mod_clk;
|
||||
struct clk *ecc_clk;
|
||||
struct clk *mbus_clk;
|
||||
struct reset_control *reset;
|
||||
unsigned long assigned_cs;
|
||||
unsigned long clk_rate;
|
||||
|
|
@ -431,7 +546,7 @@ static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs)
|
|||
if (sel->rb >= 0)
|
||||
ctl |= NFC_RB_SEL(sel->rb);
|
||||
|
||||
writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
|
||||
writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA(nfc));
|
||||
|
||||
if (nfc->clk_rate != sunxi_nand->clk_rate) {
|
||||
clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate);
|
||||
|
|
@ -455,7 +570,7 @@ static void sunxi_nfc_read_buf(struct nand_chip *nand, uint8_t *buf, int len)
|
|||
while (len > offs) {
|
||||
bool poll = false;
|
||||
|
||||
cnt = min(len - offs, NFC_SRAM_SIZE);
|
||||
cnt = min(len - offs, nfc->caps->sram_size);
|
||||
|
||||
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
|
||||
if (ret)
|
||||
|
|
@ -493,7 +608,7 @@ static void sunxi_nfc_write_buf(struct nand_chip *nand, const uint8_t *buf,
|
|||
while (len > offs) {
|
||||
bool poll = false;
|
||||
|
||||
cnt = min(len - offs, NFC_SRAM_SIZE);
|
||||
cnt = min(len - offs, nfc->caps->sram_size);
|
||||
|
||||
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
|
||||
if (ret)
|
||||
|
|
@ -623,13 +738,12 @@ static void sunxi_nfc_randomizer_config(struct nand_chip *nand, int page,
|
|||
bool ecc)
|
||||
{
|
||||
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||
u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
|
||||
u32 ecc_ctl;
|
||||
u16 state;
|
||||
|
||||
if (!(nand->options & NAND_NEED_SCRAMBLING))
|
||||
return;
|
||||
|
||||
ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
|
||||
state = sunxi_nfc_randomizer_state(nand, page, ecc);
|
||||
ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK;
|
||||
writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL);
|
||||
|
|
@ -642,7 +756,7 @@ static void sunxi_nfc_randomizer_enable(struct nand_chip *nand)
|
|||
if (!(nand->options & NAND_NEED_SCRAMBLING))
|
||||
return;
|
||||
|
||||
writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN,
|
||||
writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN(nfc),
|
||||
nfc->regs + NFC_REG_ECC_CTL);
|
||||
}
|
||||
|
||||
|
|
@ -653,7 +767,7 @@ static void sunxi_nfc_randomizer_disable(struct nand_chip *nand)
|
|||
if (!(nand->options & NAND_NEED_SCRAMBLING))
|
||||
return;
|
||||
|
||||
writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
|
||||
writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN(nfc),
|
||||
nfc->regs + NFC_REG_ECC_CTL);
|
||||
}
|
||||
|
||||
|
|
@ -717,20 +831,66 @@ static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob,
|
|||
{
|
||||
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||
|
||||
sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)),
|
||||
oob);
|
||||
sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(nfc, step)), oob);
|
||||
|
||||
/* De-randomize the Bad Block Marker. */
|
||||
if (bbm && (nand->options & NAND_NEED_SCRAMBLING))
|
||||
sunxi_nfc_randomize_bbm(nand, page, oob);
|
||||
}
|
||||
|
||||
/*
|
||||
* On H6/H6 the user_data length has to be set in specific registers
|
||||
* before writing.
|
||||
*/
|
||||
static void sunxi_nfc_reset_user_data_len(struct sunxi_nfc *nfc)
|
||||
{
|
||||
int loop_step = NFC_REG_USER_DATA_LEN_CAPACITY;
|
||||
|
||||
/* not all SoCs have this register */
|
||||
if (!nfc->caps->reg_user_data_len)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < nfc->caps->max_ecc_steps; i += loop_step)
|
||||
writel(0, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, i));
|
||||
}
|
||||
|
||||
static void sunxi_nfc_set_user_data_len(struct sunxi_nfc *nfc,
|
||||
int len, int step)
|
||||
{
|
||||
bool found = false;
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
/* not all SoCs have this register */
|
||||
if (!nfc->caps->reg_user_data_len)
|
||||
return;
|
||||
|
||||
for (i = 0; i < nfc->caps->nuser_data_tab; i++) {
|
||||
if (len == nfc->caps->user_data_len_tab[i]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
dev_warn(nfc->dev,
|
||||
"Unsupported length for user data reg: %d\n", len);
|
||||
return;
|
||||
}
|
||||
|
||||
val = readl(nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step));
|
||||
|
||||
val &= ~NFC_USER_DATA_LEN_MSK(step);
|
||||
val |= field_prep(NFC_USER_DATA_LEN_MSK(step), i);
|
||||
writel(val, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step));
|
||||
}
|
||||
|
||||
static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand,
|
||||
const u8 *oob, int step,
|
||||
bool bbm, int page)
|
||||
{
|
||||
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||
u8 user_data[4];
|
||||
u8 user_data[USER_DATA_SZ];
|
||||
|
||||
/* Randomize the Bad Block Marker. */
|
||||
if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) {
|
||||
|
|
@ -740,7 +900,7 @@ static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand,
|
|||
}
|
||||
|
||||
writel(sunxi_nfc_buf_to_user_data(oob),
|
||||
nfc->regs + NFC_REG_USER_DATA(step));
|
||||
nfc->regs + NFC_REG_USER_DATA(nfc, step));
|
||||
}
|
||||
|
||||
static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand,
|
||||
|
|
@ -757,7 +917,8 @@ static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand,
|
|||
}
|
||||
|
||||
static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob,
|
||||
int step, u32 status, bool *erased)
|
||||
int step, u32 status, u32 pattern_found,
|
||||
bool *erased)
|
||||
{
|
||||
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||
struct nand_ecc_ctrl *ecc = &nand->ecc;
|
||||
|
|
@ -768,10 +929,10 @@ static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob,
|
|||
if (status & NFC_ECC_ERR(step))
|
||||
return -EBADMSG;
|
||||
|
||||
if (status & NFC_ECC_PAT_FOUND(step)) {
|
||||
if (pattern_found & BIT(step)) {
|
||||
u8 pattern;
|
||||
|
||||
if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) {
|
||||
if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID(nfc)) & 0x1))) {
|
||||
pattern = 0x0;
|
||||
} else {
|
||||
pattern = 0xff;
|
||||
|
|
@ -782,12 +943,12 @@ static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob,
|
|||
memset(data, pattern, ecc->size);
|
||||
|
||||
if (oob)
|
||||
memset(oob, pattern, ecc->bytes + 4);
|
||||
memset(oob, pattern, ecc->bytes + USER_DATA_SZ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step));
|
||||
tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(nfc, step));
|
||||
|
||||
return NFC_ECC_ERR_CNT(step, tmp);
|
||||
}
|
||||
|
|
@ -802,6 +963,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
|
|||
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||
struct nand_ecc_ctrl *ecc = &nand->ecc;
|
||||
int raw_mode = 0;
|
||||
u32 pattern_found;
|
||||
bool erased;
|
||||
int ret;
|
||||
|
||||
|
|
@ -817,6 +979,8 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
sunxi_nfc_reset_user_data_len(nfc);
|
||||
sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0);
|
||||
sunxi_nfc_randomizer_config(nand, page, false);
|
||||
sunxi_nfc_randomizer_enable(nand);
|
||||
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
|
||||
|
|
@ -827,10 +991,14 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
*cur_off = oob_off + ecc->bytes + 4;
|
||||
*cur_off = oob_off + ecc->bytes + USER_DATA_SZ;
|
||||
|
||||
pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found);
|
||||
pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found);
|
||||
|
||||
ret = sunxi_nfc_hw_ecc_correct(nand, data, oob_required ? oob : NULL, 0,
|
||||
readl(nfc->regs + NFC_REG_ECC_ST),
|
||||
pattern_found,
|
||||
&erased);
|
||||
if (erased)
|
||||
return 1;
|
||||
|
|
@ -847,11 +1015,11 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
|
|||
memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE,
|
||||
ecc->size);
|
||||
|
||||
nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4,
|
||||
false);
|
||||
nand_change_read_column_op(nand, oob_off, oob,
|
||||
ecc->bytes + USER_DATA_SZ, false);
|
||||
|
||||
ret = nand_check_erased_ecc_chunk(data, ecc->size,
|
||||
oob, ecc->bytes + 4,
|
||||
ret = nand_check_erased_ecc_chunk(data, ecc->size, oob,
|
||||
ecc->bytes + USER_DATA_SZ,
|
||||
NULL, 0, ecc->strength);
|
||||
if (ret >= 0)
|
||||
raw_mode = 1;
|
||||
|
|
@ -861,7 +1029,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
|
|||
if (oob_required) {
|
||||
nand_change_read_column_op(nand, oob_off, NULL, 0,
|
||||
false);
|
||||
sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + 4,
|
||||
sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + USER_DATA_SZ,
|
||||
true, page);
|
||||
|
||||
sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, 0,
|
||||
|
|
@ -911,7 +1079,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
|
|||
unsigned int max_bitflips = 0;
|
||||
int ret, i, raw_mode = 0;
|
||||
struct scatterlist sg;
|
||||
u32 status, wait;
|
||||
u32 status, pattern_found, wait;
|
||||
|
||||
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
|
||||
if (ret)
|
||||
|
|
@ -923,6 +1091,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
|
|||
return ret;
|
||||
|
||||
sunxi_nfc_hw_ecc_enable(nand);
|
||||
sunxi_nfc_reset_user_data_len(nfc);
|
||||
sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0);
|
||||
sunxi_nfc_randomizer_config(nand, page, false);
|
||||
sunxi_nfc_randomizer_enable(nand);
|
||||
|
||||
|
|
@ -952,17 +1122,20 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
|
|||
return ret;
|
||||
|
||||
status = readl(nfc->regs + NFC_REG_ECC_ST);
|
||||
pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found);
|
||||
pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found);
|
||||
|
||||
for (i = 0; i < nchunks; i++) {
|
||||
int data_off = i * ecc->size;
|
||||
int oob_off = i * (ecc->bytes + 4);
|
||||
int oob_off = i * (ecc->bytes + USER_DATA_SZ);
|
||||
u8 *data = buf + data_off;
|
||||
u8 *oob = nand->oob_poi + oob_off;
|
||||
bool erased;
|
||||
|
||||
ret = sunxi_nfc_hw_ecc_correct(nand, randomized ? data : NULL,
|
||||
oob_required ? oob : NULL,
|
||||
i, status, &erased);
|
||||
i, status, pattern_found,
|
||||
&erased);
|
||||
|
||||
/* ECC errors are handled in the second loop. */
|
||||
if (ret < 0)
|
||||
|
|
@ -972,7 +1145,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
|
|||
/* TODO: use DMA to retrieve OOB */
|
||||
nand_change_read_column_op(nand,
|
||||
mtd->writesize + oob_off,
|
||||
oob, ecc->bytes + 4, false);
|
||||
oob, ecc->bytes + USER_DATA_SZ, false);
|
||||
|
||||
sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, i,
|
||||
!i, page);
|
||||
|
|
@ -984,10 +1157,10 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
|
|||
sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret);
|
||||
}
|
||||
|
||||
if (status & NFC_ECC_ERR_MSK) {
|
||||
if (status & NFC_ECC_ERR_MSK(nfc)) {
|
||||
for (i = 0; i < nchunks; i++) {
|
||||
int data_off = i * ecc->size;
|
||||
int oob_off = i * (ecc->bytes + 4);
|
||||
int oob_off = i * (ecc->bytes + USER_DATA_SZ);
|
||||
u8 *data = buf + data_off;
|
||||
u8 *oob = nand->oob_poi + oob_off;
|
||||
|
||||
|
|
@ -1007,10 +1180,10 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
|
|||
/* TODO: use DMA to retrieve OOB */
|
||||
nand_change_read_column_op(nand,
|
||||
mtd->writesize + oob_off,
|
||||
oob, ecc->bytes + 4, false);
|
||||
oob, ecc->bytes + USER_DATA_SZ, false);
|
||||
|
||||
ret = nand_check_erased_ecc_chunk(data, ecc->size,
|
||||
oob, ecc->bytes + 4,
|
||||
ret = nand_check_erased_ecc_chunk(data, ecc->size, oob,
|
||||
ecc->bytes + USER_DATA_SZ,
|
||||
NULL, 0,
|
||||
ecc->strength);
|
||||
if (ret >= 0)
|
||||
|
|
@ -1052,6 +1225,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand,
|
|||
|
||||
sunxi_nfc_randomizer_config(nand, page, false);
|
||||
sunxi_nfc_randomizer_enable(nand);
|
||||
sunxi_nfc_reset_user_data_len(nfc);
|
||||
sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0);
|
||||
sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, 0, bbm, page);
|
||||
|
||||
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
|
||||
|
|
@ -1063,7 +1238,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
*cur_off = oob_off + ecc->bytes + 4;
|
||||
*cur_off = oob_off + ecc->bytes + USER_DATA_SZ;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1074,7 +1249,7 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip *nand,
|
|||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(nand);
|
||||
struct nand_ecc_ctrl *ecc = &nand->ecc;
|
||||
int offset = ((ecc->bytes + 4) * ecc->steps);
|
||||
int offset = ((ecc->bytes + USER_DATA_SZ) * ecc->steps);
|
||||
int len = mtd->oobsize - offset;
|
||||
|
||||
if (len <= 0)
|
||||
|
|
@ -1107,7 +1282,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf,
|
|||
|
||||
for (i = 0; i < ecc->steps; i++) {
|
||||
int data_off = i * ecc->size;
|
||||
int oob_off = i * (ecc->bytes + 4);
|
||||
int oob_off = i * (ecc->bytes + USER_DATA_SZ);
|
||||
u8 *data = buf + data_off;
|
||||
u8 *oob = nand->oob_poi + oob_off;
|
||||
|
||||
|
|
@ -1166,7 +1341,7 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand,
|
|||
for (i = data_offs / ecc->size;
|
||||
i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {
|
||||
int data_off = i * ecc->size;
|
||||
int oob_off = i * (ecc->bytes + 4);
|
||||
int oob_off = i * (ecc->bytes + USER_DATA_SZ);
|
||||
u8 *data = bufpoi + data_off;
|
||||
u8 *oob = nand->oob_poi + oob_off;
|
||||
|
||||
|
|
@ -1220,7 +1395,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand,
|
|||
|
||||
for (i = 0; i < ecc->steps; i++) {
|
||||
int data_off = i * ecc->size;
|
||||
int oob_off = i * (ecc->bytes + 4);
|
||||
int oob_off = i * (ecc->bytes + USER_DATA_SZ);
|
||||
const u8 *data = buf + data_off;
|
||||
const u8 *oob = nand->oob_poi + oob_off;
|
||||
|
||||
|
|
@ -1258,7 +1433,7 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct nand_chip *nand,
|
|||
for (i = data_offs / ecc->size;
|
||||
i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) {
|
||||
int data_off = i * ecc->size;
|
||||
int oob_off = i * (ecc->bytes + 4);
|
||||
int oob_off = i * (ecc->bytes + USER_DATA_SZ);
|
||||
const u8 *data = buf + data_off;
|
||||
const u8 *oob = nand->oob_poi + oob_off;
|
||||
|
||||
|
|
@ -1296,10 +1471,12 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
|
|||
if (ret)
|
||||
goto pio_fallback;
|
||||
|
||||
sunxi_nfc_reset_user_data_len(nfc);
|
||||
for (i = 0; i < ecc->steps; i++) {
|
||||
const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4));
|
||||
const u8 *oob = nand->oob_poi + (i * (ecc->bytes + USER_DATA_SZ));
|
||||
|
||||
sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, i, !i, page);
|
||||
sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, i);
|
||||
}
|
||||
|
||||
nand_prog_page_begin_op(nand, page, 0, NULL, 0);
|
||||
|
|
@ -1567,7 +1744,7 @@ static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
|
|||
if (section >= ecc->steps)
|
||||
return -ERANGE;
|
||||
|
||||
oobregion->offset = section * (ecc->bytes + 4) + 4;
|
||||
oobregion->offset = section * (ecc->bytes + USER_DATA_SZ) + 4;
|
||||
oobregion->length = ecc->bytes;
|
||||
|
||||
return 0;
|
||||
|
|
@ -1601,10 +1778,10 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section,
|
|||
if (section == ecc->steps && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
|
||||
return -ERANGE;
|
||||
|
||||
oobregion->offset = section * (ecc->bytes + 4);
|
||||
oobregion->offset = section * (ecc->bytes + USER_DATA_SZ);
|
||||
|
||||
if (section < ecc->steps)
|
||||
oobregion->length = 4;
|
||||
oobregion->length = USER_DATA_SZ;
|
||||
else
|
||||
oobregion->length = mtd->oobsize - oobregion->offset;
|
||||
|
||||
|
|
@ -1620,9 +1797,9 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
|
|||
struct nand_ecc_ctrl *ecc,
|
||||
struct device_node *np)
|
||||
{
|
||||
static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
|
||||
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
|
||||
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||
const u8 *strengths = nfc->caps->ecc_strengths;
|
||||
struct mtd_info *mtd = nand_to_mtd(nand);
|
||||
struct nand_device *nanddev = mtd_to_nanddev(mtd);
|
||||
int nsectors;
|
||||
|
|
@ -1638,7 +1815,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
|
|||
bytes = (mtd->oobsize - 2) / nsectors;
|
||||
|
||||
/* 4 non-ECC bytes are added before each ECC bytes section */
|
||||
bytes -= 4;
|
||||
bytes -= USER_DATA_SZ;
|
||||
|
||||
/* and bytes has to be even. */
|
||||
if (bytes % 2)
|
||||
|
|
@ -1646,7 +1823,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
|
|||
|
||||
ecc->strength = bytes * 8 / fls(8 * ecc->size);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(strengths); i++) {
|
||||
for (i = 0; i < nfc->caps->nstrengths; i++) {
|
||||
if (strengths[i] > ecc->strength)
|
||||
break;
|
||||
}
|
||||
|
|
@ -1667,7 +1844,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
|
|||
}
|
||||
|
||||
/* Add ECC info retrieval from DT */
|
||||
for (i = 0; i < ARRAY_SIZE(strengths); i++) {
|
||||
for (i = 0; i < nfc->caps->nstrengths; i++) {
|
||||
if (ecc->strength <= strengths[i]) {
|
||||
/*
|
||||
* Update ecc->strength value with the actual strength
|
||||
|
|
@ -1678,7 +1855,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
|
|||
}
|
||||
}
|
||||
|
||||
if (i >= ARRAY_SIZE(strengths)) {
|
||||
if (i >= nfc->caps->nstrengths) {
|
||||
dev_err(nfc->dev, "unsupported strength\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
|
@ -1691,7 +1868,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
|
|||
|
||||
nsectors = mtd->writesize / ecc->size;
|
||||
|
||||
if (mtd->oobsize < ((ecc->bytes + 4) * nsectors))
|
||||
if (mtd->oobsize < ((ecc->bytes + USER_DATA_SZ) * nsectors))
|
||||
return -EINVAL;
|
||||
|
||||
ecc->read_oob = sunxi_nfc_hw_ecc_read_oob;
|
||||
|
|
@ -1714,11 +1891,17 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
|
|||
ecc->read_oob_raw = nand_read_oob_std;
|
||||
ecc->write_oob_raw = nand_write_oob_std;
|
||||
|
||||
sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(i) | NFC_ECC_EXCEPTION |
|
||||
sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(nfc, i) | NFC_ECC_EXCEPTION |
|
||||
NFC_ECC_PIPELINE | NFC_ECC_EN;
|
||||
|
||||
if (ecc->size == 512)
|
||||
if (ecc->size == 512) {
|
||||
if (nfc->caps->has_ecc_block_512) {
|
||||
sunxi_nand->ecc.ecc_ctl |= NFC_ECC_BLOCK_512;
|
||||
} else {
|
||||
dev_err(nfc->dev, "512B ECC block not supported\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1807,7 +1990,7 @@ static int sunxi_nfc_exec_subop(struct nand_chip *nand,
|
|||
case NAND_OP_DATA_OUT_INSTR:
|
||||
start = nand_subop_get_data_start_off(subop, i);
|
||||
remaining = nand_subop_get_data_len(subop, i);
|
||||
cnt = min_t(u32, remaining, NFC_SRAM_SIZE);
|
||||
cnt = min_t(u32, remaining, nfc->caps->sram_size);
|
||||
cmd |= NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
|
||||
|
||||
if (instr->type == NAND_OP_DATA_OUT_INSTR) {
|
||||
|
|
@ -2094,6 +2277,10 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
|
|||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
nfc->caps = of_device_get_match_data(dev);
|
||||
if (!nfc->caps)
|
||||
return -EINVAL;
|
||||
|
||||
nfc->ahb_clk = devm_clk_get_enabled(dev, "ahb");
|
||||
if (IS_ERR(nfc->ahb_clk)) {
|
||||
dev_err(dev, "failed to retrieve ahb clk\n");
|
||||
|
|
@ -2106,6 +2293,22 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(nfc->mod_clk);
|
||||
}
|
||||
|
||||
if (nfc->caps->has_ecc_clk) {
|
||||
nfc->ecc_clk = devm_clk_get_enabled(dev, "ecc");
|
||||
if (IS_ERR(nfc->ecc_clk)) {
|
||||
dev_err(dev, "failed to retrieve ecc clk\n");
|
||||
return PTR_ERR(nfc->ecc_clk);
|
||||
}
|
||||
}
|
||||
|
||||
if (nfc->caps->has_mbus_clk) {
|
||||
nfc->mbus_clk = devm_clk_get_enabled(dev, "mbus");
|
||||
if (IS_ERR(nfc->mbus_clk)) {
|
||||
dev_err(dev, "failed to retrieve mbus clk\n");
|
||||
return PTR_ERR(nfc->mbus_clk);
|
||||
}
|
||||
}
|
||||
|
||||
nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb");
|
||||
if (IS_ERR(nfc->reset))
|
||||
return PTR_ERR(nfc->reset);
|
||||
|
|
@ -2116,12 +2319,6 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
nfc->caps = of_device_get_match_data(&pdev->dev);
|
||||
if (!nfc->caps) {
|
||||
ret = -EINVAL;
|
||||
goto out_ahb_reset_reassert;
|
||||
}
|
||||
|
||||
ret = sunxi_nfc_rst(nfc);
|
||||
if (ret)
|
||||
goto out_ahb_reset_reassert;
|
||||
|
|
@ -2168,15 +2365,81 @@ static void sunxi_nfc_remove(struct platform_device *pdev)
|
|||
dma_release_channel(nfc->dmac);
|
||||
}
|
||||
|
||||
static const u8 sunxi_ecc_strengths_a10[] = {
|
||||
16, 24, 28, 32, 40, 48, 56, 60, 64
|
||||
};
|
||||
|
||||
static const u8 sunxi_ecc_strengths_h6[] = {
|
||||
16, 24, 28, 32, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80
|
||||
};
|
||||
|
||||
static const u8 sunxi_user_data_len_h6[] = {
|
||||
0, 4, 8, 12, 16, 20, 24, 28, 32
|
||||
};
|
||||
|
||||
static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
|
||||
.has_ecc_block_512 = true,
|
||||
.reg_io_data = NFC_REG_A10_IO_DATA,
|
||||
.reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT,
|
||||
.reg_user_data = NFC_REG_A10_USER_DATA,
|
||||
.reg_spare_area = NFC_REG_A10_SPARE_AREA,
|
||||
.reg_pat_id = NFC_REG_A10_PAT_ID,
|
||||
.reg_pat_found = NFC_REG_ECC_ST,
|
||||
.random_en_mask = BIT(9),
|
||||
.random_dir_mask = BIT(10),
|
||||
.ecc_mode_mask = GENMASK(15, 12),
|
||||
.ecc_err_mask = GENMASK(15, 0),
|
||||
.pat_found_mask = GENMASK(31, 16),
|
||||
.dma_maxburst = 4,
|
||||
.ecc_strengths = sunxi_ecc_strengths_a10,
|
||||
.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10),
|
||||
.max_ecc_steps = 16,
|
||||
.sram_size = 1024,
|
||||
};
|
||||
|
||||
static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
|
||||
.has_mdma = true,
|
||||
.has_ecc_block_512 = true,
|
||||
.reg_io_data = NFC_REG_A23_IO_DATA,
|
||||
.reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT,
|
||||
.reg_user_data = NFC_REG_A10_USER_DATA,
|
||||
.reg_spare_area = NFC_REG_A10_SPARE_AREA,
|
||||
.reg_pat_id = NFC_REG_A10_PAT_ID,
|
||||
.reg_pat_found = NFC_REG_ECC_ST,
|
||||
.random_en_mask = BIT(9),
|
||||
.random_dir_mask = BIT(10),
|
||||
.ecc_mode_mask = GENMASK(15, 12),
|
||||
.ecc_err_mask = GENMASK(15, 0),
|
||||
.pat_found_mask = GENMASK(31, 16),
|
||||
.dma_maxburst = 8,
|
||||
.ecc_strengths = sunxi_ecc_strengths_a10,
|
||||
.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10),
|
||||
.max_ecc_steps = 16,
|
||||
.sram_size = 1024,
|
||||
};
|
||||
|
||||
static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = {
|
||||
.has_ecc_clk = true,
|
||||
.has_mbus_clk = true,
|
||||
.reg_io_data = NFC_REG_A23_IO_DATA,
|
||||
.reg_ecc_err_cnt = NFC_REG_H6_ECC_ERR_CNT,
|
||||
.reg_user_data = NFC_REG_H6_USER_DATA,
|
||||
.reg_user_data_len = NFC_REG_H6_USER_DATA_LEN,
|
||||
.reg_spare_area = NFC_REG_H6_SPARE_AREA,
|
||||
.reg_pat_id = NFC_REG_H6_PAT_ID,
|
||||
.reg_pat_found = NFC_REG_H6_PAT_FOUND,
|
||||
.random_en_mask = BIT(5),
|
||||
.random_dir_mask = BIT(6),
|
||||
.ecc_mode_mask = GENMASK(15, 8),
|
||||
.ecc_err_mask = GENMASK(31, 0),
|
||||
.pat_found_mask = GENMASK(31, 0),
|
||||
.dma_maxburst = 8,
|
||||
.ecc_strengths = sunxi_ecc_strengths_h6,
|
||||
.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_h6),
|
||||
.user_data_len_tab = sunxi_user_data_len_h6,
|
||||
.nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6),
|
||||
.max_ecc_steps = 32,
|
||||
.sram_size = 8192,
|
||||
};
|
||||
|
||||
static const struct of_device_id sunxi_nfc_ids[] = {
|
||||
|
|
@ -2188,6 +2451,10 @@ static const struct of_device_id sunxi_nfc_ids[] = {
|
|||
.compatible = "allwinner,sun8i-a23-nand-controller",
|
||||
.data = &sunxi_nfc_a23_caps,
|
||||
},
|
||||
{
|
||||
.compatible = "allwinner,sun50i-h616-nand-controller",
|
||||
.data = &sunxi_nfc_h616_caps,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
|
||||
|
|
|
|||
|
|
@ -1227,6 +1227,7 @@ static const struct nand_ops spinand_ops = {
|
|||
static const struct spinand_manufacturer *spinand_manufacturers[] = {
|
||||
&alliancememory_spinand_manufacturer,
|
||||
&ato_spinand_manufacturer,
|
||||
&esmt_8c_spinand_manufacturer,
|
||||
&esmt_c8_spinand_manufacturer,
|
||||
&fmsh_spinand_manufacturer,
|
||||
&foresee_spinand_manufacturer,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
/* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */
|
||||
#define SPINAND_MFR_ESMT_C8 0xc8
|
||||
#define SPINAND_MFR_ESMT_8C 0x8c
|
||||
|
||||
#define ESMT_F50L1G41LB_CFG_OTP_PROTECT BIT(7)
|
||||
#define ESMT_F50L1G41LB_CFG_OTP_LOCK \
|
||||
|
|
@ -184,6 +185,21 @@ static const struct spinand_fact_otp_ops f50l1g41lb_fact_otp_ops = {
|
|||
.read = spinand_fact_otp_read,
|
||||
};
|
||||
|
||||
|
||||
static const struct spinand_info esmt_8c_spinand_table[] = {
|
||||
SPINAND_INFO("F50L1G41LC",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x2C),
|
||||
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),
|
||||
0,
|
||||
SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL),
|
||||
SPINAND_USER_OTP_INFO(28, 2, &f50l1g41lb_user_otp_ops),
|
||||
SPINAND_FACT_OTP_INFO(2, 0, &f50l1g41lb_fact_otp_ops)),
|
||||
};
|
||||
|
||||
static const struct spinand_info esmt_c8_spinand_table[] = {
|
||||
SPINAND_INFO("F50L1G41LB",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f,
|
||||
|
|
@ -224,6 +240,14 @@ static const struct spinand_info esmt_c8_spinand_table[] = {
|
|||
static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = {
|
||||
};
|
||||
|
||||
const struct spinand_manufacturer esmt_8c_spinand_manufacturer = {
|
||||
.id = SPINAND_MFR_ESMT_8C,
|
||||
.name = "ESMT",
|
||||
.chips = esmt_8c_spinand_table,
|
||||
.nchips = ARRAY_SIZE(esmt_8c_spinand_table),
|
||||
.ops = &esmt_spinand_manuf_ops,
|
||||
};
|
||||
|
||||
const struct spinand_manufacturer esmt_c8_spinand_manufacturer = {
|
||||
.id = SPINAND_MFR_ESMT_C8,
|
||||
.name = "ESMT",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,13 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/mtd/spinand.h>
|
||||
|
||||
#define FM25S01BI3_STATUS_ECC_MASK (7 << 4)
|
||||
#define FM25S01BI3_STATUS_ECC_NO_BITFLIPS (0 << 4)
|
||||
#define FM25S01BI3_STATUS_ECC_1_3_BITFLIPS (1 << 4)
|
||||
#define FM25S01BI3_STATUS_ECC_UNCOR_ERROR (2 << 4)
|
||||
#define FM25S01BI3_STATUS_ECC_4_6_BITFLIPS (3 << 4)
|
||||
#define FM25S01BI3_STATUS_ECC_7_8_BITFLIPS (5 << 4)
|
||||
|
||||
#define SPINAND_MFR_FMSH 0xA1
|
||||
|
||||
static SPINAND_OP_VARIANTS(read_cache_variants,
|
||||
|
|
@ -45,11 +52,66 @@ static int fm25s01a_ooblayout_free(struct mtd_info *mtd, int section,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fm25s01bi3_ecc_get_status(struct spinand_device *spinand,
|
||||
u8 status)
|
||||
{
|
||||
switch (status & FM25S01BI3_STATUS_ECC_MASK) {
|
||||
case FM25S01BI3_STATUS_ECC_NO_BITFLIPS:
|
||||
return 0;
|
||||
|
||||
case FM25S01BI3_STATUS_ECC_UNCOR_ERROR:
|
||||
return -EBADMSG;
|
||||
|
||||
case FM25S01BI3_STATUS_ECC_1_3_BITFLIPS:
|
||||
return 3;
|
||||
|
||||
case FM25S01BI3_STATUS_ECC_4_6_BITFLIPS:
|
||||
return 6;
|
||||
|
||||
case FM25S01BI3_STATUS_ECC_7_8_BITFLIPS:
|
||||
return 8;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int fm25s01bi3_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
region->offset = 64;
|
||||
region->length = 64;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fm25s01bi3_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section > 3)
|
||||
return -ERANGE;
|
||||
|
||||
region->offset = (16 * section) + 4;
|
||||
region->length = 12;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mtd_ooblayout_ops fm25s01a_ooblayout = {
|
||||
.ecc = fm25s01a_ooblayout_ecc,
|
||||
.free = fm25s01a_ooblayout_free,
|
||||
};
|
||||
|
||||
static const struct mtd_ooblayout_ops fm25s01bi3_ooblayout = {
|
||||
.ecc = fm25s01bi3_ooblayout_ecc,
|
||||
.free = fm25s01bi3_ooblayout_free,
|
||||
};
|
||||
|
||||
static const struct spinand_info fmsh_spinand_table[] = {
|
||||
SPINAND_INFO("FM25S01A",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4),
|
||||
|
|
@ -60,6 +122,16 @@ static const struct spinand_info fmsh_spinand_table[] = {
|
|||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)),
|
||||
SPINAND_INFO("FM25S01BI3",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd4),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&fm25s01bi3_ooblayout,
|
||||
fm25s01bi3_ecc_get_status)),
|
||||
};
|
||||
|
||||
static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = {
|
||||
|
|
|
|||
|
|
@ -44,8 +44,7 @@ static ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr,
|
|||
struct sm_sysfs_attribute *sm_attr =
|
||||
container_of(attr, struct sm_sysfs_attribute, dev_attr);
|
||||
|
||||
strncpy(buf, sm_attr->data, sm_attr->len);
|
||||
return sm_attr->len;
|
||||
return sysfs_emit(buf, "%.*s", sm_attr->len, sm_attr->data);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -157,7 +156,7 @@ static int sm_read_lba(struct sm_oob *oob)
|
|||
if (!memcmp(oob, erased_pattern, SM_OOB_SIZE))
|
||||
return -1;
|
||||
|
||||
/* Now check is both copies of the LBA differ too much */
|
||||
/* Now check if both copies of the LBA differ too much */
|
||||
lba_test = *(uint16_t *)oob->lba_copy1 ^ *(uint16_t*)oob->lba_copy2;
|
||||
if (lba_test && !is_power_of_2(lba_test))
|
||||
return -2;
|
||||
|
|
|
|||
|
|
@ -2459,6 +2459,16 @@ spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps)
|
|||
¶ms->page_programs[ppidx]))
|
||||
*hwcaps &= ~BIT(cap);
|
||||
}
|
||||
|
||||
/* Some SPI controllers might not support CR read opcode. */
|
||||
if (!(nor->flags & SNOR_F_NO_READ_CR)) {
|
||||
struct spi_mem_op op = SPI_NOR_RDCR_OP(nor->bouncebuf);
|
||||
|
||||
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
|
||||
|
||||
if (spi_nor_spimem_check_op(nor, &op))
|
||||
nor->flags |= SNOR_F_NO_READ_CR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -409,6 +409,10 @@ struct spi_nor_flash_parameter {
|
|||
* flash parameters when information provided by the flash_info
|
||||
* table is incomplete or wrong.
|
||||
* @post_bfpt: called after the BFPT table has been parsed
|
||||
* @smpt_read_dummy: called during SMPT table is being parsed. Used to fix the
|
||||
* number of dummy cycles in read register ops.
|
||||
* @smpt_map_id: called after map ID in SMPT table has been determined for the
|
||||
* case the map ID is wrong and needs to be fixed.
|
||||
* @post_sfdp: called after SFDP has been parsed (is also called for SPI NORs
|
||||
* that do not support RDSFDP). Typically used to tweak various
|
||||
* parameters that could not be extracted by other means (i.e.
|
||||
|
|
@ -426,6 +430,8 @@ struct spi_nor_fixups {
|
|||
int (*post_bfpt)(struct spi_nor *nor,
|
||||
const struct sfdp_parameter_header *bfpt_header,
|
||||
const struct sfdp_bfpt *bfpt);
|
||||
void (*smpt_read_dummy)(const struct spi_nor *nor, u8 *read_dummy);
|
||||
void (*smpt_map_id)(const struct spi_nor *nor, u8 *map_id);
|
||||
int (*post_sfdp)(struct spi_nor *nor);
|
||||
int (*late_init)(struct spi_nor *nor);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -127,9 +127,36 @@ static int micron_st_nor_set_octal_dtr(struct spi_nor *nor, bool enable)
|
|||
micron_st_nor_octal_dtr_dis(nor);
|
||||
}
|
||||
|
||||
static void mt35xu512aba_default_init(struct spi_nor *nor)
|
||||
static int micron_st_nor_four_die_late_init(struct spi_nor *nor)
|
||||
{
|
||||
nor->params->set_octal_dtr = micron_st_nor_set_octal_dtr;
|
||||
struct spi_nor_flash_parameter *params = nor->params;
|
||||
|
||||
params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE;
|
||||
params->n_dice = 4;
|
||||
|
||||
/*
|
||||
* Unfortunately the die erase opcode does not have a 4-byte opcode
|
||||
* correspondent for these flashes. The SFDP 4BAIT table fails to
|
||||
* consider the die erase too. We're forced to enter in the 4 byte
|
||||
* address mode in order to benefit of the die erase.
|
||||
*/
|
||||
return spi_nor_set_4byte_addr_mode(nor, true);
|
||||
}
|
||||
|
||||
static int micron_st_nor_two_die_late_init(struct spi_nor *nor)
|
||||
{
|
||||
struct spi_nor_flash_parameter *params = nor->params;
|
||||
|
||||
params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE;
|
||||
params->n_dice = 2;
|
||||
|
||||
/*
|
||||
* Unfortunately the die erase opcode does not have a 4-byte opcode
|
||||
* correspondent for these flashes. The SFDP 4BAIT table fails to
|
||||
* consider the die erase too. We're forced to enter in the 4 byte
|
||||
* address mode in order to benefit of the die erase.
|
||||
*/
|
||||
return spi_nor_set_4byte_addr_mode(nor, true);
|
||||
}
|
||||
|
||||
static int mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor)
|
||||
|
|
@ -155,22 +182,38 @@ static int mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor)
|
|||
}
|
||||
|
||||
static const struct spi_nor_fixups mt35xu512aba_fixups = {
|
||||
.default_init = mt35xu512aba_default_init,
|
||||
.post_sfdp = mt35xu512aba_post_sfdp_fixup,
|
||||
};
|
||||
|
||||
static const struct spi_nor_fixups mt35xu01gbba_fixups = {
|
||||
.post_sfdp = mt35xu512aba_post_sfdp_fixup,
|
||||
.late_init = micron_st_nor_two_die_late_init,
|
||||
};
|
||||
|
||||
static const struct flash_info micron_nor_parts[] = {
|
||||
{
|
||||
/* MT35XU512ABA */
|
||||
.id = SNOR_ID(0x2c, 0x5b, 0x1a),
|
||||
.name = "mt35xu512aba",
|
||||
.sector_size = SZ_128K,
|
||||
.size = SZ_64M,
|
||||
.no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ |
|
||||
SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP,
|
||||
.mfr_flags = USE_FSR,
|
||||
.fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE,
|
||||
.fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE,
|
||||
.fixups = &mt35xu512aba_fixups,
|
||||
}, {
|
||||
/* MT35XU01GBBA */
|
||||
.id = SNOR_ID(0x2c, 0x5b, 0x1b),
|
||||
.mfr_flags = USE_FSR,
|
||||
.fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE,
|
||||
.fixups = &mt35xu01gbba_fixups,
|
||||
}, {
|
||||
/*
|
||||
* The MT35XU02GCBA flash device does not support chip erase,
|
||||
* according to its datasheet. It supports die erase, which
|
||||
* means the current driver implementation will likely need to
|
||||
* be converted to use die erase. Furthermore, similar to the
|
||||
* MT35XU01GBBA, the SPI_NOR_IO_MODE_EN_VOLATILE flag probably
|
||||
* needs to be enabled.
|
||||
*
|
||||
* TODO: Fix these and test on real hardware.
|
||||
*/
|
||||
.id = SNOR_ID(0x2c, 0x5b, 0x1c),
|
||||
.name = "mt35xu02g",
|
||||
.sector_size = SZ_128K,
|
||||
|
|
@ -193,48 +236,16 @@ static const struct spi_nor_fixups mt25qu512a_fixups = {
|
|||
.post_bfpt = mt25qu512a_post_bfpt_fixup,
|
||||
};
|
||||
|
||||
static int st_nor_four_die_late_init(struct spi_nor *nor)
|
||||
{
|
||||
struct spi_nor_flash_parameter *params = nor->params;
|
||||
|
||||
params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE;
|
||||
params->n_dice = 4;
|
||||
|
||||
/*
|
||||
* Unfortunately the die erase opcode does not have a 4-byte opcode
|
||||
* correspondent for these flashes. The SFDP 4BAIT table fails to
|
||||
* consider the die erase too. We're forced to enter in the 4 byte
|
||||
* address mode in order to benefit of the die erase.
|
||||
*/
|
||||
return spi_nor_set_4byte_addr_mode(nor, true);
|
||||
}
|
||||
|
||||
static int st_nor_two_die_late_init(struct spi_nor *nor)
|
||||
{
|
||||
struct spi_nor_flash_parameter *params = nor->params;
|
||||
|
||||
params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE;
|
||||
params->n_dice = 2;
|
||||
|
||||
/*
|
||||
* Unfortunately the die erase opcode does not have a 4-byte opcode
|
||||
* correspondent for these flashes. The SFDP 4BAIT table fails to
|
||||
* consider the die erase too. We're forced to enter in the 4 byte
|
||||
* address mode in order to benefit of the die erase.
|
||||
*/
|
||||
return spi_nor_set_4byte_addr_mode(nor, true);
|
||||
}
|
||||
|
||||
static const struct spi_nor_fixups n25q00_fixups = {
|
||||
.late_init = st_nor_four_die_late_init,
|
||||
.late_init = micron_st_nor_four_die_late_init,
|
||||
};
|
||||
|
||||
static const struct spi_nor_fixups mt25q01_fixups = {
|
||||
.late_init = st_nor_two_die_late_init,
|
||||
.late_init = micron_st_nor_two_die_late_init,
|
||||
};
|
||||
|
||||
static const struct spi_nor_fixups mt25q02_fixups = {
|
||||
.late_init = st_nor_four_die_late_init,
|
||||
.late_init = micron_st_nor_four_die_late_init,
|
||||
};
|
||||
|
||||
static const struct flash_info st_nor_parts[] = {
|
||||
|
|
@ -635,6 +646,8 @@ static int micron_st_nor_late_init(struct spi_nor *nor)
|
|||
if (!params->set_4byte_addr_mode)
|
||||
params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_wren_en4b_ex4b;
|
||||
|
||||
params->set_octal_dtr = micron_st_nor_set_octal_dtr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -699,6 +699,17 @@ static u8 spi_nor_smpt_addr_nbytes(const struct spi_nor *nor, const u32 settings
|
|||
}
|
||||
}
|
||||
|
||||
static void spi_nor_smpt_read_dummy_fixups(const struct spi_nor *nor,
|
||||
u8 *read_dummy)
|
||||
{
|
||||
if (nor->manufacturer && nor->manufacturer->fixups &&
|
||||
nor->manufacturer->fixups->smpt_read_dummy)
|
||||
nor->manufacturer->fixups->smpt_read_dummy(nor, read_dummy);
|
||||
|
||||
if (nor->info->fixups && nor->info->fixups->smpt_read_dummy)
|
||||
nor->info->fixups->smpt_read_dummy(nor, read_dummy);
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_smpt_read_dummy() - return the configuration detection command read
|
||||
* latency, in clock cycles.
|
||||
|
|
@ -711,11 +722,24 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
|
|||
{
|
||||
u8 read_dummy = SMPT_CMD_READ_DUMMY(settings);
|
||||
|
||||
if (read_dummy == SMPT_CMD_READ_DUMMY_IS_VARIABLE)
|
||||
return nor->read_dummy;
|
||||
if (read_dummy == SMPT_CMD_READ_DUMMY_IS_VARIABLE) {
|
||||
read_dummy = nor->read_dummy;
|
||||
spi_nor_smpt_read_dummy_fixups(nor, &read_dummy);
|
||||
}
|
||||
|
||||
return read_dummy;
|
||||
}
|
||||
|
||||
static void spi_nor_smpt_map_id_fixups(const struct spi_nor *nor, u8 *map_id)
|
||||
{
|
||||
if (nor->manufacturer && nor->manufacturer->fixups &&
|
||||
nor->manufacturer->fixups->smpt_map_id)
|
||||
nor->manufacturer->fixups->smpt_map_id(nor, map_id);
|
||||
|
||||
if (nor->info->fixups && nor->info->fixups->smpt_map_id)
|
||||
nor->info->fixups->smpt_map_id(nor, map_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_get_map_in_use() - get the configuration map in use
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
|
|
@ -769,6 +793,8 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt,
|
|||
map_id = map_id << 1 | !!(*buf & read_data_mask);
|
||||
}
|
||||
|
||||
spi_nor_smpt_map_id_fixups(nor, &map_id);
|
||||
|
||||
/*
|
||||
* If command descriptors are provided, they always precede map
|
||||
* descriptors in the table. There is no need to start the iteration
|
||||
|
|
|
|||
|
|
@ -785,8 +785,46 @@ s25fs_s_nor_post_bfpt_fixups(struct spi_nor *nor,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void s25fs_s_nor_smpt_read_dummy(const struct spi_nor *nor,
|
||||
u8 *read_dummy)
|
||||
{
|
||||
/*
|
||||
* The configuration detection dwords in S25FS-S SMPT has 65h as
|
||||
* command instruction and 'variable' as configuration detection command
|
||||
* latency. Set 8 dummy cycles as it is factory default for 65h (read
|
||||
* any register) op.
|
||||
*/
|
||||
*read_dummy = 8;
|
||||
}
|
||||
|
||||
static void s25fs_s_nor_smpt_map_id_dummy(const struct spi_nor *nor, u8 *map_id)
|
||||
{
|
||||
/*
|
||||
* The S25FS512S chip supports:
|
||||
* - Hybrid sector option which has physical set of eight 4-KB sectors
|
||||
* and one 224-KB sector at the top or bottom of address space with
|
||||
* all remaining sectors of 256-KB
|
||||
* - Uniform sector option which has uniform 256-KB sectors
|
||||
*
|
||||
* On the other hand, the datasheet rev.O Table 71 on page 153 JEDEC
|
||||
* Sector Map Parameter Dword-6 Config. Detect-3 does use CR3NV[1] to
|
||||
* discern 64-KB(CR3NV[1]=0) and 256-KB(CR3NV[1]=1) uniform sectors
|
||||
* device configuration. And in section 7.5.5.1 Configuration Register 3
|
||||
* Non-volatile (CR3NV) page 61, the CR3NV[1] is RFU Reserved for Future
|
||||
* Use and set to 0, which means 64-KB uniform. Since the device does
|
||||
* not support 64-KB uniform sectors in any configuration, parsing SMPT
|
||||
* table cannot find a valid sector map entry and fails. Fix this up by
|
||||
* setting SMPT by overwriting the CR3NV[1] value to 1, as the table
|
||||
* expects.
|
||||
*/
|
||||
if (nor->params->size == SZ_64M)
|
||||
*map_id |= BIT(0);
|
||||
}
|
||||
|
||||
static const struct spi_nor_fixups s25fs_s_nor_fixups = {
|
||||
.post_bfpt = s25fs_s_nor_post_bfpt_fixups,
|
||||
.smpt_read_dummy = s25fs_s_nor_smpt_read_dummy,
|
||||
.smpt_map_id = s25fs_s_nor_smpt_map_id_dummy,
|
||||
};
|
||||
|
||||
static const struct flash_info spansion_nor_parts[] = {
|
||||
|
|
|
|||
|
|
@ -343,6 +343,30 @@ static const struct flash_info winbond_nor_parts[] = {
|
|||
.id = SNOR_ID(0xef, 0x80, 0x20),
|
||||
.name = "w25q512nwm",
|
||||
.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
|
||||
}, {
|
||||
/* W25Q01NWxxIQ */
|
||||
.id = SNOR_ID(0xef, 0x60, 0x21),
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
|
||||
}, {
|
||||
/* W25Q01NWxxIM */
|
||||
.id = SNOR_ID(0xef, 0x80, 0x21),
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
|
||||
}, {
|
||||
/* W25Q02NWxxIM */
|
||||
.id = SNOR_ID(0xef, 0x80, 0x22),
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
|
||||
}, {
|
||||
/* W25H512NWxxAM */
|
||||
.id = SNOR_ID(0xef, 0xa0, 0x20),
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
|
||||
}, {
|
||||
/* W25H01NWxxAM */
|
||||
.id = SNOR_ID(0xef, 0xa0, 0x21),
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
|
||||
}, {
|
||||
/* W25H02NWxxAM */
|
||||
.id = SNOR_ID(0xef, 0xa0, 0x22),
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -31,12 +31,12 @@
|
|||
* struct spear_smi_flash_info - platform structure for passing flash
|
||||
* information
|
||||
*
|
||||
* name: name of the serial nor flash for identification
|
||||
* mem_base: the memory base on which the flash is mapped
|
||||
* size: size of the flash in bytes
|
||||
* partitions: parition details
|
||||
* nr_partitions: number of partitions
|
||||
* fast_mode: whether flash supports fast mode
|
||||
* @name: name of the serial nor flash for identification
|
||||
* @mem_base: the memory base on which the flash is mapped
|
||||
* @size: size of the flash in bytes
|
||||
* @partitions: parition details
|
||||
* @nr_partitions: number of partitions
|
||||
* @fast_mode: whether flash supports fast mode
|
||||
*/
|
||||
|
||||
struct spear_smi_flash_info {
|
||||
|
|
@ -51,9 +51,10 @@ struct spear_smi_flash_info {
|
|||
/**
|
||||
* struct spear_smi_plat_data - platform structure for configuring smi
|
||||
*
|
||||
* clk_rate: clk rate at which SMI must operate
|
||||
* num_flashes: number of flashes present on board
|
||||
* board_flash_info: specific details of each flash present on board
|
||||
* @clk_rate: clk rate at which SMI must operate
|
||||
* @num_flashes: number of flashes present on board
|
||||
* @board_flash_info: specific details of each flash present on board
|
||||
* @np: array of DT node pointers for all possible flash chip devices
|
||||
*/
|
||||
struct spear_smi_plat_data {
|
||||
unsigned long clk_rate;
|
||||
|
|
|
|||
|
|
@ -354,6 +354,7 @@ struct spinand_manufacturer {
|
|||
/* SPI NAND manufacturers */
|
||||
extern const struct spinand_manufacturer alliancememory_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer ato_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer esmt_8c_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer fmsh_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer foresee_spinand_manufacturer;
|
||||
|
|
|
|||
Loading…
Reference in New Issue