mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 10:50:53 +07:00
MTD updates for 4.12-rc1:
NAND, from Boris: """ - some minor fixes/improvements on existing drivers (fsmc, gpio, ifc, davinci, brcmnand, omap) - a huge cleanup/rework of the denali driver accompanied with core fixes/improvements to simplify the driver code - a complete rewrite of the atmel driver to support new DT bindings make future evolution easier - the addition of per-vendor detection/initialization steps to avoid extending the nand_ids table with more extended-id entries """ SPI NOR, from Cyrille: """ - fixes in the hisi SPI controller driver. - fixes in the intel SPI controller driver. - fixes in the Mediatek SPI controller driver. - fixes to some SPI flash memories not supported the Chip Erase command. - add support to some new memory parts (Winbond, Macronix, Micron, ESMT). - add new driver for the STM32 QSPI controller. """ And a few fixes for Gemini and Versatile platforms on physmap-of -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJZE86yAAoJEFySrpd9RFgtlOoP/1o1s8dlKdd4TazdoxBTL2wy C4wPkqPWyfREcD5ZUYJgr6ENI2OnEwcAxAt2CXnqegx+ZIPToBW4/WK9gj/TNLRx AfSOz+EPPzo5uZwJPnfocgIFYuhsspymvmISwv66kPbjfkrSjo1l/K9nem3gh7an IkQdVVq8brvxNeDZOAzbsT2Y5DZNfs00g1jLXkcQrpfM0sWKcbHIUa0BTWy4WKGV ElTr+xh7QHh/Pd9/A5znd3xX54w5+YR/xe38jSBfTb0vEgw/RIfhIcnvxQ8G/7Se jE0+8GR5ZJGKwA9Xk5nFzS2G3uECMFNS75KfxkZ0LlEE6ivUvpDbokCbIU4bDOCt /8bWQf9AGA3gLHGgNUQTSt5HrkBXTGp917jtAZbI/y2MzTkLw3aAZ/m/j37vv9ON ezeGRO6VWK3bcimLFrt6KO5emYstmm4Tp4rRe3jakH7eyTlINDsecKtuMo2xVzyZ kK3tnDMdEntECAiKh3ndRdAUL3fs+/IdzWTAxnF9VQFQs1YxiZ1K8kY/zcN+rzbn CVkEhdm+tdDBx8XgOdfnOTGRAJ07dGOoDhLPR4/egC/ta6GIRkHQjFSwsW7bD9p9 phHH6nQX9Bpza1JV/xvljezoHjvZkny4UhRpLgYMowb41DXv7os7ZV+g7kf5sd0i mGzCH46j0DmWQ1u5/Q6j =dxj5 -----END PGP SIGNATURE----- Merge tag 'for-linus-20170510' of git://git.infradead.org/linux-mtd Pull MTD updates from Brian Norris: "NAND, from Boris: - some minor fixes/improvements on existing drivers (fsmc, gpio, ifc, davinci, brcmnand, omap) - a huge cleanup/rework of the denali driver accompanied with core fixes/improvements to simplify the driver code - a complete rewrite of the atmel driver to support new DT bindings make future evolution easier - the addition of per-vendor detection/initialization steps to avoid extending the nand_ids table with more extended-id entries SPI NOR, from Cyrille: - fixes in the hisi, intel and Mediatek SPI controller drivers - fixes to some SPI flash memories not supporting the Chip Erase command. - add support to some new memory parts (Winbond, Macronix, Micron, ESMT). - add new driver for the STM32 QSPI controller And a few fixes for Gemini and Versatile platforms on physmap-of" * tag 'for-linus-20170510' of git://git.infradead.org/linux-mtd: (100 commits) MAINTAINERS: Update NAND subsystem git repositories mtd: nand: gpio: update binding mtd: nand: add ooblayout for old hamming layout mtd: oxnas_nand: Allocating more than necessary in probe() dt-bindings: mtd: Document the STM32 QSPI bindings mtd: mtk-nor: set controller's address width according to nor flash mtd: spi-nor: add driver for STM32 quad spi flash controller mtd: nand: brcmnand: Check flash #WP pin status before nand erase/program mtd: nand: davinci: add comment on NAND subpage write status on keystone mtd: nand: omap2: Fix partition creation via cmdline mtdparts mtd: nand: NULL terminate a of_device_id table mtd: nand: Fix a couple error codes mtd: nand: allow drivers to request minimum alignment for passed buffer mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset mtd: nand: denali: allow to override revision number mtd: nand: denali_dt: use pdev instead of ofdev for platform_device mtd: nand: denali_dt: remove dma-mask DT property mtd: nand: denali: support 64bit capable DMA engine mtd: nand: denali_dt: enable HW_ECC_FIXUP for Altera SOCFPGA variant mtd: nand: denali: support HW_ECC_FIXUP capability ...
This commit is contained in:
commit
9786e34e0a
@ -1,4 +1,109 @@
|
||||
Atmel NAND flash
|
||||
Atmel NAND flash controller bindings
|
||||
|
||||
The NAND flash controller node should be defined under the EBI bus (see
|
||||
Documentation/devicetree/bindings/memory-controllers/atmel,ebi.txt).
|
||||
One or several NAND devices can be defined under this NAND controller.
|
||||
The NAND controller might be connected to an ECC engine.
|
||||
|
||||
* NAND controller bindings:
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one of the following
|
||||
"atmel,at91rm9200-nand-controller"
|
||||
"atmel,at91sam9260-nand-controller"
|
||||
"atmel,at91sam9261-nand-controller"
|
||||
"atmel,at91sam9g45-nand-controller"
|
||||
"atmel,sama5d3-nand-controller"
|
||||
- ranges: empty ranges property to forward EBI ranges definitions.
|
||||
- #address-cells: should be set to 2.
|
||||
- #size-cells: should be set to 1.
|
||||
- atmel,nfc-io: phandle to the NFC IO block. Only required for sama5d3
|
||||
controllers.
|
||||
- atmel,nfc-sram: phandle to the NFC SRAM block. Only required for sama5d3
|
||||
controllers.
|
||||
|
||||
Optional properties:
|
||||
- ecc-engine: phandle to the PMECC block. Only meaningful if the SoC embeds
|
||||
a PMECC engine.
|
||||
|
||||
* NAND device/chip bindings:
|
||||
|
||||
Required properties:
|
||||
- reg: describes the CS lines assigned to the NAND device. If the NAND device
|
||||
exposes multiple CS lines (multi-dies chips), your reg property will
|
||||
contain X tuples of 3 entries.
|
||||
1st entry: the CS line this NAND chip is connected to
|
||||
2nd entry: the base offset of the memory region assigned to this
|
||||
device (always 0)
|
||||
3rd entry: the memory region size (always 0x800000)
|
||||
|
||||
Optional properties:
|
||||
- rb-gpios: the GPIO(s) used to check the Ready/Busy status of the NAND.
|
||||
- cs-gpios: the GPIO(s) used to control the CS line.
|
||||
- det-gpios: the GPIO used to detect if a Smartmedia Card is present.
|
||||
- atmel,rb: an integer identifying the native Ready/Busy pin. Only meaningful
|
||||
on sama5 SoCs.
|
||||
|
||||
All generic properties described in
|
||||
Documentation/devicetree/bindings/mtd/{common,nand}.txt also apply to the NAND
|
||||
device node, and NAND partitions should be defined under the NAND node as
|
||||
described in Documentation/devicetree/bindings/mtd/partition.txt.
|
||||
|
||||
* ECC engine (PMECC) bindings:
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one of the following
|
||||
"atmel,at91sam9g45-pmecc"
|
||||
"atmel,sama5d4-pmecc"
|
||||
"atmel,sama5d2-pmecc"
|
||||
- reg: should contain 2 register ranges. The first one is pointing to the PMECC
|
||||
block, and the second one to the PMECC_ERRLOC block.
|
||||
|
||||
Example:
|
||||
|
||||
pmecc: ecc-engine@ffffc070 {
|
||||
compatible = "atmel,at91sam9g45-pmecc";
|
||||
reg = <0xffffc070 0x490>,
|
||||
<0xffffc500 0x100>;
|
||||
};
|
||||
|
||||
ebi: ebi@10000000 {
|
||||
compatible = "atmel,sama5d3-ebi";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
atmel,smc = <&hsmc>;
|
||||
reg = <0x10000000 0x10000000
|
||||
0x40000000 0x30000000>;
|
||||
ranges = <0x0 0x0 0x10000000 0x10000000
|
||||
0x1 0x0 0x40000000 0x10000000
|
||||
0x2 0x0 0x50000000 0x10000000
|
||||
0x3 0x0 0x60000000 0x10000000>;
|
||||
clocks = <&mck>;
|
||||
|
||||
nand_controller: nand-controller {
|
||||
compatible = "atmel,sama5d3-nand-controller";
|
||||
atmel,nfc-sram = <&nfc_sram>;
|
||||
atmel,nfc-io = <&nfc_io>;
|
||||
ecc-engine = <&pmecc>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
nand@3 {
|
||||
reg = <0x3 0x0 0x800000>;
|
||||
atmel,rb = <0>;
|
||||
|
||||
/*
|
||||
* Put generic NAND/MTD properties and
|
||||
* subnodes here.
|
||||
*/
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Deprecated bindings (should not be used in new device trees):
|
||||
|
||||
Required properties:
|
||||
- compatible: The possible values are:
|
||||
|
@ -1,11 +1,11 @@
|
||||
* Denali NAND controller
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "denali,denali-nand-dt"
|
||||
- compatible : should be one of the following:
|
||||
"altr,socfpga-denali-nand" - for Altera SOCFPGA
|
||||
- reg : should contain registers location and length for data and reg.
|
||||
- reg-names: Should contain the reg names "nand_data" and "denali_reg"
|
||||
- interrupts : The interrupt number.
|
||||
- dm-mask : DMA bit mask
|
||||
|
||||
The device tree may optionally contain sub-nodes describing partitions of the
|
||||
address space. See partition.txt for more detail.
|
||||
@ -15,9 +15,8 @@ Examples:
|
||||
nand: nand@ff900000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "denali,denali-nand-dt";
|
||||
compatible = "altr,socfpga-denali-nand";
|
||||
reg = <0xff900000 0x100000>, <0xffb80000 0x10000>;
|
||||
reg-names = "nand_data", "denali_reg";
|
||||
interrupts = <0 144 4>;
|
||||
dma-mask = <0xffffffff>;
|
||||
};
|
||||
|
@ -12,7 +12,7 @@ Required properties:
|
||||
- #address-cells, #size-cells : Must be present if the device has sub-nodes
|
||||
representing partitions.
|
||||
- gpios : Specifies the GPIO pins to control the NAND device. The order of
|
||||
GPIO references is: RDY, nCE, ALE, CLE, and an optional nWP.
|
||||
GPIO references is: RDY, nCE, ALE, CLE, and nWP. nCE and nWP are optional.
|
||||
|
||||
Optional properties:
|
||||
- bank-width : Width (in bytes) of the device. If not present, the width
|
||||
@ -36,7 +36,7 @@ gpio-nand@1,0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
gpios = <&banka 1 0>, /* RDY */
|
||||
<&banka 2 0>, /* nCE */
|
||||
<0>, /* nCE */
|
||||
<&banka 3 0>, /* ALE */
|
||||
<&banka 4 0>, /* CLE */
|
||||
<0>; /* nWP */
|
||||
|
43
Documentation/devicetree/bindings/mtd/stm32-quadspi.txt
Normal file
43
Documentation/devicetree/bindings/mtd/stm32-quadspi.txt
Normal file
@ -0,0 +1,43 @@
|
||||
* STMicroelectronics Quad Serial Peripheral Interface(QuadSPI)
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "st,stm32f469-qspi"
|
||||
- reg: the first contains the register location and length.
|
||||
the second contains the memory mapping address and length
|
||||
- reg-names: should contain the reg names "qspi" "qspi_mm"
|
||||
- interrupts: should contain the interrupt for the device
|
||||
- clocks: the phandle of the clock needed by the QSPI controller
|
||||
- A pinctrl must be defined to set pins in mode of operation for QSPI transfer
|
||||
|
||||
Optional properties:
|
||||
- resets: must contain the phandle to the reset controller.
|
||||
|
||||
A spi flash must be a child of the nor_flash node and could have some
|
||||
properties. Also see jedec,spi-nor.txt.
|
||||
|
||||
Required properties:
|
||||
- reg: chip-Select number (QSPI controller may connect 2 nor flashes)
|
||||
- spi-max-frequency: max frequency of spi bus
|
||||
|
||||
Optional property:
|
||||
- spi-rx-bus-width: see ../spi/spi-bus.txt for the description
|
||||
|
||||
Example:
|
||||
|
||||
qspi: spi@a0001000 {
|
||||
compatible = "st,stm32f469-qspi";
|
||||
reg = <0xa0001000 0x1000>, <0x90000000 0x10000000>;
|
||||
reg-names = "qspi", "qspi_mm";
|
||||
interrupts = <91>;
|
||||
resets = <&rcc STM32F4_AHB3_RESET(QSPI)>;
|
||||
clocks = <&rcc 0 STM32F4_AHB3_CLOCK(QSPI)>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_qspi0>;
|
||||
|
||||
flash@0 {
|
||||
reg = <0>;
|
||||
spi-rx-bus-width = <4>;
|
||||
spi-max-frequency = <108000000>;
|
||||
...
|
||||
};
|
||||
};
|
13
MAINTAINERS
13
MAINTAINERS
@ -2274,7 +2274,7 @@ M: Wenyou Yang <wenyou.yang@atmel.com>
|
||||
M: Josh Wu <rainyfeeling@outlook.com>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
S: Supported
|
||||
F: drivers/mtd/nand/atmel_nand*
|
||||
F: drivers/mtd/nand/atmel/*
|
||||
|
||||
ATMEL SDMMC DRIVER
|
||||
M: Ludovic Desroches <ludovic.desroches@microchip.com>
|
||||
@ -8376,12 +8376,12 @@ M: Brian Norris <computersforpeace@gmail.com>
|
||||
M: Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||
M: Marek Vasut <marek.vasut@gmail.com>
|
||||
M: Richard Weinberger <richard@nod.at>
|
||||
M: Cyrille Pitchen <cyrille.pitchen@atmel.com>
|
||||
M: Cyrille Pitchen <cyrille.pitchen@wedev4u.fr>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
W: http://www.linux-mtd.infradead.org/
|
||||
Q: http://patchwork.ozlabs.org/project/linux-mtd/list/
|
||||
T: git git://git.infradead.org/linux-mtd.git
|
||||
T: git git://git.infradead.org/l2-mtd.git
|
||||
T: git git://git.infradead.org/linux-mtd.git master
|
||||
T: git git://git.infradead.org/l2-mtd.git master
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/mtd/
|
||||
F: drivers/mtd/
|
||||
@ -8756,7 +8756,8 @@ R: Richard Weinberger <richard@nod.at>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
W: http://www.linux-mtd.infradead.org/
|
||||
Q: http://patchwork.ozlabs.org/project/linux-mtd/list/
|
||||
T: git git://github.com/linux-nand/linux.git
|
||||
T: git git://git.infradead.org/linux-mtd.git nand/fixes
|
||||
T: git git://git.infradead.org/l2-mtd.git nand/next
|
||||
S: Maintained
|
||||
F: drivers/mtd/nand/
|
||||
F: include/linux/mtd/nand*.h
|
||||
@ -12113,7 +12114,7 @@ S: Maintained
|
||||
F: drivers/clk/spear/
|
||||
|
||||
SPI NOR SUBSYSTEM
|
||||
M: Cyrille Pitchen <cyrille.pitchen@atmel.com>
|
||||
M: Cyrille Pitchen <cyrille.pitchen@wedev4u.fr>
|
||||
M: Marek Vasut <marek.vasut@gmail.com>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
W: http://www.linux-mtd.infradead.org/
|
||||
|
@ -136,7 +136,6 @@ config ETRAX_NANDFLASH
|
||||
bool "NAND flash support"
|
||||
depends on ETRAX_ARCH_V32
|
||||
select MTD_NAND
|
||||
select MTD_NAND_IDS
|
||||
help
|
||||
This option enables MTD mapping of NAND flash devices. Needed to use
|
||||
NAND flash memories. If unsure, say Y.
|
||||
|
@ -116,7 +116,7 @@ config FSL_CORENET_CF
|
||||
|
||||
config FSL_IFC
|
||||
bool
|
||||
depends on FSL_SOC || ARCH_LAYERSCAPE
|
||||
depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
|
||||
|
||||
config JZ4780_NEMC
|
||||
bool "Ingenic JZ4780 SoC NEMC driver"
|
||||
|
@ -323,7 +323,8 @@ static void fixup_sst38vf640x_sectorsize(struct mtd_info *mtd)
|
||||
* it should report a size of 8KBytes (0x0020*256).
|
||||
*/
|
||||
cfi->cfiq->EraseRegionInfo[0] = 0x002003ff;
|
||||
pr_warning("%s: Bad 38VF640x CFI data; adjusting sector size from 64 to 8KiB\n", mtd->name);
|
||||
pr_warn("%s: Bad 38VF640x CFI data; adjusting sector size from 64 to 8KiB\n",
|
||||
mtd->name);
|
||||
}
|
||||
|
||||
static void fixup_s29gl064n_sectors(struct mtd_info *mtd)
|
||||
@ -333,7 +334,8 @@ static void fixup_s29gl064n_sectors(struct mtd_info *mtd)
|
||||
|
||||
if ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0x003f) {
|
||||
cfi->cfiq->EraseRegionInfo[0] |= 0x0040;
|
||||
pr_warning("%s: Bad S29GL064N CFI data; adjust from 64 to 128 sectors\n", mtd->name);
|
||||
pr_warn("%s: Bad S29GL064N CFI data; adjust from 64 to 128 sectors\n",
|
||||
mtd->name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -344,7 +346,8 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd)
|
||||
|
||||
if ((cfi->cfiq->EraseRegionInfo[1] & 0xffff) == 0x007e) {
|
||||
cfi->cfiq->EraseRegionInfo[1] &= ~0x0040;
|
||||
pr_warning("%s: Bad S29GL032N CFI data; adjust from 127 to 63 sectors\n", mtd->name);
|
||||
pr_warn("%s: Bad S29GL032N CFI data; adjust from 127 to 63 sectors\n",
|
||||
mtd->name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -358,7 +361,8 @@ static void fixup_s29ns512p_sectors(struct mtd_info *mtd)
|
||||
* which is not permitted by CFI.
|
||||
*/
|
||||
cfi->cfiq->EraseRegionInfo[0] = 0x020001ff;
|
||||
pr_warning("%s: Bad S29NS512P CFI data; adjust to 512 sectors\n", mtd->name);
|
||||
pr_warn("%s: Bad S29NS512P CFI data; adjust to 512 sectors\n",
|
||||
mtd->name);
|
||||
}
|
||||
|
||||
/* Used to fix CFI-Tables of chips without Extended Query Tables */
|
||||
|
@ -17,12 +17,10 @@ obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o
|
||||
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
|
||||
obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o
|
||||
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
|
||||
ifdef CONFIG_MTD_PHYSMAP_OF_VERSATILE
|
||||
physmap_of-objs += physmap_of_versatile.o
|
||||
endif
|
||||
ifdef CONFIG_MTD_PHYSMAP_OF_GEMINI
|
||||
physmap_of-objs += physmap_of_gemini.o
|
||||
endif
|
||||
physmap_of-objs-y += physmap_of_core.o
|
||||
physmap_of-objs-$(CONFIG_MTD_PHYSMAP_OF_VERSATILE) += physmap_of_versatile.o
|
||||
physmap_of-objs-$(CONFIG_MTD_PHYSMAP_OF_GEMINI) += physmap_of_gemini.o
|
||||
physmap_of-objs := $(physmap_of-objs-y)
|
||||
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
|
||||
obj-$(CONFIG_MTD_PISMO) += pismo.o
|
||||
obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o
|
||||
|
@ -116,32 +116,22 @@ static const char * const part_probe_types_def[] = {
|
||||
|
||||
static const char * const *of_get_probes(struct device_node *dp)
|
||||
{
|
||||
const char *cp;
|
||||
int cplen;
|
||||
unsigned int l;
|
||||
unsigned int count;
|
||||
const char **res;
|
||||
int count;
|
||||
|
||||
cp = of_get_property(dp, "linux,part-probe", &cplen);
|
||||
if (cp == NULL)
|
||||
count = of_property_count_strings(dp, "linux,part-probe");
|
||||
if (count < 0)
|
||||
return part_probe_types_def;
|
||||
|
||||
count = 0;
|
||||
for (l = 0; l != cplen; l++)
|
||||
if (cp[l] == 0)
|
||||
count++;
|
||||
|
||||
res = kzalloc((count + 1)*sizeof(*res), GFP_KERNEL);
|
||||
res = kzalloc((count + 1) * sizeof(*res), GFP_KERNEL);
|
||||
if (!res)
|
||||
return NULL;
|
||||
count = 0;
|
||||
while (cplen > 0) {
|
||||
res[count] = cp;
|
||||
l = strlen(cp) + 1;
|
||||
cp += l;
|
||||
cplen -= l;
|
||||
count++;
|
||||
}
|
||||
|
||||
count = of_property_read_string_array(dp, "linux,part-probe", res,
|
||||
count);
|
||||
if (count < 0)
|
||||
return NULL;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -1235,10 +1235,8 @@ static int mtdswap_show(struct seq_file *s, void *data)
|
||||
|
||||
if (root->rb_node) {
|
||||
count[i] = d->trees[i].count;
|
||||
min[i] = rb_entry(rb_first(root), struct swap_eb,
|
||||
rb)->erase_count;
|
||||
max[i] = rb_entry(rb_last(root), struct swap_eb,
|
||||
rb)->erase_count;
|
||||
min[i] = MTDSWAP_ECNT_MIN(root);
|
||||
max[i] = MTDSWAP_ECNT_MAX(root);
|
||||
} else
|
||||
count[i] = 0;
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ config MTD_NAND_ECC_SMC
|
||||
menuconfig MTD_NAND
|
||||
tristate "NAND Device Support"
|
||||
depends on MTD
|
||||
select MTD_NAND_IDS
|
||||
select MTD_NAND_ECC
|
||||
help
|
||||
This enables support for accessing all type of NAND flash
|
||||
@ -60,17 +59,6 @@ config MTD_NAND_DENALI_DT
|
||||
Enable the driver for NAND flash on platforms using a Denali NAND
|
||||
controller as a DT device.
|
||||
|
||||
config MTD_NAND_DENALI_SCRATCH_REG_ADDR
|
||||
hex "Denali NAND size scratch register address"
|
||||
default "0xFF108018"
|
||||
depends on MTD_NAND_DENALI_PCI
|
||||
help
|
||||
Some platforms place the NAND chip size in a scratch register
|
||||
because (some versions of) the driver aren't able to automatically
|
||||
determine the size of certain chips. Set the address of the
|
||||
scratch register here to enable this feature. On Intel Moorestown
|
||||
boards, the scratch register is at 0xFF108018.
|
||||
|
||||
config MTD_NAND_GPIO
|
||||
tristate "GPIO assisted NAND Flash driver"
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
@ -109,9 +97,6 @@ config MTD_NAND_OMAP_BCH
|
||||
config MTD_NAND_OMAP_BCH_BUILD
|
||||
def_tristate MTD_NAND_OMAP2 && MTD_NAND_OMAP_BCH
|
||||
|
||||
config MTD_NAND_IDS
|
||||
tristate
|
||||
|
||||
config MTD_NAND_RICOH
|
||||
tristate "Ricoh xD card reader"
|
||||
default n
|
||||
@ -321,11 +306,11 @@ config MTD_NAND_CS553X
|
||||
If you say "m", the module will be called cs553x_nand.
|
||||
|
||||
config MTD_NAND_ATMEL
|
||||
tristate "Support for NAND Flash / SmartMedia on AT91 and AVR32"
|
||||
depends on ARCH_AT91 || AVR32
|
||||
tristate "Support for NAND Flash / SmartMedia on AT91"
|
||||
depends on ARCH_AT91
|
||||
help
|
||||
Enables support for NAND Flash / Smart Media Card interface
|
||||
on Atmel AT91 and AVR32 processors.
|
||||
on Atmel AT91 processors.
|
||||
|
||||
config MTD_NAND_PXA3xx
|
||||
tristate "NAND support on PXA3xx and Armada 370/XP"
|
||||
@ -443,7 +428,7 @@ config MTD_NAND_FSL_ELBC
|
||||
|
||||
config MTD_NAND_FSL_IFC
|
||||
tristate "NAND support for Freescale IFC controller"
|
||||
depends on FSL_SOC || ARCH_LAYERSCAPE
|
||||
depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
|
||||
select FSL_IFC
|
||||
select MEMORY
|
||||
help
|
||||
|
@ -5,7 +5,6 @@
|
||||
obj-$(CONFIG_MTD_NAND) += nand.o
|
||||
obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o
|
||||
obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o
|
||||
obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
|
||||
obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o
|
||||
|
||||
obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o
|
||||
@ -25,7 +24,7 @@ obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
|
||||
obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
|
||||
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
|
||||
obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_ATMEL) += atmel/
|
||||
obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o
|
||||
omap2_nand-objs := omap2.o
|
||||
obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o
|
||||
@ -61,4 +60,10 @@ obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
|
||||
obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
|
||||
obj-$(CONFIG_MTD_NAND_MTK) += mtk_nand.o mtk_ecc.o
|
||||
|
||||
nand-objs := nand_base.o nand_bbt.o nand_timings.o
|
||||
nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
|
||||
nand-objs += nand_amd.o
|
||||
nand-objs += nand_hynix.o
|
||||
nand-objs += nand_macronix.o
|
||||
nand-objs += nand_micron.o
|
||||
nand-objs += nand_samsung.o
|
||||
nand-objs += nand_toshiba.o
|
||||
|
4
drivers/mtd/nand/atmel/Makefile
Normal file
4
drivers/mtd/nand/atmel/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
obj-$(CONFIG_MTD_NAND_ATMEL) += atmel-nand-controller.o atmel-pmecc.o
|
||||
|
||||
atmel-nand-controller-objs := nand-controller.o
|
||||
atmel-pmecc-objs := pmecc.o
|
2197
drivers/mtd/nand/atmel/nand-controller.c
Normal file
2197
drivers/mtd/nand/atmel/nand-controller.c
Normal file
File diff suppressed because it is too large
Load Diff
1020
drivers/mtd/nand/atmel/pmecc.c
Normal file
1020
drivers/mtd/nand/atmel/pmecc.c
Normal file
File diff suppressed because it is too large
Load Diff
73
drivers/mtd/nand/atmel/pmecc.h
Normal file
73
drivers/mtd/nand/atmel/pmecc.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* © Copyright 2016 ATMEL
|
||||
* © Copyright 2016 Free Electrons
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||
*
|
||||
* Derived from the atmel_nand.c driver which contained the following
|
||||
* copyrights:
|
||||
*
|
||||
* Copyright © 2003 Rick Bronson
|
||||
*
|
||||
* Derived from drivers/mtd/nand/autcpu12.c
|
||||
* Copyright © 2001 Thomas Gleixner (gleixner@autronix.de)
|
||||
*
|
||||
* Derived from drivers/mtd/spia.c
|
||||
* Copyright © 2000 Steven J. Hill (sjhill@cotw.com)
|
||||
*
|
||||
*
|
||||
* Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
|
||||
* Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright © 2007
|
||||
*
|
||||
* Derived from Das U-Boot source code
|
||||
* (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
|
||||
* © Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
|
||||
*
|
||||
* Add Programmable Multibit ECC support for various AT91 SoC
|
||||
* © Copyright 2012 ATMEL, Hong Xu
|
||||
*
|
||||
* Add Nand Flash Controller support for SAMA5 SoC
|
||||
* © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ATMEL_PMECC_H
|
||||
#define ATMEL_PMECC_H
|
||||
|
||||
#define ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH 0
|
||||
#define ATMEL_PMECC_SECTOR_SIZE_AUTO 0
|
||||
#define ATMEL_PMECC_OOBOFFSET_AUTO -1
|
||||
|
||||
struct atmel_pmecc_user_req {
|
||||
int pagesize;
|
||||
int oobsize;
|
||||
struct {
|
||||
int strength;
|
||||
int bytes;
|
||||
int sectorsize;
|
||||
int nsectors;
|
||||
int ooboffset;
|
||||
} ecc;
|
||||
};
|
||||
|
||||
struct atmel_pmecc *devm_atmel_pmecc_get(struct device *dev);
|
||||
|
||||
struct atmel_pmecc_user *
|
||||
atmel_pmecc_create_user(struct atmel_pmecc *pmecc,
|
||||
struct atmel_pmecc_user_req *req);
|
||||
void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user);
|
||||
|
||||
int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op);
|
||||
void atmel_pmecc_disable(struct atmel_pmecc_user *user);
|
||||
int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user);
|
||||
int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
|
||||
void *data, void *ecc);
|
||||
bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user);
|
||||
void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user,
|
||||
int sector, void *ecc);
|
||||
|
||||
#endif /* ATMEL_PMECC_H */
|
File diff suppressed because it is too large
Load Diff
@ -1,163 +0,0 @@
|
||||
/*
|
||||
* Error Corrected Code Controller (ECC) - System peripherals regsters.
|
||||
* Based on AT91SAM9260 datasheet revision B.
|
||||
*
|
||||
* Copyright (C) 2007 Andrew Victor
|
||||
* Copyright (C) 2007 - 2012 Atmel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef ATMEL_NAND_ECC_H
|
||||
#define ATMEL_NAND_ECC_H
|
||||
|
||||
#define ATMEL_ECC_CR 0x00 /* Control register */
|
||||
#define ATMEL_ECC_RST (1 << 0) /* Reset parity */
|
||||
|
||||
#define ATMEL_ECC_MR 0x04 /* Mode register */
|
||||
#define ATMEL_ECC_PAGESIZE (3 << 0) /* Page Size */
|
||||
#define ATMEL_ECC_PAGESIZE_528 (0)
|
||||
#define ATMEL_ECC_PAGESIZE_1056 (1)
|
||||
#define ATMEL_ECC_PAGESIZE_2112 (2)
|
||||
#define ATMEL_ECC_PAGESIZE_4224 (3)
|
||||
|
||||
#define ATMEL_ECC_SR 0x08 /* Status register */
|
||||
#define ATMEL_ECC_RECERR (1 << 0) /* Recoverable Error */
|
||||
#define ATMEL_ECC_ECCERR (1 << 1) /* ECC Single Bit Error */
|
||||
#define ATMEL_ECC_MULERR (1 << 2) /* Multiple Errors */
|
||||
|
||||
#define ATMEL_ECC_PR 0x0c /* Parity register */
|
||||
#define ATMEL_ECC_BITADDR (0xf << 0) /* Bit Error Address */
|
||||
#define ATMEL_ECC_WORDADDR (0xfff << 4) /* Word Error Address */
|
||||
|
||||
#define ATMEL_ECC_NPR 0x10 /* NParity register */
|
||||
#define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */
|
||||
|
||||
/* PMECC Register Definitions */
|
||||
#define ATMEL_PMECC_CFG 0x000 /* Configuration Register */
|
||||
#define PMECC_CFG_BCH_ERR2 (0 << 0)
|
||||
#define PMECC_CFG_BCH_ERR4 (1 << 0)
|
||||
#define PMECC_CFG_BCH_ERR8 (2 << 0)
|
||||
#define PMECC_CFG_BCH_ERR12 (3 << 0)
|
||||
#define PMECC_CFG_BCH_ERR24 (4 << 0)
|
||||
#define PMECC_CFG_BCH_ERR32 (5 << 0)
|
||||
|
||||
#define PMECC_CFG_SECTOR512 (0 << 4)
|
||||
#define PMECC_CFG_SECTOR1024 (1 << 4)
|
||||
|
||||
#define PMECC_CFG_PAGE_1SECTOR (0 << 8)
|
||||
#define PMECC_CFG_PAGE_2SECTORS (1 << 8)
|
||||
#define PMECC_CFG_PAGE_4SECTORS (2 << 8)
|
||||
#define PMECC_CFG_PAGE_8SECTORS (3 << 8)
|
||||
|
||||
#define PMECC_CFG_READ_OP (0 << 12)
|
||||
#define PMECC_CFG_WRITE_OP (1 << 12)
|
||||
|
||||
#define PMECC_CFG_SPARE_ENABLE (1 << 16)
|
||||
#define PMECC_CFG_SPARE_DISABLE (0 << 16)
|
||||
|
||||
#define PMECC_CFG_AUTO_ENABLE (1 << 20)
|
||||
#define PMECC_CFG_AUTO_DISABLE (0 << 20)
|
||||
|
||||
#define ATMEL_PMECC_SAREA 0x004 /* Spare area size */
|
||||
#define ATMEL_PMECC_SADDR 0x008 /* PMECC starting address */
|
||||
#define ATMEL_PMECC_EADDR 0x00c /* PMECC ending address */
|
||||
#define ATMEL_PMECC_CLK 0x010 /* PMECC clock control */
|
||||
#define PMECC_CLK_133MHZ (2 << 0)
|
||||
|
||||
#define ATMEL_PMECC_CTRL 0x014 /* PMECC control register */
|
||||
#define PMECC_CTRL_RST (1 << 0)
|
||||
#define PMECC_CTRL_DATA (1 << 1)
|
||||
#define PMECC_CTRL_USER (1 << 2)
|
||||
#define PMECC_CTRL_ENABLE (1 << 4)
|
||||
#define PMECC_CTRL_DISABLE (1 << 5)
|
||||
|
||||
#define ATMEL_PMECC_SR 0x018 /* PMECC status register */
|
||||
#define PMECC_SR_BUSY (1 << 0)
|
||||
#define PMECC_SR_ENABLE (1 << 4)
|
||||
|
||||
#define ATMEL_PMECC_IER 0x01c /* PMECC interrupt enable */
|
||||
#define PMECC_IER_ENABLE (1 << 0)
|
||||
#define ATMEL_PMECC_IDR 0x020 /* PMECC interrupt disable */
|
||||
#define PMECC_IER_DISABLE (1 << 0)
|
||||
#define ATMEL_PMECC_IMR 0x024 /* PMECC interrupt mask */
|
||||
#define PMECC_IER_MASK (1 << 0)
|
||||
#define ATMEL_PMECC_ISR 0x028 /* PMECC interrupt status */
|
||||
#define ATMEL_PMECC_ECCx 0x040 /* PMECC ECC x */
|
||||
#define ATMEL_PMECC_REMx 0x240 /* PMECC REM x */
|
||||
|
||||
/* PMERRLOC Register Definitions */
|
||||
#define ATMEL_PMERRLOC_ELCFG 0x000 /* Error location config */
|
||||
#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0)
|
||||
#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0)
|
||||
#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16)
|
||||
|
||||
#define ATMEL_PMERRLOC_ELPRIM 0x004 /* Error location primitive */
|
||||
#define ATMEL_PMERRLOC_ELEN 0x008 /* Error location enable */
|
||||
#define ATMEL_PMERRLOC_ELDIS 0x00c /* Error location disable */
|
||||
#define PMERRLOC_DISABLE (1 << 0)
|
||||
|
||||
#define ATMEL_PMERRLOC_ELSR 0x010 /* Error location status */
|
||||
#define PMERRLOC_ELSR_BUSY (1 << 0)
|
||||
#define ATMEL_PMERRLOC_ELIER 0x014 /* Error location int enable */
|
||||
#define ATMEL_PMERRLOC_ELIDR 0x018 /* Error location int disable */
|
||||
#define ATMEL_PMERRLOC_ELIMR 0x01c /* Error location int mask */
|
||||
#define ATMEL_PMERRLOC_ELISR 0x020 /* Error location int status */
|
||||
#define PMERRLOC_ERR_NUM_MASK (0x1f << 8)
|
||||
#define PMERRLOC_CALC_DONE (1 << 0)
|
||||
#define ATMEL_PMERRLOC_SIGMAx 0x028 /* Error location SIGMA x */
|
||||
|
||||
/*
|
||||
* The ATMEL_PMERRLOC_ELx register location depends from the number of
|
||||
* bits corrected by the PMECC controller. Do not use it.
|
||||
*/
|
||||
|
||||
/* Register access macros for PMECC */
|
||||
#define pmecc_readl_relaxed(addr, reg) \
|
||||
readl_relaxed((addr) + ATMEL_PMECC_##reg)
|
||||
|
||||
#define pmecc_writel(addr, reg, value) \
|
||||
writel((value), (addr) + ATMEL_PMECC_##reg)
|
||||
|
||||
#define pmecc_readb_ecc_relaxed(addr, sector, n) \
|
||||
readb_relaxed((addr) + ATMEL_PMECC_ECCx + ((sector) * 0x40) + (n))
|
||||
|
||||
#define pmecc_readl_rem_relaxed(addr, sector, n) \
|
||||
readl_relaxed((addr) + ATMEL_PMECC_REMx + ((sector) * 0x40) + ((n) * 4))
|
||||
|
||||
#define pmerrloc_readl_relaxed(addr, reg) \
|
||||
readl_relaxed((addr) + ATMEL_PMERRLOC_##reg)
|
||||
|
||||
#define pmerrloc_writel(addr, reg, value) \
|
||||
writel((value), (addr) + ATMEL_PMERRLOC_##reg)
|
||||
|
||||
#define pmerrloc_writel_sigma_relaxed(addr, n, value) \
|
||||
writel_relaxed((value), (addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4))
|
||||
|
||||
#define pmerrloc_readl_sigma_relaxed(addr, n) \
|
||||
readl_relaxed((addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4))
|
||||
|
||||
#define pmerrloc_readl_el_relaxed(addr, n) \
|
||||
readl_relaxed((addr) + ((n) * 4))
|
||||
|
||||
/* Galois field dimension */
|
||||
#define PMECC_GF_DIMENSION_13 13
|
||||
#define PMECC_GF_DIMENSION_14 14
|
||||
|
||||
/* Primitive Polynomial used by PMECC */
|
||||
#define PMECC_GF_13_PRIMITIVE_POLY 0x201b
|
||||
#define PMECC_GF_14_PRIMITIVE_POLY 0x4443
|
||||
|
||||
#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000
|
||||
#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000
|
||||
|
||||
/* Time out value for reading PMECC status register */
|
||||
#define PMECC_MAX_TIMEOUT_MS 100
|
||||
|
||||
/* Reserved bytes in oob area */
|
||||
#define PMECC_OOB_RESERVED_BYTES 2
|
||||
|
||||
#endif
|
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Atmel Nand Flash Controller (NFC) - System peripherals regsters.
|
||||
* Based on SAMA5D3 datasheet.
|
||||
*
|
||||
* © Copyright 2013 Atmel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef ATMEL_NAND_NFC_H
|
||||
#define ATMEL_NAND_NFC_H
|
||||
|
||||
/*
|
||||
* HSMC NFC registers
|
||||
*/
|
||||
#define ATMEL_HSMC_NFC_CFG 0x00 /* NFC Configuration Register */
|
||||
#define NFC_CFG_PAGESIZE (7 << 0)
|
||||
#define NFC_CFG_PAGESIZE_512 (0 << 0)
|
||||
#define NFC_CFG_PAGESIZE_1024 (1 << 0)
|
||||
#define NFC_CFG_PAGESIZE_2048 (2 << 0)
|
||||
#define NFC_CFG_PAGESIZE_4096 (3 << 0)
|
||||
#define NFC_CFG_PAGESIZE_8192 (4 << 0)
|
||||
#define NFC_CFG_WSPARE (1 << 8)
|
||||
#define NFC_CFG_RSPARE (1 << 9)
|
||||
#define NFC_CFG_NFC_DTOCYC (0xf << 16)
|
||||
#define NFC_CFG_NFC_DTOMUL (0x7 << 20)
|
||||
#define NFC_CFG_NFC_SPARESIZE (0x7f << 24)
|
||||
#define NFC_CFG_NFC_SPARESIZE_BIT_POS 24
|
||||
|
||||
#define ATMEL_HSMC_NFC_CTRL 0x04 /* NFC Control Register */
|
||||
#define NFC_CTRL_ENABLE (1 << 0)
|
||||
#define NFC_CTRL_DISABLE (1 << 1)
|
||||
|
||||
#define ATMEL_HSMC_NFC_SR 0x08 /* NFC Status Register */
|
||||
#define NFC_SR_BUSY (1 << 8)
|
||||
#define NFC_SR_XFR_DONE (1 << 16)
|
||||
#define NFC_SR_CMD_DONE (1 << 17)
|
||||
#define NFC_SR_DTOE (1 << 20)
|
||||
#define NFC_SR_UNDEF (1 << 21)
|
||||
#define NFC_SR_AWB (1 << 22)
|
||||
#define NFC_SR_ASE (1 << 23)
|
||||
#define NFC_SR_RB_EDGE (1 << 24)
|
||||
|
||||
#define ATMEL_HSMC_NFC_IER 0x0c
|
||||
#define ATMEL_HSMC_NFC_IDR 0x10
|
||||
#define ATMEL_HSMC_NFC_IMR 0x14
|
||||
#define ATMEL_HSMC_NFC_CYCLE0 0x18 /* NFC Address Cycle Zero */
|
||||
#define ATMEL_HSMC_NFC_ADDR_CYCLE0 (0xff)
|
||||
|
||||
#define ATMEL_HSMC_NFC_BANK 0x1c /* NFC Bank Register */
|
||||
#define ATMEL_HSMC_NFC_BANK0 (0 << 0)
|
||||
#define ATMEL_HSMC_NFC_BANK1 (1 << 0)
|
||||
|
||||
#define nfc_writel(addr, reg, value) \
|
||||
writel((value), (addr) + ATMEL_HSMC_NFC_##reg)
|
||||
|
||||
#define nfc_readl(addr, reg) \
|
||||
readl_relaxed((addr) + ATMEL_HSMC_NFC_##reg)
|
||||
|
||||
/*
|
||||
* NFC Address Command definitions
|
||||
*/
|
||||
#define NFCADDR_CMD_CMD1 (0xff << 2) /* Command for Cycle 1 */
|
||||
#define NFCADDR_CMD_CMD1_BIT_POS 2
|
||||
#define NFCADDR_CMD_CMD2 (0xff << 10) /* Command for Cycle 2 */
|
||||
#define NFCADDR_CMD_CMD2_BIT_POS 10
|
||||
#define NFCADDR_CMD_VCMD2 (0x1 << 18) /* Valid Cycle 2 Command */
|
||||
#define NFCADDR_CMD_ACYCLE (0x7 << 19) /* Number of Address required */
|
||||
#define NFCADDR_CMD_ACYCLE_NONE (0x0 << 19)
|
||||
#define NFCADDR_CMD_ACYCLE_1 (0x1 << 19)
|
||||
#define NFCADDR_CMD_ACYCLE_2 (0x2 << 19)
|
||||
#define NFCADDR_CMD_ACYCLE_3 (0x3 << 19)
|
||||
#define NFCADDR_CMD_ACYCLE_4 (0x4 << 19)
|
||||
#define NFCADDR_CMD_ACYCLE_5 (0x5 << 19)
|
||||
#define NFCADDR_CMD_ACYCLE_BIT_POS 19
|
||||
#define NFCADDR_CMD_CSID (0x7 << 22) /* Chip Select Identifier */
|
||||
#define NFCADDR_CMD_CSID_0 (0x0 << 22)
|
||||
#define NFCADDR_CMD_CSID_1 (0x1 << 22)
|
||||
#define NFCADDR_CMD_CSID_2 (0x2 << 22)
|
||||
#define NFCADDR_CMD_CSID_3 (0x3 << 22)
|
||||
#define NFCADDR_CMD_CSID_4 (0x4 << 22)
|
||||
#define NFCADDR_CMD_CSID_5 (0x5 << 22)
|
||||
#define NFCADDR_CMD_CSID_6 (0x6 << 22)
|
||||
#define NFCADDR_CMD_CSID_7 (0x7 << 22)
|
||||
#define NFCADDR_CMD_DATAEN (0x1 << 25) /* Data Transfer Enable */
|
||||
#define NFCADDR_CMD_DATADIS (0x0 << 25) /* Data Transfer Disable */
|
||||
#define NFCADDR_CMD_NFCRD (0x0 << 26) /* NFC Read Enable */
|
||||
#define NFCADDR_CMD_NFCWR (0x1 << 26) /* NFC Write Enable */
|
||||
#define NFCADDR_CMD_NFCBUSY (0x1 << 27) /* NFC Busy */
|
||||
|
||||
#define nfc_cmd_addr1234_writel(cmd, addr1234, nfc_base) \
|
||||
writel((addr1234), (cmd) + nfc_base)
|
||||
|
||||
#define nfc_cmd_readl(bitstatus, nfc_base) \
|
||||
readl_relaxed((bitstatus) + nfc_base)
|
||||
|
||||
#define NFC_TIME_OUT_MS 100
|
||||
#define NFC_SRAM_BANK1_OFFSET 0x1200
|
||||
|
||||
#endif
|
@ -101,6 +101,9 @@ struct brcm_nand_dma_desc {
|
||||
#define BRCMNAND_MIN_BLOCKSIZE (8 * 1024)
|
||||
#define BRCMNAND_MIN_DEVSIZE (4ULL * 1024 * 1024)
|
||||
|
||||
#define NAND_CTRL_RDY (INTFC_CTLR_READY | INTFC_FLASH_READY)
|
||||
#define NAND_POLL_STATUS_TIMEOUT_MS 100
|
||||
|
||||
/* Controller feature flags */
|
||||
enum {
|
||||
BRCMNAND_HAS_1K_SECTORS = BIT(0),
|
||||
@ -765,6 +768,31 @@ enum {
|
||||
CS_SELECT_AUTO_DEVICE_ID_CFG = BIT(30),
|
||||
};
|
||||
|
||||
static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl,
|
||||
u32 mask, u32 expected_val,
|
||||
unsigned long timeout_ms)
|
||||
{
|
||||
unsigned long limit;
|
||||
u32 val;
|
||||
|
||||
if (!timeout_ms)
|
||||
timeout_ms = NAND_POLL_STATUS_TIMEOUT_MS;
|
||||
|
||||
limit = jiffies + msecs_to_jiffies(timeout_ms);
|
||||
do {
|
||||
val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
|
||||
if ((val & mask) == expected_val)
|
||||
return 0;
|
||||
|
||||
cpu_relax();
|
||||
} while (time_after(limit, jiffies));
|
||||
|
||||
dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n",
|
||||
expected_val, val & mask);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en)
|
||||
{
|
||||
u32 val = en ? CS_SELECT_NAND_WP : 0;
|
||||
@ -1024,12 +1052,39 @@ static void brcmnand_wp(struct mtd_info *mtd, int wp)
|
||||
|
||||
if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) {
|
||||
static int old_wp = -1;
|
||||
int ret;
|
||||
|
||||
if (old_wp != wp) {
|
||||
dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off");
|
||||
old_wp = wp;
|
||||
}
|
||||
|
||||
/*
|
||||
* make sure ctrl/flash ready before and after
|
||||
* changing state of #WP pin
|
||||
*/
|
||||
ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY |
|
||||
NAND_STATUS_READY,
|
||||
NAND_CTRL_RDY |
|
||||
NAND_STATUS_READY, 0);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
brcmnand_set_wp(ctrl, wp);
|
||||
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
|
||||
/* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */
|
||||
ret = bcmnand_ctrl_poll_status(ctrl,
|
||||
NAND_CTRL_RDY |
|
||||
NAND_STATUS_READY |
|
||||
NAND_STATUS_WP,
|
||||
NAND_CTRL_RDY |
|
||||
NAND_STATUS_READY |
|
||||
(wp ? 0 : NAND_STATUS_WP), 0);
|
||||
|
||||
if (ret)
|
||||
dev_err_ratelimited(&host->pdev->dev,
|
||||
"nand #WP expected %s\n",
|
||||
wp ? "on" : "off");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1157,15 +1212,15 @@ static irqreturn_t brcmnand_dma_irq(int irq, void *data)
|
||||
static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd)
|
||||
{
|
||||
struct brcmnand_controller *ctrl = host->ctrl;
|
||||
u32 intfc;
|
||||
int ret;
|
||||
|
||||
dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd,
|
||||
brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS));
|
||||
BUG_ON(ctrl->cmd_pending != 0);
|
||||
ctrl->cmd_pending = cmd;
|
||||
|
||||
intfc = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
|
||||
WARN_ON(!(intfc & INTFC_CTLR_READY));
|
||||
ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0);
|
||||
WARN_ON(ret);
|
||||
|
||||
mb(); /* flush previous writes */
|
||||
brcmnand_write_reg(ctrl, BRCMNAND_CMD_START,
|
||||
|
@ -145,7 +145,7 @@ static int __init cmx270_init(void)
|
||||
|
||||
ret = gpio_request(GPIO_NAND_CS, "NAND CS");
|
||||
if (ret) {
|
||||
pr_warning("CM-X270: failed to request NAND CS gpio\n");
|
||||
pr_warn("CM-X270: failed to request NAND CS gpio\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -153,7 +153,7 @@ static int __init cmx270_init(void)
|
||||
|
||||
ret = gpio_request(GPIO_NAND_RB, "NAND R/B");
|
||||
if (ret) {
|
||||
pr_warning("CM-X270: failed to request NAND R/B gpio\n");
|
||||
pr_warn("CM-X270: failed to request NAND R/B gpio\n");
|
||||
goto err_gpio_request;
|
||||
}
|
||||
|
||||
|
@ -581,6 +581,17 @@ static struct davinci_nand_pdata
|
||||
"ti,davinci-nand-use-bbt"))
|
||||
pdata->bbt_options = NAND_BBT_USE_FLASH;
|
||||
|
||||
/*
|
||||
* Since kernel v4.8, this driver has been fixed to enable
|
||||
* use of 4-bit hardware ECC with subpages and verified on
|
||||
* TI's keystone EVMs (K2L, K2HK and K2E).
|
||||
* However, in the interest of not breaking systems using
|
||||
* existing UBI partitions, sub-page writes are not being
|
||||
* (re)enabled. If you want to use subpage writes on Keystone
|
||||
* platforms (i.e. do not have any existing UBI partitions),
|
||||
* then use "ti,davinci-nand" as the compatible in your
|
||||
* device-tree file.
|
||||
*/
|
||||
if (of_device_is_compatible(pdev->dev.of_node,
|
||||
"ti,keystone-nand")) {
|
||||
pdata->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
|
@ -45,16 +45,16 @@ MODULE_PARM_DESC(onfi_timing_mode,
|
||||
* We define a macro here that combines all interrupts this driver uses into
|
||||
* a single constant value, for convenience.
|
||||
*/
|
||||
#define DENALI_IRQ_ALL (INTR_STATUS__DMA_CMD_COMP | \
|
||||
INTR_STATUS__ECC_TRANSACTION_DONE | \
|
||||
INTR_STATUS__ECC_ERR | \
|
||||
INTR_STATUS__PROGRAM_FAIL | \
|
||||
INTR_STATUS__LOAD_COMP | \
|
||||
INTR_STATUS__PROGRAM_COMP | \
|
||||
INTR_STATUS__TIME_OUT | \
|
||||
INTR_STATUS__ERASE_FAIL | \
|
||||
INTR_STATUS__RST_COMP | \
|
||||
INTR_STATUS__ERASE_COMP)
|
||||
#define DENALI_IRQ_ALL (INTR__DMA_CMD_COMP | \
|
||||
INTR__ECC_TRANSACTION_DONE | \
|
||||
INTR__ECC_ERR | \
|
||||
INTR__PROGRAM_FAIL | \
|
||||
INTR__LOAD_COMP | \
|
||||
INTR__PROGRAM_COMP | \
|
||||
INTR__TIME_OUT | \
|
||||
INTR__ERASE_FAIL | \
|
||||
INTR__RST_COMP | \
|
||||
INTR__ERASE_COMP)
|
||||
|
||||
/*
|
||||
* indicates whether or not the internal value for the flash bank is
|
||||
@ -62,8 +62,6 @@ MODULE_PARM_DESC(onfi_timing_mode,
|
||||
*/
|
||||
#define CHIP_SELECT_INVALID -1
|
||||
|
||||
#define SUPPORT_8BITECC 1
|
||||
|
||||
/*
|
||||
* This macro divides two integers and rounds fractional values up
|
||||
* to the nearest integer value.
|
||||
@ -86,16 +84,10 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
|
||||
#define SPARE_ACCESS 0x41
|
||||
#define MAIN_ACCESS 0x42
|
||||
#define MAIN_SPARE_ACCESS 0x43
|
||||
#define PIPELINE_ACCESS 0x2000
|
||||
|
||||
#define DENALI_READ 0
|
||||
#define DENALI_WRITE 0x100
|
||||
|
||||
/* types of device accesses. We can issue commands and get status */
|
||||
#define COMMAND_CYCLE 0
|
||||
#define ADDR_CYCLE 1
|
||||
#define STATUS_CYCLE 2
|
||||
|
||||
/*
|
||||
* this is a helper macro that allows us to
|
||||
* format the bank into the proper bits for the controller
|
||||
@ -164,7 +156,7 @@ static void read_status(struct denali_nand_info *denali)
|
||||
static void reset_bank(struct denali_nand_info *denali)
|
||||
{
|
||||
uint32_t irq_status;
|
||||
uint32_t irq_mask = INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT;
|
||||
uint32_t irq_mask = INTR__RST_COMP | INTR__TIME_OUT;
|
||||
|
||||
clear_interrupts(denali);
|
||||
|
||||
@ -172,7 +164,7 @@ static void reset_bank(struct denali_nand_info *denali)
|
||||
|
||||
irq_status = wait_for_irq(denali, irq_mask);
|
||||
|
||||
if (irq_status & INTR_STATUS__TIME_OUT)
|
||||
if (irq_status & INTR__TIME_OUT)
|
||||
dev_err(denali->dev, "reset bank failed.\n");
|
||||
}
|
||||
|
||||
@ -182,22 +174,22 @@ static uint16_t denali_nand_reset(struct denali_nand_info *denali)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < denali->max_banks; i++)
|
||||
iowrite32(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT,
|
||||
iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
|
||||
denali->flash_reg + INTR_STATUS(i));
|
||||
|
||||
for (i = 0; i < denali->max_banks; i++) {
|
||||
iowrite32(1 << i, denali->flash_reg + DEVICE_RESET);
|
||||
while (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
|
||||
(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT)))
|
||||
(INTR__RST_COMP | INTR__TIME_OUT)))
|
||||
cpu_relax();
|
||||
if (ioread32(denali->flash_reg + INTR_STATUS(i)) &
|
||||
INTR_STATUS__TIME_OUT)
|
||||
INTR__TIME_OUT)
|
||||
dev_dbg(denali->dev,
|
||||
"NAND Reset operation timed out on bank %d\n", i);
|
||||
}
|
||||
|
||||
for (i = 0; i < denali->max_banks; i++)
|
||||
iowrite32(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT,
|
||||
iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
|
||||
denali->flash_reg + INTR_STATUS(i));
|
||||
|
||||
return PASS;
|
||||
@ -347,52 +339,25 @@ static void get_samsung_nand_para(struct denali_nand_info *denali,
|
||||
|
||||
static void get_toshiba_nand_para(struct denali_nand_info *denali)
|
||||
{
|
||||
uint32_t tmp;
|
||||
|
||||
/*
|
||||
* Workaround to fix a controller bug which reports a wrong
|
||||
* spare area size for some kind of Toshiba NAND device
|
||||
*/
|
||||
if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) == 4096) &&
|
||||
(ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) == 64)) {
|
||||
(ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) == 64))
|
||||
iowrite32(216, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
|
||||
tmp = ioread32(denali->flash_reg + DEVICES_CONNECTED) *
|
||||
ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
|
||||
iowrite32(tmp,
|
||||
denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE);
|
||||
#if SUPPORT_15BITECC
|
||||
iowrite32(15, denali->flash_reg + ECC_CORRECTION);
|
||||
#elif SUPPORT_8BITECC
|
||||
iowrite32(8, denali->flash_reg + ECC_CORRECTION);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void get_hynix_nand_para(struct denali_nand_info *denali,
|
||||
uint8_t device_id)
|
||||
{
|
||||
uint32_t main_size, spare_size;
|
||||
|
||||
switch (device_id) {
|
||||
case 0xD5: /* Hynix H27UAG8T2A, H27UBG8U5A or H27UCG8VFA */
|
||||
case 0xD7: /* Hynix H27UDG8VEM, H27UCG8UDM or H27UCG8V5A */
|
||||
iowrite32(128, denali->flash_reg + PAGES_PER_BLOCK);
|
||||
iowrite32(4096, denali->flash_reg + DEVICE_MAIN_AREA_SIZE);
|
||||
iowrite32(224, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
|
||||
main_size = 4096 *
|
||||
ioread32(denali->flash_reg + DEVICES_CONNECTED);
|
||||
spare_size = 224 *
|
||||
ioread32(denali->flash_reg + DEVICES_CONNECTED);
|
||||
iowrite32(main_size,
|
||||
denali->flash_reg + LOGICAL_PAGE_DATA_SIZE);
|
||||
iowrite32(spare_size,
|
||||
denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE);
|
||||
iowrite32(0, denali->flash_reg + DEVICE_WIDTH);
|
||||
#if SUPPORT_15BITECC
|
||||
iowrite32(15, denali->flash_reg + ECC_CORRECTION);
|
||||
#elif SUPPORT_8BITECC
|
||||
iowrite32(8, denali->flash_reg + ECC_CORRECTION);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
dev_warn(denali->dev,
|
||||
@ -454,17 +419,12 @@ static void find_valid_banks(struct denali_nand_info *denali)
|
||||
static void detect_max_banks(struct denali_nand_info *denali)
|
||||
{
|
||||
uint32_t features = ioread32(denali->flash_reg + FEATURES);
|
||||
/*
|
||||
* Read the revision register, so we can calculate the max_banks
|
||||
* properly: the encoding changed from rev 5.0 to 5.1
|
||||
*/
|
||||
u32 revision = MAKE_COMPARABLE_REVISION(
|
||||
ioread32(denali->flash_reg + REVISION));
|
||||
|
||||
if (revision < REVISION_5_1)
|
||||
denali->max_banks = 2 << (features & FEATURES__N_BANKS);
|
||||
else
|
||||
denali->max_banks = 1 << (features & FEATURES__N_BANKS);
|
||||
denali->max_banks = 1 << (features & FEATURES__N_BANKS);
|
||||
|
||||
/* the encoding changed from rev 5.0 to 5.1 */
|
||||
if (denali->revision < 0x0501)
|
||||
denali->max_banks <<= 1;
|
||||
}
|
||||
|
||||
static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
|
||||
@ -653,7 +613,6 @@ static irqreturn_t denali_isr(int irq, void *dev_id)
|
||||
spin_unlock(&denali->irq_lock);
|
||||
return result;
|
||||
}
|
||||
#define BANK(x) ((x) << 24)
|
||||
|
||||
static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask)
|
||||
{
|
||||
@ -718,15 +677,7 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
|
||||
int access_type, int op)
|
||||
{
|
||||
int status = PASS;
|
||||
uint32_t page_count = 1;
|
||||
uint32_t addr, cmd, irq_status, irq_mask;
|
||||
|
||||
if (op == DENALI_READ)
|
||||
irq_mask = INTR_STATUS__LOAD_COMP;
|
||||
else if (op == DENALI_WRITE)
|
||||
irq_mask = 0;
|
||||
else
|
||||
BUG();
|
||||
uint32_t addr, cmd;
|
||||
|
||||
setup_ecc_for_xfer(denali, ecc_en, transfer_spare);
|
||||
|
||||
@ -749,35 +700,8 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
|
||||
cmd = MODE_10 | addr;
|
||||
index_addr(denali, cmd, access_type);
|
||||
|
||||
/*
|
||||
* page 33 of the NAND controller spec indicates we should not
|
||||
* use the pipeline commands in Spare area only mode.
|
||||
* So we don't.
|
||||
*/
|
||||
if (access_type == SPARE_ACCESS) {
|
||||
cmd = MODE_01 | addr;
|
||||
iowrite32(cmd, denali->flash_mem);
|
||||
} else {
|
||||
index_addr(denali, cmd,
|
||||
PIPELINE_ACCESS | op | page_count);
|
||||
|
||||
/*
|
||||
* wait for command to be accepted
|
||||
* can always use status0 bit as the
|
||||
* mask is identical for each bank.
|
||||
*/
|
||||
irq_status = wait_for_irq(denali, irq_mask);
|
||||
|
||||
if (irq_status == 0) {
|
||||
dev_err(denali->dev,
|
||||
"cmd, page, addr on timeout (0x%x, 0x%x, 0x%x)\n",
|
||||
cmd, denali->page, addr);
|
||||
status = FAIL;
|
||||
} else {
|
||||
cmd = MODE_01 | addr;
|
||||
iowrite32(cmd, denali->flash_mem);
|
||||
}
|
||||
}
|
||||
cmd = MODE_01 | addr;
|
||||
iowrite32(cmd, denali->flash_mem);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@ -829,8 +753,7 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
|
||||
{
|
||||
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
||||
uint32_t irq_status;
|
||||
uint32_t irq_mask = INTR_STATUS__PROGRAM_COMP |
|
||||
INTR_STATUS__PROGRAM_FAIL;
|
||||
uint32_t irq_mask = INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL;
|
||||
int status = 0;
|
||||
|
||||
denali->page = page;
|
||||
@ -857,7 +780,7 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
|
||||
static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
|
||||
{
|
||||
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
||||
uint32_t irq_mask = INTR_STATUS__LOAD_COMP;
|
||||
uint32_t irq_mask = INTR__LOAD_COMP;
|
||||
uint32_t irq_status, addr, cmd;
|
||||
|
||||
denali->page = page;
|
||||
@ -890,98 +813,158 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* this function examines buffers to see if they contain data that
|
||||
* indicate that the buffer is part of an erased region of flash.
|
||||
*/
|
||||
static bool is_erased(uint8_t *buf, int len)
|
||||
static int denali_check_erased_page(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t *buf,
|
||||
unsigned long uncor_ecc_flags,
|
||||
unsigned int max_bitflips)
|
||||
{
|
||||
int i;
|
||||
uint8_t *ecc_code = chip->buffers->ecccode;
|
||||
int ecc_steps = chip->ecc.steps;
|
||||
int ecc_size = chip->ecc.size;
|
||||
int ecc_bytes = chip->ecc.bytes;
|
||||
int i, ret, stat;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
if (buf[i] != 0xFF)
|
||||
return false;
|
||||
return true;
|
||||
ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
|
||||
chip->ecc.total);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < ecc_steps; i++) {
|
||||
if (!(uncor_ecc_flags & BIT(i)))
|
||||
continue;
|
||||
|
||||
stat = nand_check_erased_ecc_chunk(buf, ecc_size,
|
||||
ecc_code, ecc_bytes,
|
||||
NULL, 0,
|
||||
chip->ecc.strength);
|
||||
if (stat < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
}
|
||||
|
||||
buf += ecc_size;
|
||||
ecc_code += ecc_bytes;
|
||||
}
|
||||
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
static int denali_hw_ecc_fixup(struct mtd_info *mtd,
|
||||
struct denali_nand_info *denali,
|
||||
unsigned long *uncor_ecc_flags)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
int bank = denali->flash_bank;
|
||||
uint32_t ecc_cor;
|
||||
unsigned int max_bitflips;
|
||||
|
||||
ecc_cor = ioread32(denali->flash_reg + ECC_COR_INFO(bank));
|
||||
ecc_cor >>= ECC_COR_INFO__SHIFT(bank);
|
||||
|
||||
if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) {
|
||||
/*
|
||||
* This flag is set when uncorrectable error occurs at least in
|
||||
* one ECC sector. We can not know "how many sectors", or
|
||||
* "which sector(s)". We need erase-page check for all sectors.
|
||||
*/
|
||||
*uncor_ecc_flags = GENMASK(chip->ecc.steps - 1, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
max_bitflips = ecc_cor & ECC_COR_INFO__MAX_ERRORS;
|
||||
|
||||
/*
|
||||
* The register holds the maximum of per-sector corrected bitflips.
|
||||
* This is suitable for the return value of the ->read_page() callback.
|
||||
* Unfortunately, we can not know the total number of corrected bits in
|
||||
* the page. Increase the stats by max_bitflips. (compromised solution)
|
||||
*/
|
||||
mtd->ecc_stats.corrected += max_bitflips;
|
||||
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
#define ECC_SECTOR_SIZE 512
|
||||
|
||||
#define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
|
||||
#define ECC_BYTE(x) (((x) & ECC_ERROR_ADDRESS__OFFSET))
|
||||
#define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK)
|
||||
#define ECC_ERROR_CORRECTABLE(x) (!((x) & ERR_CORRECTION_INFO__ERROR_TYPE))
|
||||
#define ECC_ERROR_UNCORRECTABLE(x) ((x) & ERR_CORRECTION_INFO__ERROR_TYPE)
|
||||
#define ECC_ERR_DEVICE(x) (((x) & ERR_CORRECTION_INFO__DEVICE_NR) >> 8)
|
||||
#define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO)
|
||||
|
||||
static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
|
||||
uint32_t irq_status, unsigned int *max_bitflips)
|
||||
static int denali_sw_ecc_fixup(struct mtd_info *mtd,
|
||||
struct denali_nand_info *denali,
|
||||
unsigned long *uncor_ecc_flags, uint8_t *buf)
|
||||
{
|
||||
bool check_erased_page = false;
|
||||
unsigned int bitflips = 0;
|
||||
unsigned int max_bitflips = 0;
|
||||
uint32_t err_addr, err_cor_info;
|
||||
unsigned int err_byte, err_sector, err_device;
|
||||
uint8_t err_cor_value;
|
||||
unsigned int prev_sector = 0;
|
||||
|
||||
if (irq_status & INTR_STATUS__ECC_ERR) {
|
||||
/* read the ECC errors. we'll ignore them for now */
|
||||
uint32_t err_address, err_correction_info, err_byte,
|
||||
err_sector, err_device, err_correction_value;
|
||||
denali_set_intr_modes(denali, false);
|
||||
/* read the ECC errors. we'll ignore them for now */
|
||||
denali_set_intr_modes(denali, false);
|
||||
|
||||
do {
|
||||
err_address = ioread32(denali->flash_reg +
|
||||
ECC_ERROR_ADDRESS);
|
||||
err_sector = ECC_SECTOR(err_address);
|
||||
err_byte = ECC_BYTE(err_address);
|
||||
do {
|
||||
err_addr = ioread32(denali->flash_reg + ECC_ERROR_ADDRESS);
|
||||
err_sector = ECC_SECTOR(err_addr);
|
||||
err_byte = ECC_BYTE(err_addr);
|
||||
|
||||
err_correction_info = ioread32(denali->flash_reg +
|
||||
ERR_CORRECTION_INFO);
|
||||
err_correction_value =
|
||||
ECC_CORRECTION_VALUE(err_correction_info);
|
||||
err_device = ECC_ERR_DEVICE(err_correction_info);
|
||||
err_cor_info = ioread32(denali->flash_reg + ERR_CORRECTION_INFO);
|
||||
err_cor_value = ECC_CORRECTION_VALUE(err_cor_info);
|
||||
err_device = ECC_ERR_DEVICE(err_cor_info);
|
||||
|
||||
if (ECC_ERROR_CORRECTABLE(err_correction_info)) {
|
||||
/*
|
||||
* If err_byte is larger than ECC_SECTOR_SIZE,
|
||||
* means error happened in OOB, so we ignore
|
||||
* it. It's no need for us to correct it
|
||||
* err_device is represented the NAND error
|
||||
* bits are happened in if there are more
|
||||
* than one NAND connected.
|
||||
*/
|
||||
if (err_byte < ECC_SECTOR_SIZE) {
|
||||
struct mtd_info *mtd =
|
||||
nand_to_mtd(&denali->nand);
|
||||
int offset;
|
||||
/* reset the bitflip counter when crossing ECC sector */
|
||||
if (err_sector != prev_sector)
|
||||
bitflips = 0;
|
||||
|
||||
offset = (err_sector *
|
||||
ECC_SECTOR_SIZE +
|
||||
err_byte) *
|
||||
denali->devnum +
|
||||
err_device;
|
||||
/* correct the ECC error */
|
||||
buf[offset] ^= err_correction_value;
|
||||
mtd->ecc_stats.corrected++;
|
||||
bitflips++;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* if the error is not correctable, need to
|
||||
* look at the page to see if it is an erased
|
||||
* page. if so, then it's not a real ECC error
|
||||
*/
|
||||
check_erased_page = true;
|
||||
}
|
||||
} while (!ECC_LAST_ERR(err_correction_info));
|
||||
/*
|
||||
* Once handle all ecc errors, controller will triger
|
||||
* a ECC_TRANSACTION_DONE interrupt, so here just wait
|
||||
* for a while for this interrupt
|
||||
*/
|
||||
while (!(read_interrupt_status(denali) &
|
||||
INTR_STATUS__ECC_TRANSACTION_DONE))
|
||||
cpu_relax();
|
||||
clear_interrupts(denali);
|
||||
denali_set_intr_modes(denali, true);
|
||||
}
|
||||
*max_bitflips = bitflips;
|
||||
return check_erased_page;
|
||||
if (ECC_ERROR_UNCORRECTABLE(err_cor_info)) {
|
||||
/*
|
||||
* Check later if this is a real ECC error, or
|
||||
* an erased sector.
|
||||
*/
|
||||
*uncor_ecc_flags |= BIT(err_sector);
|
||||
} else if (err_byte < ECC_SECTOR_SIZE) {
|
||||
/*
|
||||
* If err_byte is larger than ECC_SECTOR_SIZE, means error
|
||||
* happened in OOB, so we ignore it. It's no need for
|
||||
* us to correct it err_device is represented the NAND
|
||||
* error bits are happened in if there are more than
|
||||
* one NAND connected.
|
||||
*/
|
||||
int offset;
|
||||
unsigned int flips_in_byte;
|
||||
|
||||
offset = (err_sector * ECC_SECTOR_SIZE + err_byte) *
|
||||
denali->devnum + err_device;
|
||||
|
||||
/* correct the ECC error */
|
||||
flips_in_byte = hweight8(buf[offset] ^ err_cor_value);
|
||||
buf[offset] ^= err_cor_value;
|
||||
mtd->ecc_stats.corrected += flips_in_byte;
|
||||
bitflips += flips_in_byte;
|
||||
|
||||
max_bitflips = max(max_bitflips, bitflips);
|
||||
}
|
||||
|
||||
prev_sector = err_sector;
|
||||
} while (!ECC_LAST_ERR(err_cor_info));
|
||||
|
||||
/*
|
||||
* Once handle all ecc errors, controller will trigger a
|
||||
* ECC_TRANSACTION_DONE interrupt, so here just wait for
|
||||
* a while for this interrupt
|
||||
*/
|
||||
while (!(read_interrupt_status(denali) & INTR__ECC_TRANSACTION_DONE))
|
||||
cpu_relax();
|
||||
clear_interrupts(denali);
|
||||
denali_set_intr_modes(denali, true);
|
||||
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/* programs the controller to either enable/disable DMA transfers */
|
||||
@ -991,8 +974,30 @@ static void denali_enable_dma(struct denali_nand_info *denali, bool en)
|
||||
ioread32(denali->flash_reg + DMA_ENABLE);
|
||||
}
|
||||
|
||||
/* setups the HW to perform the data DMA */
|
||||
static void denali_setup_dma(struct denali_nand_info *denali, int op)
|
||||
static void denali_setup_dma64(struct denali_nand_info *denali, int op)
|
||||
{
|
||||
uint32_t mode;
|
||||
const int page_count = 1;
|
||||
uint64_t addr = denali->buf.dma_buf;
|
||||
|
||||
mode = MODE_10 | BANK(denali->flash_bank) | denali->page;
|
||||
|
||||
/* DMA is a three step process */
|
||||
|
||||
/*
|
||||
* 1. setup transfer type, interrupt when complete,
|
||||
* burst len = 64 bytes, the number of pages
|
||||
*/
|
||||
index_addr(denali, mode, 0x01002000 | (64 << 16) | op | page_count);
|
||||
|
||||
/* 2. set memory low address */
|
||||
index_addr(denali, mode, addr);
|
||||
|
||||
/* 3. set memory high address */
|
||||
index_addr(denali, mode, addr >> 32);
|
||||
}
|
||||
|
||||
static void denali_setup_dma32(struct denali_nand_info *denali, int op)
|
||||
{
|
||||
uint32_t mode;
|
||||
const int page_count = 1;
|
||||
@ -1015,6 +1020,14 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op)
|
||||
index_addr(denali, mode | 0x14000, 0x2400);
|
||||
}
|
||||
|
||||
static void denali_setup_dma(struct denali_nand_info *denali, int op)
|
||||
{
|
||||
if (denali->caps & DENALI_CAP_DMA_64BIT)
|
||||
denali_setup_dma64(denali, op);
|
||||
else
|
||||
denali_setup_dma32(denali, op);
|
||||
}
|
||||
|
||||
/*
|
||||
* writes a page. user specifies type, and this function handles the
|
||||
* configuration details.
|
||||
@ -1026,8 +1039,7 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
dma_addr_t addr = denali->buf.dma_buf;
|
||||
size_t size = mtd->writesize + mtd->oobsize;
|
||||
uint32_t irq_status;
|
||||
uint32_t irq_mask = INTR_STATUS__DMA_CMD_COMP |
|
||||
INTR_STATUS__PROGRAM_FAIL;
|
||||
uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
|
||||
|
||||
/*
|
||||
* if it is a raw xfer, we want to disable ecc and send the spare area.
|
||||
@ -1118,16 +1130,15 @@ static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
unsigned int max_bitflips;
|
||||
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
||||
|
||||
dma_addr_t addr = denali->buf.dma_buf;
|
||||
size_t size = mtd->writesize + mtd->oobsize;
|
||||
|
||||
uint32_t irq_status;
|
||||
uint32_t irq_mask = INTR_STATUS__ECC_TRANSACTION_DONE |
|
||||
INTR_STATUS__ECC_ERR;
|
||||
bool check_erased_page = false;
|
||||
uint32_t irq_mask = denali->caps & DENALI_CAP_HW_ECC_FIXUP ?
|
||||
INTR__DMA_CMD_COMP | INTR__ECC_UNCOR_ERR :
|
||||
INTR__ECC_TRANSACTION_DONE | INTR__ECC_ERR;
|
||||
unsigned long uncor_ecc_flags = 0;
|
||||
int stat = 0;
|
||||
|
||||
if (page != denali->page) {
|
||||
dev_err(denali->dev,
|
||||
@ -1151,21 +1162,23 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
|
||||
memcpy(buf, denali->buf.buf, mtd->writesize);
|
||||
|
||||
check_erased_page = handle_ecc(denali, buf, irq_status, &max_bitflips);
|
||||
if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
|
||||
stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags);
|
||||
else if (irq_status & INTR__ECC_ERR)
|
||||
stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf);
|
||||
denali_enable_dma(denali, false);
|
||||
|
||||
if (check_erased_page) {
|
||||
if (stat < 0)
|
||||
return stat;
|
||||
|
||||
if (uncor_ecc_flags) {
|
||||
read_oob_data(mtd, chip->oob_poi, denali->page);
|
||||
|
||||
/* check ECC failures that may have occurred on erased pages */
|
||||
if (check_erased_page) {
|
||||
if (!is_erased(buf, mtd->writesize))
|
||||
mtd->ecc_stats.failed++;
|
||||
if (!is_erased(buf, mtd->oobsize))
|
||||
mtd->ecc_stats.failed++;
|
||||
}
|
||||
stat = denali_check_erased_page(mtd, chip, buf,
|
||||
uncor_ecc_flags, stat);
|
||||
}
|
||||
return max_bitflips;
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
@ -1174,7 +1187,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
||||
dma_addr_t addr = denali->buf.dma_buf;
|
||||
size_t size = mtd->writesize + mtd->oobsize;
|
||||
uint32_t irq_mask = INTR_STATUS__DMA_CMD_COMP;
|
||||
uint32_t irq_mask = INTR__DMA_CMD_COMP;
|
||||
|
||||
if (page != denali->page) {
|
||||
dev_err(denali->dev,
|
||||
@ -1247,10 +1260,9 @@ static int denali_erase(struct mtd_info *mtd, int page)
|
||||
index_addr(denali, cmd, 0x1);
|
||||
|
||||
/* wait for erase to complete or failure to occur */
|
||||
irq_status = wait_for_irq(denali, INTR_STATUS__ERASE_COMP |
|
||||
INTR_STATUS__ERASE_FAIL);
|
||||
irq_status = wait_for_irq(denali, INTR__ERASE_COMP | INTR__ERASE_FAIL);
|
||||
|
||||
return irq_status & INTR_STATUS__ERASE_FAIL ? NAND_STATUS_FAIL : PASS;
|
||||
return irq_status & INTR__ERASE_FAIL ? NAND_STATUS_FAIL : PASS;
|
||||
}
|
||||
|
||||
static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
|
||||
@ -1302,6 +1314,14 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
|
||||
/* Initialization code to bring the device up to a known good state */
|
||||
static void denali_hw_init(struct denali_nand_info *denali)
|
||||
{
|
||||
/*
|
||||
* The REVISION register may not be reliable. Platforms are allowed to
|
||||
* override it.
|
||||
*/
|
||||
if (!denali->revision)
|
||||
denali->revision =
|
||||
swab16(ioread32(denali->flash_reg + REVISION));
|
||||
|
||||
/*
|
||||
* tell driver how many bit controller will skip before
|
||||
* writing ECC code in OOB, this register may be already
|
||||
@ -1413,9 +1433,61 @@ static void denali_drv_init(struct denali_nand_info *denali)
|
||||
denali->irq_status = 0;
|
||||
}
|
||||
|
||||
static int denali_multidev_fixup(struct denali_nand_info *denali)
|
||||
{
|
||||
struct nand_chip *chip = &denali->nand;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
||||
/*
|
||||
* Support for multi device:
|
||||
* When the IP configuration is x16 capable and two x8 chips are
|
||||
* connected in parallel, DEVICES_CONNECTED should be set to 2.
|
||||
* In this case, the core framework knows nothing about this fact,
|
||||
* so we should tell it the _logical_ pagesize and anything necessary.
|
||||
*/
|
||||
denali->devnum = ioread32(denali->flash_reg + DEVICES_CONNECTED);
|
||||
|
||||
/*
|
||||
* On some SoCs, DEVICES_CONNECTED is not auto-detected.
|
||||
* For those, DEVICES_CONNECTED is left to 0. Set 1 if it is the case.
|
||||
*/
|
||||
if (denali->devnum == 0) {
|
||||
denali->devnum = 1;
|
||||
iowrite32(1, denali->flash_reg + DEVICES_CONNECTED);
|
||||
}
|
||||
|
||||
if (denali->devnum == 1)
|
||||
return 0;
|
||||
|
||||
if (denali->devnum != 2) {
|
||||
dev_err(denali->dev, "unsupported number of devices %d\n",
|
||||
denali->devnum);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 2 chips in parallel */
|
||||
mtd->size <<= 1;
|
||||
mtd->erasesize <<= 1;
|
||||
mtd->writesize <<= 1;
|
||||
mtd->oobsize <<= 1;
|
||||
chip->chipsize <<= 1;
|
||||
chip->page_shift += 1;
|
||||
chip->phys_erase_shift += 1;
|
||||
chip->bbt_erase_shift += 1;
|
||||
chip->chip_shift += 1;
|
||||
chip->pagemask <<= 1;
|
||||
chip->ecc.size <<= 1;
|
||||
chip->ecc.bytes <<= 1;
|
||||
chip->ecc.strength <<= 1;
|
||||
denali->bbtskipbytes <<= 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int denali_init(struct denali_nand_info *denali)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(&denali->nand);
|
||||
struct nand_chip *chip = &denali->nand;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int ret;
|
||||
|
||||
if (denali->platform == INTEL_CE4100) {
|
||||
@ -1449,13 +1521,16 @@ int denali_init(struct denali_nand_info *denali)
|
||||
|
||||
/* now that our ISR is registered, we can enable interrupts */
|
||||
denali_set_intr_modes(denali, true);
|
||||
mtd->name = "denali-nand";
|
||||
nand_set_flash_node(chip, denali->dev->of_node);
|
||||
/* Fallback to the default name if DT did not give "label" property */
|
||||
if (!mtd->name)
|
||||
mtd->name = "denali-nand";
|
||||
|
||||
/* register the driver with the NAND core subsystem */
|
||||
denali->nand.select_chip = denali_select_chip;
|
||||
denali->nand.cmdfunc = denali_cmdfunc;
|
||||
denali->nand.read_byte = denali_read_byte;
|
||||
denali->nand.waitfunc = denali_waitfunc;
|
||||
chip->select_chip = denali_select_chip;
|
||||
chip->cmdfunc = denali_cmdfunc;
|
||||
chip->read_byte = denali_read_byte;
|
||||
chip->waitfunc = denali_waitfunc;
|
||||
|
||||
/*
|
||||
* scan for NAND devices attached to the controller
|
||||
@ -1476,8 +1551,9 @@ int denali_init(struct denali_nand_info *denali)
|
||||
goto failed_req_irq;
|
||||
}
|
||||
|
||||
/* Is 32-bit DMA supported? */
|
||||
ret = dma_set_mask(denali->dev, DMA_BIT_MASK(32));
|
||||
ret = dma_set_mask(denali->dev,
|
||||
DMA_BIT_MASK(denali->caps & DENALI_CAP_DMA_64BIT ?
|
||||
64 : 32));
|
||||
if (ret) {
|
||||
dev_err(denali->dev, "No usable DMA configuration\n");
|
||||
goto failed_req_irq;
|
||||
@ -1492,25 +1568,6 @@ int denali_init(struct denali_nand_info *denali)
|
||||
goto failed_req_irq;
|
||||
}
|
||||
|
||||
/*
|
||||
* support for multi nand
|
||||
* MTD known nothing about multi nand, so we should tell it
|
||||
* the real pagesize and anything necessery
|
||||
*/
|
||||
denali->devnum = ioread32(denali->flash_reg + DEVICES_CONNECTED);
|
||||
denali->nand.chipsize <<= denali->devnum - 1;
|
||||
denali->nand.page_shift += denali->devnum - 1;
|
||||
denali->nand.pagemask = (denali->nand.chipsize >>
|
||||
denali->nand.page_shift) - 1;
|
||||
denali->nand.bbt_erase_shift += denali->devnum - 1;
|
||||
denali->nand.phys_erase_shift = denali->nand.bbt_erase_shift;
|
||||
denali->nand.chip_shift += denali->devnum - 1;
|
||||
mtd->writesize <<= denali->devnum - 1;
|
||||
mtd->oobsize <<= denali->devnum - 1;
|
||||
mtd->erasesize <<= denali->devnum - 1;
|
||||
mtd->size = denali->nand.numchips * denali->nand.chipsize;
|
||||
denali->bbtskipbytes *= denali->devnum;
|
||||
|
||||
/*
|
||||
* second stage of the NAND scan
|
||||
* this stage requires information regarding ECC and
|
||||
@ -1518,29 +1575,29 @@ int denali_init(struct denali_nand_info *denali)
|
||||
*/
|
||||
|
||||
/* Bad block management */
|
||||
denali->nand.bbt_td = &bbt_main_descr;
|
||||
denali->nand.bbt_md = &bbt_mirror_descr;
|
||||
chip->bbt_td = &bbt_main_descr;
|
||||
chip->bbt_md = &bbt_mirror_descr;
|
||||
|
||||
/* skip the scan for now until we have OOB read and write support */
|
||||
denali->nand.bbt_options |= NAND_BBT_USE_FLASH;
|
||||
denali->nand.options |= NAND_SKIP_BBTSCAN;
|
||||
denali->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
|
||||
chip->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
chip->options |= NAND_SKIP_BBTSCAN;
|
||||
chip->ecc.mode = NAND_ECC_HW_SYNDROME;
|
||||
|
||||
/* no subpage writes on denali */
|
||||
denali->nand.options |= NAND_NO_SUBPAGE_WRITE;
|
||||
chip->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
|
||||
/*
|
||||
* Denali Controller only support 15bit and 8bit ECC in MRST,
|
||||
* so just let controller do 15bit ECC for MLC and 8bit ECC for
|
||||
* SLC if possible.
|
||||
* */
|
||||
if (!nand_is_slc(&denali->nand) &&
|
||||
if (!nand_is_slc(chip) &&
|
||||
(mtd->oobsize > (denali->bbtskipbytes +
|
||||
ECC_15BITS * (mtd->writesize /
|
||||
ECC_SECTOR_SIZE)))) {
|
||||
/* if MLC OOB size is large enough, use 15bit ECC*/
|
||||
denali->nand.ecc.strength = 15;
|
||||
denali->nand.ecc.bytes = ECC_15BITS;
|
||||
chip->ecc.strength = 15;
|
||||
chip->ecc.bytes = ECC_15BITS;
|
||||
iowrite32(15, denali->flash_reg + ECC_CORRECTION);
|
||||
} else if (mtd->oobsize < (denali->bbtskipbytes +
|
||||
ECC_8BITS * (mtd->writesize /
|
||||
@ -1548,24 +1605,26 @@ int denali_init(struct denali_nand_info *denali)
|
||||
pr_err("Your NAND chip OOB is not large enough to contain 8bit ECC correction codes");
|
||||
goto failed_req_irq;
|
||||
} else {
|
||||
denali->nand.ecc.strength = 8;
|
||||
denali->nand.ecc.bytes = ECC_8BITS;
|
||||
chip->ecc.strength = 8;
|
||||
chip->ecc.bytes = ECC_8BITS;
|
||||
iowrite32(8, denali->flash_reg + ECC_CORRECTION);
|
||||
}
|
||||
|
||||
mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
|
||||
denali->nand.ecc.bytes *= denali->devnum;
|
||||
denali->nand.ecc.strength *= denali->devnum;
|
||||
|
||||
/* override the default read operations */
|
||||
denali->nand.ecc.size = ECC_SECTOR_SIZE * denali->devnum;
|
||||
denali->nand.ecc.read_page = denali_read_page;
|
||||
denali->nand.ecc.read_page_raw = denali_read_page_raw;
|
||||
denali->nand.ecc.write_page = denali_write_page;
|
||||
denali->nand.ecc.write_page_raw = denali_write_page_raw;
|
||||
denali->nand.ecc.read_oob = denali_read_oob;
|
||||
denali->nand.ecc.write_oob = denali_write_oob;
|
||||
denali->nand.erase = denali_erase;
|
||||
chip->ecc.size = ECC_SECTOR_SIZE;
|
||||
chip->ecc.read_page = denali_read_page;
|
||||
chip->ecc.read_page_raw = denali_read_page_raw;
|
||||
chip->ecc.write_page = denali_write_page;
|
||||
chip->ecc.write_page_raw = denali_write_page_raw;
|
||||
chip->ecc.read_oob = denali_read_oob;
|
||||
chip->ecc.write_oob = denali_write_oob;
|
||||
chip->erase = denali_erase;
|
||||
|
||||
ret = denali_multidev_fixup(denali);
|
||||
if (ret)
|
||||
goto failed_req_irq;
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
if (ret)
|
||||
|
@ -20,6 +20,7 @@
|
||||
#ifndef __DENALI_H__
|
||||
#define __DENALI_H__
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
|
||||
#define DEVICE_RESET 0x0
|
||||
@ -178,8 +179,6 @@
|
||||
|
||||
#define REVISION 0x370
|
||||
#define REVISION__VALUE 0xffff
|
||||
#define MAKE_COMPARABLE_REVISION(x) swab16((x) & REVISION__VALUE)
|
||||
#define REVISION_5_1 0x00000501
|
||||
|
||||
#define ONFI_DEVICE_FEATURES 0x380
|
||||
#define ONFI_DEVICE_FEATURES__VALUE 0x003f
|
||||
@ -218,65 +217,29 @@
|
||||
|
||||
#define INTR_STATUS(__bank) (0x410 + ((__bank) * 0x50))
|
||||
#define INTR_EN(__bank) (0x420 + ((__bank) * 0x50))
|
||||
|
||||
#define INTR_STATUS__ECC_TRANSACTION_DONE 0x0001
|
||||
#define INTR_STATUS__ECC_ERR 0x0002
|
||||
#define INTR_STATUS__DMA_CMD_COMP 0x0004
|
||||
#define INTR_STATUS__TIME_OUT 0x0008
|
||||
#define INTR_STATUS__PROGRAM_FAIL 0x0010
|
||||
#define INTR_STATUS__ERASE_FAIL 0x0020
|
||||
#define INTR_STATUS__LOAD_COMP 0x0040
|
||||
#define INTR_STATUS__PROGRAM_COMP 0x0080
|
||||
#define INTR_STATUS__ERASE_COMP 0x0100
|
||||
#define INTR_STATUS__PIPE_CPYBCK_CMD_COMP 0x0200
|
||||
#define INTR_STATUS__LOCKED_BLK 0x0400
|
||||
#define INTR_STATUS__UNSUP_CMD 0x0800
|
||||
#define INTR_STATUS__INT_ACT 0x1000
|
||||
#define INTR_STATUS__RST_COMP 0x2000
|
||||
#define INTR_STATUS__PIPE_CMD_ERR 0x4000
|
||||
#define INTR_STATUS__PAGE_XFER_INC 0x8000
|
||||
|
||||
#define INTR_EN__ECC_TRANSACTION_DONE 0x0001
|
||||
#define INTR_EN__ECC_ERR 0x0002
|
||||
#define INTR_EN__DMA_CMD_COMP 0x0004
|
||||
#define INTR_EN__TIME_OUT 0x0008
|
||||
#define INTR_EN__PROGRAM_FAIL 0x0010
|
||||
#define INTR_EN__ERASE_FAIL 0x0020
|
||||
#define INTR_EN__LOAD_COMP 0x0040
|
||||
#define INTR_EN__PROGRAM_COMP 0x0080
|
||||
#define INTR_EN__ERASE_COMP 0x0100
|
||||
#define INTR_EN__PIPE_CPYBCK_CMD_COMP 0x0200
|
||||
#define INTR_EN__LOCKED_BLK 0x0400
|
||||
#define INTR_EN__UNSUP_CMD 0x0800
|
||||
#define INTR_EN__INT_ACT 0x1000
|
||||
#define INTR_EN__RST_COMP 0x2000
|
||||
#define INTR_EN__PIPE_CMD_ERR 0x4000
|
||||
#define INTR_EN__PAGE_XFER_INC 0x8000
|
||||
/* bit[1:0] is used differently depending on IP version */
|
||||
#define INTR__ECC_UNCOR_ERR 0x0001 /* new IP */
|
||||
#define INTR__ECC_TRANSACTION_DONE 0x0001 /* old IP */
|
||||
#define INTR__ECC_ERR 0x0002 /* old IP */
|
||||
#define INTR__DMA_CMD_COMP 0x0004
|
||||
#define INTR__TIME_OUT 0x0008
|
||||
#define INTR__PROGRAM_FAIL 0x0010
|
||||
#define INTR__ERASE_FAIL 0x0020
|
||||
#define INTR__LOAD_COMP 0x0040
|
||||
#define INTR__PROGRAM_COMP 0x0080
|
||||
#define INTR__ERASE_COMP 0x0100
|
||||
#define INTR__PIPE_CPYBCK_CMD_COMP 0x0200
|
||||
#define INTR__LOCKED_BLK 0x0400
|
||||
#define INTR__UNSUP_CMD 0x0800
|
||||
#define INTR__INT_ACT 0x1000
|
||||
#define INTR__RST_COMP 0x2000
|
||||
#define INTR__PIPE_CMD_ERR 0x4000
|
||||
#define INTR__PAGE_XFER_INC 0x8000
|
||||
|
||||
#define PAGE_CNT(__bank) (0x430 + ((__bank) * 0x50))
|
||||
#define ERR_PAGE_ADDR(__bank) (0x440 + ((__bank) * 0x50))
|
||||
#define ERR_BLOCK_ADDR(__bank) (0x450 + ((__bank) * 0x50))
|
||||
|
||||
#define DATA_INTR 0x550
|
||||
#define DATA_INTR__WRITE_SPACE_AV 0x0001
|
||||
#define DATA_INTR__READ_DATA_AV 0x0002
|
||||
|
||||
#define DATA_INTR_EN 0x560
|
||||
#define DATA_INTR_EN__WRITE_SPACE_AV 0x0001
|
||||
#define DATA_INTR_EN__READ_DATA_AV 0x0002
|
||||
|
||||
#define GPREG_0 0x570
|
||||
#define GPREG_0__VALUE 0xffff
|
||||
|
||||
#define GPREG_1 0x580
|
||||
#define GPREG_1__VALUE 0xffff
|
||||
|
||||
#define GPREG_2 0x590
|
||||
#define GPREG_2__VALUE 0xffff
|
||||
|
||||
#define GPREG_3 0x5a0
|
||||
#define GPREG_3__VALUE 0xffff
|
||||
|
||||
#define ECC_THRESHOLD 0x600
|
||||
#define ECC_THRESHOLD__VALUE 0x03ff
|
||||
|
||||
@ -297,6 +260,11 @@
|
||||
#define ERR_CORRECTION_INFO__ERROR_TYPE 0x4000
|
||||
#define ERR_CORRECTION_INFO__LAST_ERR_INFO 0x8000
|
||||
|
||||
#define ECC_COR_INFO(bank) (0x650 + (bank) / 2 * 0x10)
|
||||
#define ECC_COR_INFO__SHIFT(bank) ((bank) % 2 * 8)
|
||||
#define ECC_COR_INFO__MAX_ERRORS 0x007f
|
||||
#define ECC_COR_INFO__UNCOR_ERR 0x0080
|
||||
|
||||
#define DMA_ENABLE 0x700
|
||||
#define DMA_ENABLE__FLAG 0x0001
|
||||
|
||||
@ -304,20 +272,13 @@
|
||||
#define IGNORE_ECC_DONE__FLAG 0x0001
|
||||
|
||||
#define DMA_INTR 0x720
|
||||
#define DMA_INTR_EN 0x730
|
||||
#define DMA_INTR__TARGET_ERROR 0x0001
|
||||
#define DMA_INTR__DESC_COMP_CHANNEL0 0x0002
|
||||
#define DMA_INTR__DESC_COMP_CHANNEL1 0x0004
|
||||
#define DMA_INTR__DESC_COMP_CHANNEL2 0x0008
|
||||
#define DMA_INTR__DESC_COMP_CHANNEL3 0x0010
|
||||
#define DMA_INTR__MEMCOPY_DESC_COMP 0x0020
|
||||
|
||||
#define DMA_INTR_EN 0x730
|
||||
#define DMA_INTR_EN__TARGET_ERROR 0x0001
|
||||
#define DMA_INTR_EN__DESC_COMP_CHANNEL0 0x0002
|
||||
#define DMA_INTR_EN__DESC_COMP_CHANNEL1 0x0004
|
||||
#define DMA_INTR_EN__DESC_COMP_CHANNEL2 0x0008
|
||||
#define DMA_INTR_EN__DESC_COMP_CHANNEL3 0x0010
|
||||
#define DMA_INTR_EN__MEMCOPY_DESC_COMP 0x0020
|
||||
#define DMA_INTR__MEMCOPY_DESC_COMP 0x0020
|
||||
|
||||
#define TARGET_ERR_ADDR_LO 0x740
|
||||
#define TARGET_ERR_ADDR_LO__VALUE 0xffff
|
||||
@ -331,69 +292,12 @@
|
||||
#define CHNL_ACTIVE__CHANNEL2 0x0004
|
||||
#define CHNL_ACTIVE__CHANNEL3 0x0008
|
||||
|
||||
#define ACTIVE_SRC_ID 0x800
|
||||
#define ACTIVE_SRC_ID__VALUE 0x00ff
|
||||
|
||||
#define PTN_INTR 0x810
|
||||
#define PTN_INTR__CONFIG_ERROR 0x0001
|
||||
#define PTN_INTR__ACCESS_ERROR_BANK0 0x0002
|
||||
#define PTN_INTR__ACCESS_ERROR_BANK1 0x0004
|
||||
#define PTN_INTR__ACCESS_ERROR_BANK2 0x0008
|
||||
#define PTN_INTR__ACCESS_ERROR_BANK3 0x0010
|
||||
#define PTN_INTR__REG_ACCESS_ERROR 0x0020
|
||||
|
||||
#define PTN_INTR_EN 0x820
|
||||
#define PTN_INTR_EN__CONFIG_ERROR 0x0001
|
||||
#define PTN_INTR_EN__ACCESS_ERROR_BANK0 0x0002
|
||||
#define PTN_INTR_EN__ACCESS_ERROR_BANK1 0x0004
|
||||
#define PTN_INTR_EN__ACCESS_ERROR_BANK2 0x0008
|
||||
#define PTN_INTR_EN__ACCESS_ERROR_BANK3 0x0010
|
||||
#define PTN_INTR_EN__REG_ACCESS_ERROR 0x0020
|
||||
|
||||
#define PERM_SRC_ID(__bank) (0x830 + ((__bank) * 0x40))
|
||||
#define PERM_SRC_ID__SRCID 0x00ff
|
||||
#define PERM_SRC_ID__DIRECT_ACCESS_ACTIVE 0x0800
|
||||
#define PERM_SRC_ID__WRITE_ACTIVE 0x2000
|
||||
#define PERM_SRC_ID__READ_ACTIVE 0x4000
|
||||
#define PERM_SRC_ID__PARTITION_VALID 0x8000
|
||||
|
||||
#define MIN_BLK_ADDR(__bank) (0x840 + ((__bank) * 0x40))
|
||||
#define MIN_BLK_ADDR__VALUE 0xffff
|
||||
|
||||
#define MAX_BLK_ADDR(__bank) (0x850 + ((__bank) * 0x40))
|
||||
#define MAX_BLK_ADDR__VALUE 0xffff
|
||||
|
||||
#define MIN_MAX_BANK(__bank) (0x860 + ((__bank) * 0x40))
|
||||
#define MIN_MAX_BANK__MIN_VALUE 0x0003
|
||||
#define MIN_MAX_BANK__MAX_VALUE 0x000c
|
||||
|
||||
|
||||
/* ffsdefs.h */
|
||||
#define CLEAR 0 /*use this to clear a field instead of "fail"*/
|
||||
#define SET 1 /*use this to set a field instead of "pass"*/
|
||||
#define FAIL 1 /*failed flag*/
|
||||
#define PASS 0 /*success flag*/
|
||||
#define ERR -1 /*error flag*/
|
||||
|
||||
/* lld.h */
|
||||
#define GOOD_BLOCK 0
|
||||
#define DEFECTIVE_BLOCK 1
|
||||
#define READ_ERROR 2
|
||||
|
||||
#define CLK_X 5
|
||||
#define CLK_MULTI 4
|
||||
|
||||
/* KBV - Updated to LNW scratch register address */
|
||||
#define SCRATCH_REG_ADDR CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR
|
||||
#define SCRATCH_REG_SIZE 64
|
||||
|
||||
#define GLOB_HWCTL_DEFAULT_BLKS 2048
|
||||
|
||||
#define SUPPORT_15BITECC 1
|
||||
#define SUPPORT_8BITECC 1
|
||||
|
||||
#define CUSTOM_CONF_PARAMS 0
|
||||
|
||||
#define ONFI_BLOOM_TIME 1
|
||||
#define MODE5_WORKAROUND 0
|
||||
|
||||
@ -403,31 +307,6 @@
|
||||
#define MODE_10 0x08000000
|
||||
#define MODE_11 0x0C000000
|
||||
|
||||
|
||||
#define DATA_TRANSFER_MODE 0
|
||||
#define PROTECTION_PER_BLOCK 1
|
||||
#define LOAD_WAIT_COUNT 2
|
||||
#define PROGRAM_WAIT_COUNT 3
|
||||
#define ERASE_WAIT_COUNT 4
|
||||
#define INT_MONITOR_CYCLE_COUNT 5
|
||||
#define READ_BUSY_PIN_ENABLED 6
|
||||
#define MULTIPLANE_OPERATION_SUPPORT 7
|
||||
#define PRE_FETCH_MODE 8
|
||||
#define CE_DONT_CARE_SUPPORT 9
|
||||
#define COPYBACK_SUPPORT 10
|
||||
#define CACHE_WRITE_SUPPORT 11
|
||||
#define CACHE_READ_SUPPORT 12
|
||||
#define NUM_PAGES_IN_BLOCK 13
|
||||
#define ECC_ENABLE_SELECT 14
|
||||
#define WRITE_ENABLE_2_READ_ENABLE 15
|
||||
#define ADDRESS_2_DATA 16
|
||||
#define READ_ENABLE_2_WRITE_ENABLE 17
|
||||
#define TWO_ROW_ADDRESS_CYCLES 18
|
||||
#define MULTIPLANE_ADDRESS_RESTRICT 19
|
||||
#define ACC_CLOCKS 20
|
||||
#define READ_WRITE_ENABLE_LOW_COUNT 21
|
||||
#define READ_WRITE_ENABLE_HIGH_COUNT 22
|
||||
|
||||
#define ECC_SECTOR_SIZE 512
|
||||
|
||||
struct nand_buf {
|
||||
@ -449,23 +328,26 @@ struct denali_nand_info {
|
||||
struct nand_buf buf;
|
||||
struct device *dev;
|
||||
int total_used_banks;
|
||||
uint32_t block; /* stored for future use */
|
||||
uint16_t page;
|
||||
void __iomem *flash_reg; /* Mapped io reg base address */
|
||||
void __iomem *flash_mem; /* Mapped io reg base address */
|
||||
int page;
|
||||
void __iomem *flash_reg; /* Register Interface */
|
||||
void __iomem *flash_mem; /* Host Data/Command Interface */
|
||||
|
||||
/* elements used by ISR */
|
||||
struct completion complete;
|
||||
spinlock_t irq_lock;
|
||||
uint32_t irq_status;
|
||||
int irq_debug_array[32];
|
||||
int irq;
|
||||
|
||||
uint32_t devnum; /* represent how many nands connected */
|
||||
uint32_t bbtskipbytes;
|
||||
uint32_t max_banks;
|
||||
int devnum; /* represent how many nands connected */
|
||||
int bbtskipbytes;
|
||||
int max_banks;
|
||||
unsigned int revision;
|
||||
unsigned int caps;
|
||||
};
|
||||
|
||||
#define DENALI_CAP_HW_ECC_FIXUP BIT(0)
|
||||
#define DENALI_CAP_DMA_64BIT BIT(1)
|
||||
|
||||
extern int denali_init(struct denali_nand_info *denali);
|
||||
extern void denali_remove(struct denali_nand_info *denali);
|
||||
|
||||
|
@ -29,64 +29,66 @@ struct denali_dt {
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static const struct of_device_id denali_nand_dt_ids[] = {
|
||||
{ .compatible = "denali,denali-nand-dt" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
struct denali_dt_data {
|
||||
unsigned int revision;
|
||||
unsigned int caps;
|
||||
};
|
||||
|
||||
static const struct denali_dt_data denali_socfpga_data = {
|
||||
.caps = DENALI_CAP_HW_ECC_FIXUP,
|
||||
};
|
||||
|
||||
static const struct of_device_id denali_nand_dt_ids[] = {
|
||||
{
|
||||
.compatible = "altr,socfpga-denali-nand",
|
||||
.data = &denali_socfpga_data,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
|
||||
|
||||
static u64 denali_dma_mask;
|
||||
|
||||
static int denali_dt_probe(struct platform_device *ofdev)
|
||||
static int denali_dt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *denali_reg, *nand_data;
|
||||
struct denali_dt *dt;
|
||||
const struct denali_dt_data *data;
|
||||
struct denali_nand_info *denali;
|
||||
int ret;
|
||||
const struct of_device_id *of_id;
|
||||
|
||||
of_id = of_match_device(denali_nand_dt_ids, &ofdev->dev);
|
||||
if (of_id) {
|
||||
ofdev->id_entry = of_id->data;
|
||||
} else {
|
||||
pr_err("Failed to find the right device id.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dt = devm_kzalloc(&ofdev->dev, sizeof(*dt), GFP_KERNEL);
|
||||
dt = devm_kzalloc(&pdev->dev, sizeof(*dt), GFP_KERNEL);
|
||||
if (!dt)
|
||||
return -ENOMEM;
|
||||
denali = &dt->denali;
|
||||
|
||||
data = of_device_get_match_data(&pdev->dev);
|
||||
if (data) {
|
||||
denali->revision = data->revision;
|
||||
denali->caps = data->caps;
|
||||
}
|
||||
|
||||
denali->platform = DT;
|
||||
denali->dev = &ofdev->dev;
|
||||
denali->irq = platform_get_irq(ofdev, 0);
|
||||
denali->dev = &pdev->dev;
|
||||
denali->irq = platform_get_irq(pdev, 0);
|
||||
if (denali->irq < 0) {
|
||||
dev_err(&ofdev->dev, "no irq defined\n");
|
||||
dev_err(&pdev->dev, "no irq defined\n");
|
||||
return denali->irq;
|
||||
}
|
||||
|
||||
denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg");
|
||||
denali->flash_reg = devm_ioremap_resource(&ofdev->dev, denali_reg);
|
||||
denali_reg = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"denali_reg");
|
||||
denali->flash_reg = devm_ioremap_resource(&pdev->dev, denali_reg);
|
||||
if (IS_ERR(denali->flash_reg))
|
||||
return PTR_ERR(denali->flash_reg);
|
||||
|
||||
nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data");
|
||||
denali->flash_mem = devm_ioremap_resource(&ofdev->dev, nand_data);
|
||||
nand_data = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"nand_data");
|
||||
denali->flash_mem = devm_ioremap_resource(&pdev->dev, nand_data);
|
||||
if (IS_ERR(denali->flash_mem))
|
||||
return PTR_ERR(denali->flash_mem);
|
||||
|
||||
if (!of_property_read_u32(ofdev->dev.of_node,
|
||||
"dma-mask", (u32 *)&denali_dma_mask)) {
|
||||
denali->dev->dma_mask = &denali_dma_mask;
|
||||
} else {
|
||||
denali->dev->dma_mask = NULL;
|
||||
}
|
||||
|
||||
dt->clk = devm_clk_get(&ofdev->dev, NULL);
|
||||
dt->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(dt->clk)) {
|
||||
dev_err(&ofdev->dev, "no clk available\n");
|
||||
dev_err(&pdev->dev, "no clk available\n");
|
||||
return PTR_ERR(dt->clk);
|
||||
}
|
||||
clk_prepare_enable(dt->clk);
|
||||
@ -95,7 +97,7 @@ static int denali_dt_probe(struct platform_device *ofdev)
|
||||
if (ret)
|
||||
goto out_disable_clk;
|
||||
|
||||
platform_set_drvdata(ofdev, dt);
|
||||
platform_set_drvdata(pdev, dt);
|
||||
return 0;
|
||||
|
||||
out_disable_clk:
|
||||
@ -104,9 +106,9 @@ static int denali_dt_probe(struct platform_device *ofdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int denali_dt_remove(struct platform_device *ofdev)
|
||||
static int denali_dt_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct denali_dt *dt = platform_get_drvdata(ofdev);
|
||||
struct denali_dt *dt = platform_get_drvdata(pdev);
|
||||
|
||||
denali_remove(&dt->denali);
|
||||
clk_disable_unprepare(dt->clk);
|
||||
|
@ -38,15 +38,6 @@
|
||||
#include <linux/amba/bus.h>
|
||||
#include <mtd/mtd-abi.h>
|
||||
|
||||
#define FSMC_NAND_BW8 1
|
||||
#define FSMC_NAND_BW16 2
|
||||
|
||||
#define FSMC_MAX_NOR_BANKS 4
|
||||
#define FSMC_MAX_NAND_BANKS 4
|
||||
|
||||
#define FSMC_FLASH_WIDTH8 1
|
||||
#define FSMC_FLASH_WIDTH16 2
|
||||
|
||||
/* fsmc controller registers for NOR flash */
|
||||
#define CTRL 0x0
|
||||
/* ctrl register definitions */
|
||||
@ -133,33 +124,48 @@ enum access_mode {
|
||||
};
|
||||
|
||||
/**
|
||||
* fsmc_nand_platform_data - platform specific NAND controller config
|
||||
* @nand_timings: timing setup for the physical NAND interface
|
||||
* @partitions: partition table for the platform, use a default fallback
|
||||
* if this is NULL
|
||||
* @nr_partitions: the number of partitions in the previous entry
|
||||
* @options: different options for the driver
|
||||
* @width: bus width
|
||||
* @bank: default bank
|
||||
* @select_bank: callback to select a certain bank, this is
|
||||
* platform-specific. If the controller only supports one bank
|
||||
* this may be set to NULL
|
||||
* struct fsmc_nand_data - structure for FSMC NAND device state
|
||||
*
|
||||
* @pid: Part ID on the AMBA PrimeCell format
|
||||
* @mtd: MTD info for a NAND flash.
|
||||
* @nand: Chip related info for a NAND flash.
|
||||
* @partitions: Partition info for a NAND Flash.
|
||||
* @nr_partitions: Total number of partition of a NAND flash.
|
||||
*
|
||||
* @bank: Bank number for probed device.
|
||||
* @clk: Clock structure for FSMC.
|
||||
*
|
||||
* @read_dma_chan: DMA channel for read access
|
||||
* @write_dma_chan: DMA channel for write access to NAND
|
||||
* @dma_access_complete: Completion structure
|
||||
*
|
||||
* @data_pa: NAND Physical port for Data.
|
||||
* @data_va: NAND port for Data.
|
||||
* @cmd_va: NAND port for Command.
|
||||
* @addr_va: NAND port for Address.
|
||||
* @regs_va: FSMC regs base address.
|
||||
*/
|
||||
struct fsmc_nand_platform_data {
|
||||
struct fsmc_nand_timings *nand_timings;
|
||||
struct mtd_partition *partitions;
|
||||
unsigned int nr_partitions;
|
||||
unsigned int options;
|
||||
unsigned int width;
|
||||
struct fsmc_nand_data {
|
||||
u32 pid;
|
||||
struct nand_chip nand;
|
||||
|
||||
unsigned int bank;
|
||||
|
||||
struct device *dev;
|
||||
enum access_mode mode;
|
||||
struct clk *clk;
|
||||
|
||||
void (*select_bank)(uint32_t bank, uint32_t busw);
|
||||
/* DMA related objects */
|
||||
struct dma_chan *read_dma_chan;
|
||||
struct dma_chan *write_dma_chan;
|
||||
struct completion dma_access_complete;
|
||||
|
||||
/* priv structures for dma accesses */
|
||||
void *read_dma_priv;
|
||||
void *write_dma_priv;
|
||||
struct fsmc_nand_timings *dev_timings;
|
||||
|
||||
dma_addr_t data_pa;
|
||||
void __iomem *data_va;
|
||||
void __iomem *cmd_va;
|
||||
void __iomem *addr_va;
|
||||
void __iomem *regs_va;
|
||||
};
|
||||
|
||||
static int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
@ -246,86 +252,11 @@ static const struct mtd_ooblayout_ops fsmc_ecc4_ooblayout_ops = {
|
||||
.free = fsmc_ecc4_ooblayout_free,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct fsmc_nand_data - structure for FSMC NAND device state
|
||||
*
|
||||
* @pid: Part ID on the AMBA PrimeCell format
|
||||
* @mtd: MTD info for a NAND flash.
|
||||
* @nand: Chip related info for a NAND flash.
|
||||
* @partitions: Partition info for a NAND Flash.
|
||||
* @nr_partitions: Total number of partition of a NAND flash.
|
||||
*
|
||||
* @bank: Bank number for probed device.
|
||||
* @clk: Clock structure for FSMC.
|
||||
*
|
||||
* @read_dma_chan: DMA channel for read access
|
||||
* @write_dma_chan: DMA channel for write access to NAND
|
||||
* @dma_access_complete: Completion structure
|
||||
*
|
||||
* @data_pa: NAND Physical port for Data.
|
||||
* @data_va: NAND port for Data.
|
||||
* @cmd_va: NAND port for Command.
|
||||
* @addr_va: NAND port for Address.
|
||||
* @regs_va: FSMC regs base address.
|
||||
*/
|
||||
struct fsmc_nand_data {
|
||||
u32 pid;
|
||||
struct nand_chip nand;
|
||||
struct mtd_partition *partitions;
|
||||
unsigned int nr_partitions;
|
||||
|
||||
unsigned int bank;
|
||||
struct device *dev;
|
||||
enum access_mode mode;
|
||||
struct clk *clk;
|
||||
|
||||
/* DMA related objects */
|
||||
struct dma_chan *read_dma_chan;
|
||||
struct dma_chan *write_dma_chan;
|
||||
struct completion dma_access_complete;
|
||||
|
||||
struct fsmc_nand_timings *dev_timings;
|
||||
|
||||
dma_addr_t data_pa;
|
||||
void __iomem *data_va;
|
||||
void __iomem *cmd_va;
|
||||
void __iomem *addr_va;
|
||||
void __iomem *regs_va;
|
||||
|
||||
void (*select_chip)(uint32_t bank, uint32_t busw);
|
||||
};
|
||||
|
||||
static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd)
|
||||
{
|
||||
return container_of(mtd_to_nand(mtd), struct fsmc_nand_data, nand);
|
||||
}
|
||||
|
||||
/* Assert CS signal based on chipnr */
|
||||
static void fsmc_select_chip(struct mtd_info *mtd, int chipnr)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fsmc_nand_data *host;
|
||||
|
||||
host = mtd_to_fsmc(mtd);
|
||||
|
||||
switch (chipnr) {
|
||||
case -1:
|
||||
chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
|
||||
break;
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
if (host->select_chip)
|
||||
host->select_chip(chipnr,
|
||||
chip->options & NAND_BUSWIDTH_16);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(host->dev, "unsupported chip-select %d\n", chipnr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* fsmc_cmd_ctrl - For facilitaing Hardware access
|
||||
* This routine allows hardware specific access to control-lines(ALE,CLE)
|
||||
@ -838,44 +769,46 @@ static bool filter(struct dma_chan *chan, void *slave)
|
||||
}
|
||||
|
||||
static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
|
||||
struct device_node *np)
|
||||
struct fsmc_nand_data *host,
|
||||
struct nand_chip *nand)
|
||||
{
|
||||
struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* Set default NAND width to 8 bits */
|
||||
pdata->width = 8;
|
||||
nand->options = 0;
|
||||
|
||||
if (!of_property_read_u32(np, "bank-width", &val)) {
|
||||
if (val == 2) {
|
||||
pdata->width = 16;
|
||||
nand->options |= NAND_BUSWIDTH_16;
|
||||
} else if (val != 1) {
|
||||
dev_err(&pdev->dev, "invalid bank-width %u\n", val);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (of_get_property(np, "nand-skip-bbtscan", NULL))
|
||||
pdata->options = NAND_SKIP_BBTSCAN;
|
||||
|
||||
pdata->nand_timings = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*pdata->nand_timings), GFP_KERNEL);
|
||||
if (!pdata->nand_timings)
|
||||
if (of_get_property(np, "nand-skip-bbtscan", NULL))
|
||||
nand->options |= NAND_SKIP_BBTSCAN;
|
||||
|
||||
host->dev_timings = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*host->dev_timings), GFP_KERNEL);
|
||||
if (!host->dev_timings)
|
||||
return -ENOMEM;
|
||||
ret = of_property_read_u8_array(np, "timings", (u8 *)pdata->nand_timings,
|
||||
sizeof(*pdata->nand_timings));
|
||||
ret = of_property_read_u8_array(np, "timings", (u8 *)host->dev_timings,
|
||||
sizeof(*host->dev_timings));
|
||||
if (ret) {
|
||||
dev_info(&pdev->dev, "No timings in dts specified, using default timings!\n");
|
||||
pdata->nand_timings = NULL;
|
||||
host->dev_timings = NULL;
|
||||
}
|
||||
|
||||
/* Set default NAND bank to 0 */
|
||||
pdata->bank = 0;
|
||||
host->bank = 0;
|
||||
if (!of_property_read_u32(np, "bank", &val)) {
|
||||
if (val > 3) {
|
||||
dev_err(&pdev->dev, "invalid bank %u\n", val);
|
||||
return -EINVAL;
|
||||
}
|
||||
pdata->bank = val;
|
||||
host->bank = val;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -886,8 +819,6 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
|
||||
*/
|
||||
static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct device_node __maybe_unused *np = pdev->dev.of_node;
|
||||
struct fsmc_nand_data *host;
|
||||
struct mtd_info *mtd;
|
||||
struct nand_chip *nand;
|
||||
@ -897,22 +828,17 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||
u32 pid;
|
||||
int i;
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
pdev->dev.platform_data = pdata;
|
||||
ret = fsmc_nand_probe_config_dt(pdev, np);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "no platform data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Allocate memory for the device structure (and zero it) */
|
||||
host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
|
||||
nand = &host->nand;
|
||||
|
||||
ret = fsmc_nand_probe_config_dt(pdev, host, nand);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
|
||||
host->data_va = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(host->data_va))
|
||||
@ -935,7 +861,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(host->regs_va))
|
||||
return PTR_ERR(host->regs_va);
|
||||
|
||||
host->clk = clk_get(&pdev->dev, NULL);
|
||||
host->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(host->clk)) {
|
||||
dev_err(&pdev->dev, "failed to fetch block clock\n");
|
||||
return PTR_ERR(host->clk);
|
||||
@ -943,7 +869,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||
|
||||
ret = clk_prepare_enable(host->clk);
|
||||
if (ret)
|
||||
goto err_clk_prepare_enable;
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* This device ID is actually a common AMBA ID as used on the
|
||||
@ -957,22 +883,15 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||
AMBA_PART_BITS(pid), AMBA_MANF_BITS(pid),
|
||||
AMBA_REV_BITS(pid), AMBA_CONFIG_BITS(pid));
|
||||
|
||||
host->bank = pdata->bank;
|
||||
host->select_chip = pdata->select_bank;
|
||||
host->partitions = pdata->partitions;
|
||||
host->nr_partitions = pdata->nr_partitions;
|
||||
host->dev = &pdev->dev;
|
||||
host->dev_timings = pdata->nand_timings;
|
||||
host->mode = pdata->mode;
|
||||
|
||||
if (host->mode == USE_DMA_ACCESS)
|
||||
init_completion(&host->dma_access_complete);
|
||||
|
||||
/* Link all private pointers */
|
||||
mtd = nand_to_mtd(&host->nand);
|
||||
nand = &host->nand;
|
||||
nand_set_controller_data(nand, host);
|
||||
nand_set_flash_node(nand, np);
|
||||
nand_set_flash_node(nand, pdev->dev.of_node);
|
||||
|
||||
mtd->dev.parent = &pdev->dev;
|
||||
nand->IO_ADDR_R = host->data_va;
|
||||
@ -987,26 +906,18 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||
nand->ecc.mode = NAND_ECC_HW;
|
||||
nand->ecc.hwctl = fsmc_enable_hwecc;
|
||||
nand->ecc.size = 512;
|
||||
nand->options = pdata->options;
|
||||
nand->select_chip = fsmc_select_chip;
|
||||
nand->badblockbits = 7;
|
||||
nand_set_flash_node(nand, np);
|
||||
|
||||
if (pdata->width == FSMC_NAND_BW16)
|
||||
nand->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
switch (host->mode) {
|
||||
case USE_DMA_ACCESS:
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_MEMCPY, mask);
|
||||
host->read_dma_chan = dma_request_channel(mask, filter,
|
||||
pdata->read_dma_priv);
|
||||
host->read_dma_chan = dma_request_channel(mask, filter, NULL);
|
||||
if (!host->read_dma_chan) {
|
||||
dev_err(&pdev->dev, "Unable to get read dma channel\n");
|
||||
goto err_req_read_chnl;
|
||||
}
|
||||
host->write_dma_chan = dma_request_channel(mask, filter,
|
||||
pdata->write_dma_priv);
|
||||
host->write_dma_chan = dma_request_channel(mask, filter, NULL);
|
||||
if (!host->write_dma_chan) {
|
||||
dev_err(&pdev->dev, "Unable to get write dma channel\n");
|
||||
goto err_req_write_chnl;
|
||||
@ -1107,18 +1018,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err_probe;
|
||||
|
||||
/*
|
||||
* The partition information can is accessed by (in the same precedence)
|
||||
*
|
||||
* command line through Bootloader,
|
||||
* platform data,
|
||||
* default partition information present in driver.
|
||||
*/
|
||||
/*
|
||||
* Check for partition info passed
|
||||
*/
|
||||
mtd->name = "nand";
|
||||
ret = mtd_device_register(mtd, host->partitions, host->nr_partitions);
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret)
|
||||
goto err_probe;
|
||||
|
||||
@ -1135,8 +1036,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
||||
dma_release_channel(host->read_dma_chan);
|
||||
err_req_read_chnl:
|
||||
clk_disable_unprepare(host->clk);
|
||||
err_clk_prepare_enable:
|
||||
clk_put(host->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1155,7 +1054,6 @@ static int fsmc_nand_remove(struct platform_device *pdev)
|
||||
dma_release_channel(host->read_dma_chan);
|
||||
}
|
||||
clk_disable_unprepare(host->clk);
|
||||
clk_put(host->clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1185,20 +1083,18 @@ static int fsmc_nand_resume(struct device *dev)
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id fsmc_nand_id_table[] = {
|
||||
{ .compatible = "st,spear600-fsmc-nand" },
|
||||
{ .compatible = "stericsson,fsmc-nand" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsmc_nand_id_table);
|
||||
#endif
|
||||
|
||||
static struct platform_driver fsmc_nand_driver = {
|
||||
.remove = fsmc_nand_remove,
|
||||
.driver = {
|
||||
.name = "fsmc-nand",
|
||||
.of_match_table = of_match_ptr(fsmc_nand_id_table),
|
||||
.of_match_table = fsmc_nand_id_table,
|
||||
.pm = &fsmc_nand_pm_ops,
|
||||
},
|
||||
};
|
||||
|
@ -78,7 +78,9 @@ static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
gpio_set_value(gpiomtd->plat.gpio_nce, !(ctrl & NAND_NCE));
|
||||
if (gpio_is_valid(gpiomtd->plat.gpio_nce))
|
||||
gpio_set_value(gpiomtd->plat.gpio_nce,
|
||||
!(ctrl & NAND_NCE));
|
||||
gpio_set_value(gpiomtd->plat.gpio_cle, !!(ctrl & NAND_CLE));
|
||||
gpio_set_value(gpiomtd->plat.gpio_ale, !!(ctrl & NAND_ALE));
|
||||
gpio_nand_dosync(gpiomtd);
|
||||
@ -201,7 +203,8 @@ static int gpio_nand_remove(struct platform_device *pdev)
|
||||
|
||||
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
|
||||
gpio_set_value(gpiomtd->plat.gpio_nwp, 0);
|
||||
gpio_set_value(gpiomtd->plat.gpio_nce, 1);
|
||||
if (gpio_is_valid(gpiomtd->plat.gpio_nce))
|
||||
gpio_set_value(gpiomtd->plat.gpio_nce, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -239,10 +242,13 @@ static int gpio_nand_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nce, "NAND NCE");
|
||||
if (ret)
|
||||
return ret;
|
||||
gpio_direction_output(gpiomtd->plat.gpio_nce, 1);
|
||||
if (gpio_is_valid(gpiomtd->plat.gpio_nce)) {
|
||||
ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nce,
|
||||
"NAND NCE");
|
||||
if (ret)
|
||||
return ret;
|
||||
gpio_direction_output(gpiomtd->plat.gpio_nce, 1);
|
||||
}
|
||||
|
||||
if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) {
|
||||
ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nwp,
|
||||
|
51
drivers/mtd/nand/nand_amd.c
Normal file
51
drivers/mtd/nand/nand_amd.c
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Free Electrons
|
||||
* Copyright (C) 2017 NextThing Co
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/mtd/nand.h>
|
||||
|
||||
static void amd_nand_decode_id(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
||||
nand_decode_ext_id(chip);
|
||||
|
||||
/*
|
||||
* Check for Spansion/AMD ID + repeating 5th, 6th byte since
|
||||
* some Spansion chips have erasesize that conflicts with size
|
||||
* listed in nand_ids table.
|
||||
* Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
|
||||
*/
|
||||
if (chip->id.data[4] != 0x00 && chip->id.data[5] == 0x00 &&
|
||||
chip->id.data[6] == 0x00 && chip->id.data[7] == 0x00 &&
|
||||
mtd->writesize == 512) {
|
||||
mtd->erasesize = 128 * 1024;
|
||||
mtd->erasesize <<= ((chip->id.data[3] & 0x03) << 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int amd_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
if (nand_is_slc(chip))
|
||||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct nand_manufacturer_ops amd_nand_manuf_ops = {
|
||||
.detect = amd_nand_decode_id,
|
||||
.init = amd_nand_init,
|
||||
};
|
@ -139,6 +139,74 @@ const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops);
|
||||
|
||||
/*
|
||||
* Support the old "large page" layout used for 1-bit Hamming ECC where ECC
|
||||
* are placed at a fixed offset.
|
||||
*/
|
||||
static int nand_ooblayout_ecc_lp_hamming(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
switch (mtd->oobsize) {
|
||||
case 64:
|
||||
oobregion->offset = 40;
|
||||
break;
|
||||
case 128:
|
||||
oobregion->offset = 80;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
oobregion->length = ecc->total;
|
||||
if (oobregion->offset + oobregion->length > mtd->oobsize)
|
||||
return -ERANGE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nand_ooblayout_free_lp_hamming(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *oobregion)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
int ecc_offset = 0;
|
||||
|
||||
if (section < 0 || section > 1)
|
||||
return -ERANGE;
|
||||
|
||||
switch (mtd->oobsize) {
|
||||
case 64:
|
||||
ecc_offset = 40;
|
||||
break;
|
||||
case 128:
|
||||
ecc_offset = 80;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (section == 0) {
|
||||
oobregion->offset = 2;
|
||||
oobregion->length = ecc_offset - 2;
|
||||
} else {
|
||||
oobregion->offset = ecc_offset + ecc->total;
|
||||
oobregion->length = mtd->oobsize - oobregion->offset;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = {
|
||||
.ecc = nand_ooblayout_ecc_lp_hamming,
|
||||
.free = nand_ooblayout_free_lp_hamming,
|
||||
};
|
||||
|
||||
static int check_offs_len(struct mtd_info *mtd,
|
||||
loff_t ofs, uint64_t len)
|
||||
{
|
||||
@ -354,40 +422,32 @@ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||
*/
|
||||
static int nand_block_bad(struct mtd_info *mtd, loff_t ofs)
|
||||
{
|
||||
int page, res = 0, i = 0;
|
||||
int page, page_end, res;
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
u16 bad;
|
||||
u8 bad;
|
||||
|
||||
if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
|
||||
ofs += mtd->erasesize - mtd->writesize;
|
||||
|
||||
page = (int)(ofs >> chip->page_shift) & chip->pagemask;
|
||||
page_end = page + (chip->bbt_options & NAND_BBT_SCAN2NDPAGE ? 2 : 1);
|
||||
|
||||
do {
|
||||
if (chip->options & NAND_BUSWIDTH_16) {
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB,
|
||||
chip->badblockpos & 0xFE, page);
|
||||
bad = cpu_to_le16(chip->read_word(mtd));
|
||||
if (chip->badblockpos & 0x1)
|
||||
bad >>= 8;
|
||||
else
|
||||
bad &= 0xFF;
|
||||
} else {
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos,
|
||||
page);
|
||||
bad = chip->read_byte(mtd);
|
||||
}
|
||||
for (; page < page_end; page++) {
|
||||
res = chip->ecc.read_oob(mtd, chip, page);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
bad = chip->oob_poi[chip->badblockpos];
|
||||
|
||||
if (likely(chip->badblockbits == 8))
|
||||
res = bad != 0xFF;
|
||||
else
|
||||
res = hweight8(bad) < chip->badblockbits;
|
||||
ofs += mtd->writesize;
|
||||
page = (int)(ofs >> chip->page_shift) & chip->pagemask;
|
||||
i++;
|
||||
} while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -676,6 +736,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
|
||||
case NAND_CMD_ERASE2:
|
||||
case NAND_CMD_SEQIN:
|
||||
case NAND_CMD_STATUS:
|
||||
case NAND_CMD_READID:
|
||||
case NAND_CMD_SET_FEATURES:
|
||||
return;
|
||||
|
||||
case NAND_CMD_RESET:
|
||||
@ -794,6 +856,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
|
||||
case NAND_CMD_ERASE2:
|
||||
case NAND_CMD_SEQIN:
|
||||
case NAND_CMD_STATUS:
|
||||
case NAND_CMD_READID:
|
||||
case NAND_CMD_SET_FEATURES:
|
||||
return;
|
||||
|
||||
case NAND_CMD_RNDIN:
|
||||
@ -1958,7 +2022,9 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
||||
if (!aligned)
|
||||
use_bufpoi = 1;
|
||||
else if (chip->options & NAND_USE_BOUNCE_BUFFER)
|
||||
use_bufpoi = !virt_addr_valid(buf);
|
||||
use_bufpoi = !virt_addr_valid(buf) ||
|
||||
!IS_ALIGNED((unsigned long)buf,
|
||||
chip->buf_align);
|
||||
else
|
||||
use_bufpoi = 0;
|
||||
|
||||
@ -1997,8 +2063,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
||||
break;
|
||||
}
|
||||
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, ret);
|
||||
|
||||
/* Transfer not aligned data */
|
||||
if (use_bufpoi) {
|
||||
if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
|
||||
@ -2049,6 +2113,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
||||
}
|
||||
|
||||
buf += bytes;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, ret);
|
||||
} else {
|
||||
memcpy(buf, chip->buffers->databuf + col, bytes);
|
||||
buf += bytes;
|
||||
@ -2637,7 +2702,7 @@ static int nand_write_page_syndrome(struct mtd_info *mtd,
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_write_page - [REPLACEABLE] write one page
|
||||
* nand_write_page - write one page
|
||||
* @mtd: MTD device structure
|
||||
* @chip: NAND chip descriptor
|
||||
* @offset: address offset within the page
|
||||
@ -2815,7 +2880,9 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
||||
if (part_pagewr)
|
||||
use_bufpoi = 1;
|
||||
else if (chip->options & NAND_USE_BOUNCE_BUFFER)
|
||||
use_bufpoi = !virt_addr_valid(buf);
|
||||
use_bufpoi = !virt_addr_valid(buf) ||
|
||||
!IS_ALIGNED((unsigned long)buf,
|
||||
chip->buf_align);
|
||||
else
|
||||
use_bufpoi = 0;
|
||||
|
||||
@ -2840,9 +2907,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
||||
/* We still need to erase leftover OOB data */
|
||||
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
||||
}
|
||||
ret = chip->write_page(mtd, chip, column, bytes, wbuf,
|
||||
oob_required, page, cached,
|
||||
(ops->mode == MTD_OPS_RAW));
|
||||
|
||||
ret = nand_write_page(mtd, chip, column, bytes, wbuf,
|
||||
oob_required, page, cached,
|
||||
(ops->mode == MTD_OPS_RAW));
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
@ -3385,8 +3453,10 @@ static void nand_shutdown(struct mtd_info *mtd)
|
||||
}
|
||||
|
||||
/* Set default functions */
|
||||
static void nand_set_defaults(struct nand_chip *chip, int busw)
|
||||
static void nand_set_defaults(struct nand_chip *chip)
|
||||
{
|
||||
unsigned int busw = chip->options & NAND_BUSWIDTH_16;
|
||||
|
||||
/* check for proper chip_delay setup, set 20us if not */
|
||||
if (!chip->chip_delay)
|
||||
chip->chip_delay = 20;
|
||||
@ -3431,6 +3501,8 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
|
||||
nand_hw_control_init(chip->controller);
|
||||
}
|
||||
|
||||
if (!chip->buf_align)
|
||||
chip->buf_align = 1;
|
||||
}
|
||||
|
||||
/* Sanitize ONFI strings so we can safely print them */
|
||||
@ -3464,9 +3536,10 @@ static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
|
||||
}
|
||||
|
||||
/* Parse the Extended Parameter Page. */
|
||||
static int nand_flash_detect_ext_param_page(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, struct nand_onfi_params *p)
|
||||
static int nand_flash_detect_ext_param_page(struct nand_chip *chip,
|
||||
struct nand_onfi_params *p)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct onfi_ext_param_page *ep;
|
||||
struct onfi_ext_section *s;
|
||||
struct onfi_ext_ecc_info *ecc;
|
||||
@ -3534,36 +3607,12 @@ static int nand_flash_detect_ext_param_page(struct mtd_info *mtd,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nand_setup_read_retry_micron(struct mtd_info *mtd, int retry_mode)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode};
|
||||
|
||||
return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY,
|
||||
feature);
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure chip properties from Micron vendor-specific ONFI table
|
||||
*/
|
||||
static void nand_onfi_detect_micron(struct nand_chip *chip,
|
||||
struct nand_onfi_params *p)
|
||||
{
|
||||
struct nand_onfi_vendor_micron *micron = (void *)p->vendor;
|
||||
|
||||
if (le16_to_cpu(p->vendor_revision) < 1)
|
||||
return;
|
||||
|
||||
chip->read_retries = micron->read_retry_options;
|
||||
chip->setup_read_retry = nand_setup_read_retry_micron;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
|
||||
*/
|
||||
static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int *busw)
|
||||
static int nand_flash_detect_onfi(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_onfi_params *p = &chip->onfi_params;
|
||||
int i, j;
|
||||
int val;
|
||||
@ -3633,9 +3682,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
chip->blocks_per_die = le32_to_cpu(p->blocks_per_lun);
|
||||
|
||||
if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS)
|
||||
*busw = NAND_BUSWIDTH_16;
|
||||
else
|
||||
*busw = 0;
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
if (p->ecc_bits != 0xff) {
|
||||
chip->ecc_strength_ds = p->ecc_bits;
|
||||
@ -3653,24 +3700,21 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
chip->cmdfunc = nand_command_lp;
|
||||
|
||||
/* The Extended Parameter Page is supported since ONFI 2.1. */
|
||||
if (nand_flash_detect_ext_param_page(mtd, chip, p))
|
||||
if (nand_flash_detect_ext_param_page(chip, p))
|
||||
pr_warn("Failed to detect ONFI extended param page\n");
|
||||
} else {
|
||||
pr_warn("Could not retrieve ONFI ECC requirements\n");
|
||||
}
|
||||
|
||||
if (p->jedec_id == NAND_MFR_MICRON)
|
||||
nand_onfi_detect_micron(chip, p);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise.
|
||||
*/
|
||||
static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int *busw)
|
||||
static int nand_flash_detect_jedec(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct nand_jedec_params *p = &chip->jedec_params;
|
||||
struct jedec_ecc_info *ecc;
|
||||
int val;
|
||||
@ -3729,9 +3773,7 @@ static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
chip->bits_per_cell = p->bits_per_cell;
|
||||
|
||||
if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS)
|
||||
*busw = NAND_BUSWIDTH_16;
|
||||
else
|
||||
*busw = 0;
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
/* ECC info */
|
||||
ecc = &p->ecc_info[0];
|
||||
@ -3820,165 +3862,46 @@ static int nand_get_bits_per_cell(u8 cellinfo)
|
||||
* chip. The rest of the parameters must be decoded according to generic or
|
||||
* manufacturer-specific "extended ID" decoding patterns.
|
||||
*/
|
||||
static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
u8 id_data[8], int *busw)
|
||||
void nand_decode_ext_id(struct nand_chip *chip)
|
||||
{
|
||||
int extid, id_len;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int extid;
|
||||
u8 *id_data = chip->id.data;
|
||||
/* The 3rd id byte holds MLC / multichip data */
|
||||
chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
|
||||
/* The 4th id byte is the important one */
|
||||
extid = id_data[3];
|
||||
|
||||
id_len = nand_id_len(id_data, 8);
|
||||
|
||||
/*
|
||||
* Field definitions are in the following datasheets:
|
||||
* Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
|
||||
* New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44)
|
||||
* Hynix MLC (6 byte ID): Hynix H27UBG8T2B (p.22)
|
||||
*
|
||||
* Check for ID length, non-zero 6th byte, cell type, and Hynix/Samsung
|
||||
* ID to decide what to do.
|
||||
*/
|
||||
if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG &&
|
||||
!nand_is_slc(chip) && id_data[5] != 0x00) {
|
||||
/* Calc pagesize */
|
||||
mtd->writesize = 2048 << (extid & 0x03);
|
||||
extid >>= 2;
|
||||
/* Calc oobsize */
|
||||
switch (((extid >> 2) & 0x04) | (extid & 0x03)) {
|
||||
case 1:
|
||||
mtd->oobsize = 128;
|
||||
break;
|
||||
case 2:
|
||||
mtd->oobsize = 218;
|
||||
break;
|
||||
case 3:
|
||||
mtd->oobsize = 400;
|
||||
break;
|
||||
case 4:
|
||||
mtd->oobsize = 436;
|
||||
break;
|
||||
case 5:
|
||||
mtd->oobsize = 512;
|
||||
break;
|
||||
case 6:
|
||||
mtd->oobsize = 640;
|
||||
break;
|
||||
case 7:
|
||||
default: /* Other cases are "reserved" (unknown) */
|
||||
mtd->oobsize = 1024;
|
||||
break;
|
||||
}
|
||||
extid >>= 2;
|
||||
/* Calc blocksize */
|
||||
mtd->erasesize = (128 * 1024) <<
|
||||
(((extid >> 1) & 0x04) | (extid & 0x03));
|
||||
*busw = 0;
|
||||
} else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX &&
|
||||
!nand_is_slc(chip)) {
|
||||
unsigned int tmp;
|
||||
|
||||
/* Calc pagesize */
|
||||
mtd->writesize = 2048 << (extid & 0x03);
|
||||
extid >>= 2;
|
||||
/* Calc oobsize */
|
||||
switch (((extid >> 2) & 0x04) | (extid & 0x03)) {
|
||||
case 0:
|
||||
mtd->oobsize = 128;
|
||||
break;
|
||||
case 1:
|
||||
mtd->oobsize = 224;
|
||||
break;
|
||||
case 2:
|
||||
mtd->oobsize = 448;
|
||||
break;
|
||||
case 3:
|
||||
mtd->oobsize = 64;
|
||||
break;
|
||||
case 4:
|
||||
mtd->oobsize = 32;
|
||||
break;
|
||||
case 5:
|
||||
mtd->oobsize = 16;
|
||||
break;
|
||||
default:
|
||||
mtd->oobsize = 640;
|
||||
break;
|
||||
}
|
||||
extid >>= 2;
|
||||
/* Calc blocksize */
|
||||
tmp = ((extid >> 1) & 0x04) | (extid & 0x03);
|
||||
if (tmp < 0x03)
|
||||
mtd->erasesize = (128 * 1024) << tmp;
|
||||
else if (tmp == 0x03)
|
||||
mtd->erasesize = 768 * 1024;
|
||||
else
|
||||
mtd->erasesize = (64 * 1024) << tmp;
|
||||
*busw = 0;
|
||||
} else {
|
||||
/* Calc pagesize */
|
||||
mtd->writesize = 1024 << (extid & 0x03);
|
||||
extid >>= 2;
|
||||
/* Calc oobsize */
|
||||
mtd->oobsize = (8 << (extid & 0x01)) *
|
||||
(mtd->writesize >> 9);
|
||||
extid >>= 2;
|
||||
/* Calc blocksize. Blocksize is multiples of 64KiB */
|
||||
mtd->erasesize = (64 * 1024) << (extid & 0x03);
|
||||
extid >>= 2;
|
||||
/* Get buswidth information */
|
||||
*busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
|
||||
|
||||
/*
|
||||
* Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per
|
||||
* 512B page. For Toshiba SLC, we decode the 5th/6th byte as
|
||||
* follows:
|
||||
* - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm,
|
||||
* 110b -> 24nm
|
||||
* - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC
|
||||
*/
|
||||
if (id_len >= 6 && id_data[0] == NAND_MFR_TOSHIBA &&
|
||||
nand_is_slc(chip) &&
|
||||
(id_data[5] & 0x7) == 0x6 /* 24nm */ &&
|
||||
!(id_data[4] & 0x80) /* !BENAND */) {
|
||||
mtd->oobsize = 32 * mtd->writesize >> 9;
|
||||
}
|
||||
|
||||
}
|
||||
/* Calc pagesize */
|
||||
mtd->writesize = 1024 << (extid & 0x03);
|
||||
extid >>= 2;
|
||||
/* Calc oobsize */
|
||||
mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
|
||||
extid >>= 2;
|
||||
/* Calc blocksize. Blocksize is multiples of 64KiB */
|
||||
mtd->erasesize = (64 * 1024) << (extid & 0x03);
|
||||
extid >>= 2;
|
||||
/* Get buswidth information */
|
||||
if (extid & 0x1)
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_decode_ext_id);
|
||||
|
||||
/*
|
||||
* Old devices have chip data hardcoded in the device ID table. nand_decode_id
|
||||
* decodes a matching ID table entry and assigns the MTD size parameters for
|
||||
* the chip.
|
||||
*/
|
||||
static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
struct nand_flash_dev *type, u8 id_data[8],
|
||||
int *busw)
|
||||
static void nand_decode_id(struct nand_chip *chip, struct nand_flash_dev *type)
|
||||
{
|
||||
int maf_id = id_data[0];
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
||||
mtd->erasesize = type->erasesize;
|
||||
mtd->writesize = type->pagesize;
|
||||
mtd->oobsize = mtd->writesize / 32;
|
||||
*busw = type->options & NAND_BUSWIDTH_16;
|
||||
|
||||
/* All legacy ID NAND are small-page, SLC */
|
||||
chip->bits_per_cell = 1;
|
||||
|
||||
/*
|
||||
* Check for Spansion/AMD ID + repeating 5th, 6th byte since
|
||||
* some Spansion chips have erasesize that conflicts with size
|
||||
* listed in nand_ids table.
|
||||
* Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
|
||||
*/
|
||||
if (maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00
|
||||
&& id_data[6] == 0x00 && id_data[7] == 0x00
|
||||
&& mtd->writesize == 512) {
|
||||
mtd->erasesize = 128 * 1024;
|
||||
mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3986,36 +3909,15 @@ static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
* heuristic patterns using various detected parameters (e.g., manufacturer,
|
||||
* page size, cell-type information).
|
||||
*/
|
||||
static void nand_decode_bbm_options(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, u8 id_data[8])
|
||||
static void nand_decode_bbm_options(struct nand_chip *chip)
|
||||
{
|
||||
int maf_id = id_data[0];
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
||||
/* Set the bad block position */
|
||||
if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16))
|
||||
chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
|
||||
else
|
||||
chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
|
||||
|
||||
/*
|
||||
* Bad block marker is stored in the last page of each block on Samsung
|
||||
* and Hynix MLC devices; stored in first two pages of each block on
|
||||
* Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba,
|
||||
* AMD/Spansion, and Macronix. All others scan only the first page.
|
||||
*/
|
||||
if (!nand_is_slc(chip) &&
|
||||
(maf_id == NAND_MFR_SAMSUNG ||
|
||||
maf_id == NAND_MFR_HYNIX))
|
||||
chip->bbt_options |= NAND_BBT_SCANLASTPAGE;
|
||||
else if ((nand_is_slc(chip) &&
|
||||
(maf_id == NAND_MFR_SAMSUNG ||
|
||||
maf_id == NAND_MFR_HYNIX ||
|
||||
maf_id == NAND_MFR_TOSHIBA ||
|
||||
maf_id == NAND_MFR_AMD ||
|
||||
maf_id == NAND_MFR_MACRONIX)) ||
|
||||
(mtd->writesize == 2048 &&
|
||||
maf_id == NAND_MFR_MICRON))
|
||||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
}
|
||||
|
||||
static inline bool is_full_id_nand(struct nand_flash_dev *type)
|
||||
@ -4023,9 +3925,12 @@ static inline bool is_full_id_nand(struct nand_flash_dev *type)
|
||||
return type->id_len;
|
||||
}
|
||||
|
||||
static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
struct nand_flash_dev *type, u8 *id_data, int *busw)
|
||||
static bool find_full_id_nand(struct nand_chip *chip,
|
||||
struct nand_flash_dev *type)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
u8 *id_data = chip->id.data;
|
||||
|
||||
if (!strncmp(type->id, id_data, type->id_len)) {
|
||||
mtd->writesize = type->pagesize;
|
||||
mtd->erasesize = type->erasesize;
|
||||
@ -4039,8 +3944,6 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
chip->onfi_timing_mode_default =
|
||||
type->onfi_timing_mode_default;
|
||||
|
||||
*busw = type->options & NAND_BUSWIDTH_16;
|
||||
|
||||
if (!mtd->name)
|
||||
mtd->name = type->name;
|
||||
|
||||
@ -4049,16 +3952,64 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Manufacturer detection. Only used when the NAND is not ONFI or JEDEC
|
||||
* compliant and does not have a full-id or legacy-id entry in the nand_ids
|
||||
* table.
|
||||
*/
|
||||
static void nand_manufacturer_detect(struct nand_chip *chip)
|
||||
{
|
||||
/*
|
||||
* Try manufacturer detection if available and use
|
||||
* nand_decode_ext_id() otherwise.
|
||||
*/
|
||||
if (chip->manufacturer.desc && chip->manufacturer.desc->ops &&
|
||||
chip->manufacturer.desc->ops->detect)
|
||||
chip->manufacturer.desc->ops->detect(chip);
|
||||
else
|
||||
nand_decode_ext_id(chip);
|
||||
}
|
||||
|
||||
/*
|
||||
* Manufacturer initialization. This function is called for all NANDs including
|
||||
* ONFI and JEDEC compliant ones.
|
||||
* Manufacturer drivers should put all their specific initialization code in
|
||||
* their ->init() hook.
|
||||
*/
|
||||
static int nand_manufacturer_init(struct nand_chip *chip)
|
||||
{
|
||||
if (!chip->manufacturer.desc || !chip->manufacturer.desc->ops ||
|
||||
!chip->manufacturer.desc->ops->init)
|
||||
return 0;
|
||||
|
||||
return chip->manufacturer.desc->ops->init(chip);
|
||||
}
|
||||
|
||||
/*
|
||||
* Manufacturer cleanup. This function is called for all NANDs including
|
||||
* ONFI and JEDEC compliant ones.
|
||||
* Manufacturer drivers should put all their specific cleanup code in their
|
||||
* ->cleanup() hook.
|
||||
*/
|
||||
static void nand_manufacturer_cleanup(struct nand_chip *chip)
|
||||
{
|
||||
/* Release manufacturer private data */
|
||||
if (chip->manufacturer.desc && chip->manufacturer.desc->ops &&
|
||||
chip->manufacturer.desc->ops->cleanup)
|
||||
chip->manufacturer.desc->ops->cleanup(chip);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the flash and manufacturer id and lookup if the type is supported.
|
||||
*/
|
||||
static int nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int *maf_id, int *dev_id,
|
||||
struct nand_flash_dev *type)
|
||||
static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
|
||||
{
|
||||
const struct nand_manufacturer *manufacturer;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int busw;
|
||||
int i, maf_idx;
|
||||
u8 id_data[8];
|
||||
int i, ret;
|
||||
u8 *id_data = chip->id.data;
|
||||
u8 maf_id, dev_id;
|
||||
|
||||
/*
|
||||
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
|
||||
@ -4073,8 +4024,8 @@ static int nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
||||
|
||||
/* Read manufacturer and device IDs */
|
||||
*maf_id = chip->read_byte(mtd);
|
||||
*dev_id = chip->read_byte(mtd);
|
||||
maf_id = chip->read_byte(mtd);
|
||||
dev_id = chip->read_byte(mtd);
|
||||
|
||||
/*
|
||||
* Try again to make sure, as some systems the bus-hold or other
|
||||
@ -4089,20 +4040,41 @@ static int nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
for (i = 0; i < 8; i++)
|
||||
id_data[i] = chip->read_byte(mtd);
|
||||
|
||||
if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
|
||||
if (id_data[0] != maf_id || id_data[1] != dev_id) {
|
||||
pr_info("second ID read did not match %02x,%02x against %02x,%02x\n",
|
||||
*maf_id, *dev_id, id_data[0], id_data[1]);
|
||||
maf_id, dev_id, id_data[0], id_data[1]);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
chip->id.len = nand_id_len(id_data, 8);
|
||||
|
||||
/* Try to identify manufacturer */
|
||||
manufacturer = nand_get_manufacturer(maf_id);
|
||||
chip->manufacturer.desc = manufacturer;
|
||||
|
||||
if (!type)
|
||||
type = nand_flash_ids;
|
||||
|
||||
/*
|
||||
* Save the NAND_BUSWIDTH_16 flag before letting auto-detection logic
|
||||
* override it.
|
||||
* This is required to make sure initial NAND bus width set by the
|
||||
* NAND controller driver is coherent with the real NAND bus width
|
||||
* (extracted by auto-detection code).
|
||||
*/
|
||||
busw = chip->options & NAND_BUSWIDTH_16;
|
||||
|
||||
/*
|
||||
* The flag is only set (never cleared), reset it to its default value
|
||||
* before starting auto-detection.
|
||||
*/
|
||||
chip->options &= ~NAND_BUSWIDTH_16;
|
||||
|
||||
for (; type->name != NULL; type++) {
|
||||
if (is_full_id_nand(type)) {
|
||||
if (find_full_id_nand(mtd, chip, type, id_data, &busw))
|
||||
if (find_full_id_nand(chip, type))
|
||||
goto ident_done;
|
||||
} else if (*dev_id == type->dev_id) {
|
||||
} else if (dev_id == type->dev_id) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -4110,11 +4082,11 @@ static int nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
chip->onfi_version = 0;
|
||||
if (!type->name || !type->pagesize) {
|
||||
/* Check if the chip is ONFI compliant */
|
||||
if (nand_flash_detect_onfi(mtd, chip, &busw))
|
||||
if (nand_flash_detect_onfi(chip))
|
||||
goto ident_done;
|
||||
|
||||
/* Check if the chip is JEDEC compliant */
|
||||
if (nand_flash_detect_jedec(mtd, chip, &busw))
|
||||
if (nand_flash_detect_jedec(chip))
|
||||
goto ident_done;
|
||||
}
|
||||
|
||||
@ -4126,48 +4098,34 @@ static int nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
|
||||
chip->chipsize = (uint64_t)type->chipsize << 20;
|
||||
|
||||
if (!type->pagesize) {
|
||||
/* Decode parameters from extended ID */
|
||||
nand_decode_ext_id(mtd, chip, id_data, &busw);
|
||||
} else {
|
||||
nand_decode_id(mtd, chip, type, id_data, &busw);
|
||||
}
|
||||
if (!type->pagesize)
|
||||
nand_manufacturer_detect(chip);
|
||||
else
|
||||
nand_decode_id(chip, type);
|
||||
|
||||
/* Get chip options */
|
||||
chip->options |= type->options;
|
||||
|
||||
/*
|
||||
* Check if chip is not a Samsung device. Do not clear the
|
||||
* options for chips which do not have an extended id.
|
||||
*/
|
||||
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
|
||||
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
|
||||
ident_done:
|
||||
|
||||
/* Try to identify manufacturer */
|
||||
for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
|
||||
if (nand_manuf_ids[maf_idx].id == *maf_id)
|
||||
break;
|
||||
}
|
||||
|
||||
if (chip->options & NAND_BUSWIDTH_AUTO) {
|
||||
WARN_ON(chip->options & NAND_BUSWIDTH_16);
|
||||
chip->options |= busw;
|
||||
nand_set_defaults(chip, busw);
|
||||
WARN_ON(busw & NAND_BUSWIDTH_16);
|
||||
nand_set_defaults(chip);
|
||||
} else if (busw != (chip->options & NAND_BUSWIDTH_16)) {
|
||||
/*
|
||||
* Check, if buswidth is correct. Hardware drivers should set
|
||||
* chip correct!
|
||||
*/
|
||||
pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
|
||||
*maf_id, *dev_id);
|
||||
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, mtd->name);
|
||||
pr_warn("bus width %d instead %d bit\n",
|
||||
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
|
||||
busw ? 16 : 8);
|
||||
maf_id, dev_id);
|
||||
pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
|
||||
mtd->name);
|
||||
pr_warn("bus width %d instead of %d bits\n", busw ? 16 : 8,
|
||||
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
nand_decode_bbm_options(mtd, chip, id_data);
|
||||
nand_decode_bbm_options(chip);
|
||||
|
||||
/* Calculate the address shift from the page size */
|
||||
chip->page_shift = ffs(mtd->writesize) - 1;
|
||||
@ -4190,18 +4148,22 @@ static int nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
|
||||
chip->cmdfunc = nand_command_lp;
|
||||
|
||||
ret = nand_manufacturer_init(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
|
||||
*maf_id, *dev_id);
|
||||
maf_id, dev_id);
|
||||
|
||||
if (chip->onfi_version)
|
||||
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
|
||||
chip->onfi_params.model);
|
||||
pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
|
||||
chip->onfi_params.model);
|
||||
else if (chip->jedec_version)
|
||||
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
|
||||
chip->jedec_params.model);
|
||||
pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
|
||||
chip->jedec_params.model);
|
||||
else
|
||||
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
|
||||
type->name);
|
||||
pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
|
||||
type->name);
|
||||
|
||||
pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
|
||||
(int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
|
||||
@ -4333,12 +4295,6 @@ static int nand_dt_init(struct nand_chip *chip)
|
||||
ecc_strength = of_get_nand_ecc_strength(dn);
|
||||
ecc_step = of_get_nand_ecc_step_size(dn);
|
||||
|
||||
if ((ecc_step >= 0 && !(ecc_strength >= 0)) ||
|
||||
(!(ecc_step >= 0) && ecc_strength >= 0)) {
|
||||
pr_err("must set both strength and step size in DT\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ecc_mode >= 0)
|
||||
chip->ecc.mode = ecc_mode;
|
||||
|
||||
@ -4391,10 +4347,10 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Set the default functions */
|
||||
nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
|
||||
nand_set_defaults(chip);
|
||||
|
||||
/* Read the flash type */
|
||||
ret = nand_get_flash_type(mtd, chip, &nand_maf_id, &nand_dev_id, table);
|
||||
ret = nand_detect(chip, table);
|
||||
if (ret) {
|
||||
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
|
||||
pr_warn("No NAND device found\n");
|
||||
@ -4419,6 +4375,9 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nand_maf_id = chip->id.data[0];
|
||||
nand_dev_id = chip->id.data[1];
|
||||
|
||||
chip->select_chip(mtd, -1);
|
||||
|
||||
/* Check for a chip array */
|
||||
@ -4610,7 +4569,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
struct nand_buffers *nbuf;
|
||||
struct nand_buffers *nbuf = NULL;
|
||||
int ret;
|
||||
|
||||
/* New bad blocks should be marked in OOB, flash-based BBT, or both */
|
||||
@ -4624,13 +4583,28 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
}
|
||||
|
||||
if (!(chip->options & NAND_OWN_BUFFERS)) {
|
||||
nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
|
||||
+ mtd->oobsize * 3, GFP_KERNEL);
|
||||
nbuf = kzalloc(sizeof(*nbuf), GFP_KERNEL);
|
||||
if (!nbuf)
|
||||
return -ENOMEM;
|
||||
nbuf->ecccalc = (uint8_t *)(nbuf + 1);
|
||||
nbuf->ecccode = nbuf->ecccalc + mtd->oobsize;
|
||||
nbuf->databuf = nbuf->ecccode + mtd->oobsize;
|
||||
|
||||
nbuf->ecccalc = kmalloc(mtd->oobsize, GFP_KERNEL);
|
||||
if (!nbuf->ecccalc) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
nbuf->ecccode = kmalloc(mtd->oobsize, GFP_KERNEL);
|
||||
if (!nbuf->ecccode) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
nbuf->databuf = kmalloc(mtd->writesize + mtd->oobsize,
|
||||
GFP_KERNEL);
|
||||
if (!nbuf->databuf) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
chip->buffers = nbuf;
|
||||
} else {
|
||||
@ -4653,7 +4627,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
break;
|
||||
case 64:
|
||||
case 128:
|
||||
mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
|
||||
mtd_set_ooblayout(mtd, &nand_ooblayout_lp_hamming_ops);
|
||||
break;
|
||||
default:
|
||||
WARN(1, "No oob scheme defined for oobsize %d\n",
|
||||
@ -4663,9 +4637,6 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
}
|
||||
}
|
||||
|
||||
if (!chip->write_page)
|
||||
chip->write_page = nand_write_page;
|
||||
|
||||
/*
|
||||
* Check ECC mode, default to software if 3byte/512byte hardware ECC is
|
||||
* selected and we have 256 byte pagesize fallback to software ECC
|
||||
@ -4873,8 +4844,12 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
/* Build bad block table */
|
||||
return chip->scan_bbt(mtd);
|
||||
err_free:
|
||||
if (!(chip->options & NAND_OWN_BUFFERS))
|
||||
kfree(chip->buffers);
|
||||
if (nbuf) {
|
||||
kfree(nbuf->databuf);
|
||||
kfree(nbuf->ecccode);
|
||||
kfree(nbuf->ecccalc);
|
||||
kfree(nbuf);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_scan_tail);
|
||||
@ -4925,13 +4900,20 @@ void nand_cleanup(struct nand_chip *chip)
|
||||
|
||||
/* Free bad block table memory */
|
||||
kfree(chip->bbt);
|
||||
if (!(chip->options & NAND_OWN_BUFFERS))
|
||||
if (!(chip->options & NAND_OWN_BUFFERS) && chip->buffers) {
|
||||
kfree(chip->buffers->databuf);
|
||||
kfree(chip->buffers->ecccode);
|
||||
kfree(chip->buffers->ecccalc);
|
||||
kfree(chip->buffers);
|
||||
}
|
||||
|
||||
/* Free bad block descriptor memory */
|
||||
if (chip->badblock_pattern && chip->badblock_pattern->options
|
||||
& NAND_BBT_DYNAMICSTRUCT)
|
||||
kfree(chip->badblock_pattern);
|
||||
|
||||
/* Free manufacturer priv data. */
|
||||
nand_manufacturer_cleanup(chip);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_cleanup);
|
||||
|
||||
|
631
drivers/mtd/nand/nand_hynix.c
Normal file
631
drivers/mtd/nand/nand_hynix.c
Normal file
@ -0,0 +1,631 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Free Electrons
|
||||
* Copyright (C) 2017 NextThing Co
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define NAND_HYNIX_CMD_SET_PARAMS 0x36
|
||||
#define NAND_HYNIX_CMD_APPLY_PARAMS 0x16
|
||||
|
||||
#define NAND_HYNIX_1XNM_RR_REPEAT 8
|
||||
|
||||
/**
|
||||
* struct hynix_read_retry - read-retry data
|
||||
* @nregs: number of register to set when applying a new read-retry mode
|
||||
* @regs: register offsets (NAND chip dependent)
|
||||
* @values: array of values to set in registers. The array size is equal to
|
||||
* (nregs * nmodes)
|
||||
*/
|
||||
struct hynix_read_retry {
|
||||
int nregs;
|
||||
const u8 *regs;
|
||||
u8 values[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct hynix_nand - private Hynix NAND struct
|
||||
* @nand_technology: manufacturing process expressed in picometer
|
||||
* @read_retry: read-retry information
|
||||
*/
|
||||
struct hynix_nand {
|
||||
const struct hynix_read_retry *read_retry;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct hynix_read_retry_otp - structure describing how the read-retry OTP
|
||||
* area
|
||||
* @nregs: number of hynix private registers to set before reading the reading
|
||||
* the OTP area
|
||||
* @regs: registers that should be configured
|
||||
* @values: values that should be set in regs
|
||||
* @page: the address to pass to the READ_PAGE command. Depends on the NAND
|
||||
* chip
|
||||
* @size: size of the read-retry OTP section
|
||||
*/
|
||||
struct hynix_read_retry_otp {
|
||||
int nregs;
|
||||
const u8 *regs;
|
||||
const u8 *values;
|
||||
int page;
|
||||
int size;
|
||||
};
|
||||
|
||||
static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
u8 jedecid[6] = { };
|
||||
int i = 0;
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x40, -1);
|
||||
for (i = 0; i < 5; i++)
|
||||
jedecid[i] = chip->read_byte(mtd);
|
||||
|
||||
return !strcmp("JEDEC", jedecid);
|
||||
}
|
||||
|
||||
static int hynix_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct hynix_nand *hynix = nand_get_manufacturer_data(chip);
|
||||
const u8 *values;
|
||||
int status;
|
||||
int i;
|
||||
|
||||
values = hynix->read_retry->values +
|
||||
(retry_mode * hynix->read_retry->nregs);
|
||||
|
||||
/* Enter 'Set Hynix Parameters' mode */
|
||||
chip->cmdfunc(mtd, NAND_HYNIX_CMD_SET_PARAMS, -1, -1);
|
||||
|
||||
/*
|
||||
* Configure the NAND in the requested read-retry mode.
|
||||
* This is done by setting pre-defined values in internal NAND
|
||||
* registers.
|
||||
*
|
||||
* The set of registers is NAND specific, and the values are either
|
||||
* predefined or extracted from an OTP area on the NAND (values are
|
||||
* probably tweaked at production in this case).
|
||||
*/
|
||||
for (i = 0; i < hynix->read_retry->nregs; i++) {
|
||||
int column = hynix->read_retry->regs[i];
|
||||
|
||||
column |= column << 8;
|
||||
chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1);
|
||||
chip->write_byte(mtd, values[i]);
|
||||
}
|
||||
|
||||
/* Apply the new settings. */
|
||||
chip->cmdfunc(mtd, NAND_HYNIX_CMD_APPLY_PARAMS, -1, -1);
|
||||
|
||||
status = chip->waitfunc(mtd, chip);
|
||||
if (status & NAND_STATUS_FAIL)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hynix_get_majority - get the value that is occurring the most in a given
|
||||
* set of values
|
||||
* @in: the array of values to test
|
||||
* @repeat: the size of the in array
|
||||
* @out: pointer used to store the output value
|
||||
*
|
||||
* This function implements the 'majority check' logic that is supposed to
|
||||
* overcome the unreliability of MLC NANDs when reading the OTP area storing
|
||||
* the read-retry parameters.
|
||||
*
|
||||
* It's based on a pretty simple assumption: if we repeat the same value
|
||||
* several times and then take the one that is occurring the most, we should
|
||||
* find the correct value.
|
||||
* Let's hope this dummy algorithm prevents us from losing the read-retry
|
||||
* parameters.
|
||||
*/
|
||||
static int hynix_get_majority(const u8 *in, int repeat, u8 *out)
|
||||
{
|
||||
int i, j, half = repeat / 2;
|
||||
|
||||
/*
|
||||
* We only test the first half of the in array because we must ensure
|
||||
* that the value is at least occurring repeat / 2 times.
|
||||
*
|
||||
* This loop is suboptimal since we may count the occurrences of the
|
||||
* same value several time, but we are doing that on small sets, which
|
||||
* makes it acceptable.
|
||||
*/
|
||||
for (i = 0; i < half; i++) {
|
||||
int cnt = 0;
|
||||
u8 val = in[i];
|
||||
|
||||
/* Count all values that are matching the one at index i. */
|
||||
for (j = i + 1; j < repeat; j++) {
|
||||
if (in[j] == val)
|
||||
cnt++;
|
||||
}
|
||||
|
||||
/* We found a value occurring more than repeat / 2. */
|
||||
if (cnt > half) {
|
||||
*out = val;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int hynix_read_rr_otp(struct nand_chip *chip,
|
||||
const struct hynix_read_retry_otp *info,
|
||||
void *buf)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int i;
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
||||
|
||||
chip->cmdfunc(mtd, NAND_HYNIX_CMD_SET_PARAMS, -1, -1);
|
||||
|
||||
for (i = 0; i < info->nregs; i++) {
|
||||
int column = info->regs[i];
|
||||
|
||||
column |= column << 8;
|
||||
chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1);
|
||||
chip->write_byte(mtd, info->values[i]);
|
||||
}
|
||||
|
||||
chip->cmdfunc(mtd, NAND_HYNIX_CMD_APPLY_PARAMS, -1, -1);
|
||||
|
||||
/* Sequence to enter OTP mode? */
|
||||
chip->cmdfunc(mtd, 0x17, -1, -1);
|
||||
chip->cmdfunc(mtd, 0x04, -1, -1);
|
||||
chip->cmdfunc(mtd, 0x19, -1, -1);
|
||||
|
||||
/* Now read the page */
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, info->page);
|
||||
chip->read_buf(mtd, buf, info->size);
|
||||
|
||||
/* Put everything back to normal */
|
||||
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
||||
chip->cmdfunc(mtd, NAND_HYNIX_CMD_SET_PARAMS, 0x38, -1);
|
||||
chip->write_byte(mtd, 0x0);
|
||||
chip->cmdfunc(mtd, NAND_HYNIX_CMD_APPLY_PARAMS, -1, -1);
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NAND_HYNIX_1XNM_RR_COUNT_OFFS 0
|
||||
#define NAND_HYNIX_1XNM_RR_REG_COUNT_OFFS 8
|
||||
#define NAND_HYNIX_1XNM_RR_SET_OFFS(x, setsize, inv) \
|
||||
(16 + ((((x) * 2) + ((inv) ? 1 : 0)) * (setsize)))
|
||||
|
||||
static int hynix_mlc_1xnm_rr_value(const u8 *buf, int nmodes, int nregs,
|
||||
int mode, int reg, bool inv, u8 *val)
|
||||
{
|
||||
u8 tmp[NAND_HYNIX_1XNM_RR_REPEAT];
|
||||
int val_offs = (mode * nregs) + reg;
|
||||
int set_size = nmodes * nregs;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < NAND_HYNIX_1XNM_RR_REPEAT; i++) {
|
||||
int set_offs = NAND_HYNIX_1XNM_RR_SET_OFFS(i, set_size, inv);
|
||||
|
||||
tmp[i] = buf[val_offs + set_offs];
|
||||
}
|
||||
|
||||
ret = hynix_get_majority(tmp, NAND_HYNIX_1XNM_RR_REPEAT, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (inv)
|
||||
*val = ~*val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 hynix_1xnm_mlc_read_retry_regs[] = {
|
||||
0xcc, 0xbf, 0xaa, 0xab, 0xcd, 0xad, 0xae, 0xaf
|
||||
};
|
||||
|
||||
static int hynix_mlc_1xnm_rr_init(struct nand_chip *chip,
|
||||
const struct hynix_read_retry_otp *info)
|
||||
{
|
||||
struct hynix_nand *hynix = nand_get_manufacturer_data(chip);
|
||||
struct hynix_read_retry *rr = NULL;
|
||||
int ret, i, j;
|
||||
u8 nregs, nmodes;
|
||||
u8 *buf;
|
||||
|
||||
buf = kmalloc(info->size, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = hynix_read_rr_otp(chip, info, buf);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = hynix_get_majority(buf, NAND_HYNIX_1XNM_RR_REPEAT,
|
||||
&nmodes);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = hynix_get_majority(buf + NAND_HYNIX_1XNM_RR_REPEAT,
|
||||
NAND_HYNIX_1XNM_RR_REPEAT,
|
||||
&nregs);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
rr = kzalloc(sizeof(*rr) + (nregs * nmodes), GFP_KERNEL);
|
||||
if (!rr) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < nmodes; i++) {
|
||||
for (j = 0; j < nregs; j++) {
|
||||
u8 *val = rr->values + (i * nregs);
|
||||
|
||||
ret = hynix_mlc_1xnm_rr_value(buf, nmodes, nregs, i, j,
|
||||
false, val);
|
||||
if (!ret)
|
||||
continue;
|
||||
|
||||
ret = hynix_mlc_1xnm_rr_value(buf, nmodes, nregs, i, j,
|
||||
true, val);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
rr->nregs = nregs;
|
||||
rr->regs = hynix_1xnm_mlc_read_retry_regs;
|
||||
hynix->read_retry = rr;
|
||||
chip->setup_read_retry = hynix_nand_setup_read_retry;
|
||||
chip->read_retries = nmodes;
|
||||
|
||||
out:
|
||||
kfree(buf);
|
||||
|
||||
if (ret)
|
||||
kfree(rr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const u8 hynix_mlc_1xnm_rr_otp_regs[] = { 0x38 };
|
||||
static const u8 hynix_mlc_1xnm_rr_otp_values[] = { 0x52 };
|
||||
|
||||
static const struct hynix_read_retry_otp hynix_mlc_1xnm_rr_otps[] = {
|
||||
{
|
||||
.nregs = ARRAY_SIZE(hynix_mlc_1xnm_rr_otp_regs),
|
||||
.regs = hynix_mlc_1xnm_rr_otp_regs,
|
||||
.values = hynix_mlc_1xnm_rr_otp_values,
|
||||
.page = 0x21f,
|
||||
.size = 784
|
||||
},
|
||||
{
|
||||
.nregs = ARRAY_SIZE(hynix_mlc_1xnm_rr_otp_regs),
|
||||
.regs = hynix_mlc_1xnm_rr_otp_regs,
|
||||
.values = hynix_mlc_1xnm_rr_otp_values,
|
||||
.page = 0x200,
|
||||
.size = 528,
|
||||
},
|
||||
};
|
||||
|
||||
static int hynix_nand_rr_init(struct nand_chip *chip)
|
||||
{
|
||||
int i, ret = 0;
|
||||
bool valid_jedecid;
|
||||
|
||||
valid_jedecid = hynix_nand_has_valid_jedecid(chip);
|
||||
|
||||
/*
|
||||
* We only support read-retry for 1xnm NANDs, and those NANDs all
|
||||
* expose a valid JEDEC ID.
|
||||
*/
|
||||
if (valid_jedecid) {
|
||||
u8 nand_tech = chip->id.data[5] >> 4;
|
||||
|
||||
/* 1xnm technology */
|
||||
if (nand_tech == 4) {
|
||||
for (i = 0; i < ARRAY_SIZE(hynix_mlc_1xnm_rr_otps);
|
||||
i++) {
|
||||
/*
|
||||
* FIXME: Hynix recommend to copy the
|
||||
* read-retry OTP area into a normal page.
|
||||
*/
|
||||
ret = hynix_mlc_1xnm_rr_init(chip,
|
||||
hynix_mlc_1xnm_rr_otps);
|
||||
if (!ret)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
pr_warn("failed to initialize read-retry infrastructure");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hynix_nand_extract_oobsize(struct nand_chip *chip,
|
||||
bool valid_jedecid)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
u8 oobsize;
|
||||
|
||||
oobsize = ((chip->id.data[3] >> 2) & 0x3) |
|
||||
((chip->id.data[3] >> 4) & 0x4);
|
||||
|
||||
if (valid_jedecid) {
|
||||
switch (oobsize) {
|
||||
case 0:
|
||||
mtd->oobsize = 2048;
|
||||
break;
|
||||
case 1:
|
||||
mtd->oobsize = 1664;
|
||||
break;
|
||||
case 2:
|
||||
mtd->oobsize = 1024;
|
||||
break;
|
||||
case 3:
|
||||
mtd->oobsize = 640;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* We should never reach this case, but if that
|
||||
* happens, this probably means Hynix decided to use
|
||||
* a different extended ID format, and we should find
|
||||
* a way to support it.
|
||||
*/
|
||||
WARN(1, "Invalid OOB size");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (oobsize) {
|
||||
case 0:
|
||||
mtd->oobsize = 128;
|
||||
break;
|
||||
case 1:
|
||||
mtd->oobsize = 224;
|
||||
break;
|
||||
case 2:
|
||||
mtd->oobsize = 448;
|
||||
break;
|
||||
case 3:
|
||||
mtd->oobsize = 64;
|
||||
break;
|
||||
case 4:
|
||||
mtd->oobsize = 32;
|
||||
break;
|
||||
case 5:
|
||||
mtd->oobsize = 16;
|
||||
break;
|
||||
case 6:
|
||||
mtd->oobsize = 640;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* We should never reach this case, but if that
|
||||
* happens, this probably means Hynix decided to use
|
||||
* a different extended ID format, and we should find
|
||||
* a way to support it.
|
||||
*/
|
||||
WARN(1, "Invalid OOB size");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip,
|
||||
bool valid_jedecid)
|
||||
{
|
||||
u8 ecc_level = (chip->id.data[4] >> 4) & 0x7;
|
||||
|
||||
if (valid_jedecid) {
|
||||
/* Reference: H27UCG8T2E datasheet */
|
||||
chip->ecc_step_ds = 1024;
|
||||
|
||||
switch (ecc_level) {
|
||||
case 0:
|
||||
chip->ecc_step_ds = 0;
|
||||
chip->ecc_strength_ds = 0;
|
||||
break;
|
||||
case 1:
|
||||
chip->ecc_strength_ds = 4;
|
||||
break;
|
||||
case 2:
|
||||
chip->ecc_strength_ds = 24;
|
||||
break;
|
||||
case 3:
|
||||
chip->ecc_strength_ds = 32;
|
||||
break;
|
||||
case 4:
|
||||
chip->ecc_strength_ds = 40;
|
||||
break;
|
||||
case 5:
|
||||
chip->ecc_strength_ds = 50;
|
||||
break;
|
||||
case 6:
|
||||
chip->ecc_strength_ds = 60;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* We should never reach this case, but if that
|
||||
* happens, this probably means Hynix decided to use
|
||||
* a different extended ID format, and we should find
|
||||
* a way to support it.
|
||||
*/
|
||||
WARN(1, "Invalid ECC requirements");
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* The ECC requirements field meaning depends on the
|
||||
* NAND technology.
|
||||
*/
|
||||
u8 nand_tech = chip->id.data[5] & 0x3;
|
||||
|
||||
if (nand_tech < 3) {
|
||||
/* > 26nm, reference: H27UBG8T2A datasheet */
|
||||
if (ecc_level < 5) {
|
||||
chip->ecc_step_ds = 512;
|
||||
chip->ecc_strength_ds = 1 << ecc_level;
|
||||
} else if (ecc_level < 7) {
|
||||
if (ecc_level == 5)
|
||||
chip->ecc_step_ds = 2048;
|
||||
else
|
||||
chip->ecc_step_ds = 1024;
|
||||
chip->ecc_strength_ds = 24;
|
||||
} else {
|
||||
/*
|
||||
* We should never reach this case, but if that
|
||||
* happens, this probably means Hynix decided
|
||||
* to use a different extended ID format, and
|
||||
* we should find a way to support it.
|
||||
*/
|
||||
WARN(1, "Invalid ECC requirements");
|
||||
}
|
||||
} else {
|
||||
/* <= 26nm, reference: H27UBG8T2B datasheet */
|
||||
if (!ecc_level) {
|
||||
chip->ecc_step_ds = 0;
|
||||
chip->ecc_strength_ds = 0;
|
||||
} else if (ecc_level < 5) {
|
||||
chip->ecc_step_ds = 512;
|
||||
chip->ecc_strength_ds = 1 << (ecc_level - 1);
|
||||
} else {
|
||||
chip->ecc_step_ds = 1024;
|
||||
chip->ecc_strength_ds = 24 +
|
||||
(8 * (ecc_level - 5));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hynix_nand_extract_scrambling_requirements(struct nand_chip *chip,
|
||||
bool valid_jedecid)
|
||||
{
|
||||
u8 nand_tech;
|
||||
|
||||
/* We need scrambling on all TLC NANDs*/
|
||||
if (chip->bits_per_cell > 2)
|
||||
chip->options |= NAND_NEED_SCRAMBLING;
|
||||
|
||||
/* And on MLC NANDs with sub-3xnm process */
|
||||
if (valid_jedecid) {
|
||||
nand_tech = chip->id.data[5] >> 4;
|
||||
|
||||
/* < 3xnm */
|
||||
if (nand_tech > 0)
|
||||
chip->options |= NAND_NEED_SCRAMBLING;
|
||||
} else {
|
||||
nand_tech = chip->id.data[5] & 0x3;
|
||||
|
||||
/* < 32nm */
|
||||
if (nand_tech > 2)
|
||||
chip->options |= NAND_NEED_SCRAMBLING;
|
||||
}
|
||||
}
|
||||
|
||||
static void hynix_nand_decode_id(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
bool valid_jedecid;
|
||||
u8 tmp;
|
||||
|
||||
/*
|
||||
* Exclude all SLC NANDs from this advanced detection scheme.
|
||||
* According to the ranges defined in several datasheets, it might
|
||||
* appear that even SLC NANDs could fall in this extended ID scheme.
|
||||
* If that the case rework the test to let SLC NANDs go through the
|
||||
* detection process.
|
||||
*/
|
||||
if (chip->id.len < 6 || nand_is_slc(chip)) {
|
||||
nand_decode_ext_id(chip);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Extract pagesize */
|
||||
mtd->writesize = 2048 << (chip->id.data[3] & 0x03);
|
||||
|
||||
tmp = (chip->id.data[3] >> 4) & 0x3;
|
||||
/*
|
||||
* When bit7 is set that means we start counting at 1MiB, otherwise
|
||||
* we start counting at 128KiB and shift this value the content of
|
||||
* ID[3][4:5].
|
||||
* The only exception is when ID[3][4:5] == 3 and ID[3][7] == 0, in
|
||||
* this case the erasesize is set to 768KiB.
|
||||
*/
|
||||
if (chip->id.data[3] & 0x80)
|
||||
mtd->erasesize = SZ_1M << tmp;
|
||||
else if (tmp == 3)
|
||||
mtd->erasesize = SZ_512K + SZ_256K;
|
||||
else
|
||||
mtd->erasesize = SZ_128K << tmp;
|
||||
|
||||
/*
|
||||
* Modern Toggle DDR NANDs have a valid JEDECID even though they are
|
||||
* not exposing a valid JEDEC parameter table.
|
||||
* These NANDs use a different NAND ID scheme.
|
||||
*/
|
||||
valid_jedecid = hynix_nand_has_valid_jedecid(chip);
|
||||
|
||||
hynix_nand_extract_oobsize(chip, valid_jedecid);
|
||||
hynix_nand_extract_ecc_requirements(chip, valid_jedecid);
|
||||
hynix_nand_extract_scrambling_requirements(chip, valid_jedecid);
|
||||
}
|
||||
|
||||
static void hynix_nand_cleanup(struct nand_chip *chip)
|
||||
{
|
||||
struct hynix_nand *hynix = nand_get_manufacturer_data(chip);
|
||||
|
||||
if (!hynix)
|
||||
return;
|
||||
|
||||
kfree(hynix->read_retry);
|
||||
kfree(hynix);
|
||||
nand_set_manufacturer_data(chip, NULL);
|
||||
}
|
||||
|
||||
static int hynix_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
struct hynix_nand *hynix;
|
||||
int ret;
|
||||
|
||||
if (!nand_is_slc(chip))
|
||||
chip->bbt_options |= NAND_BBT_SCANLASTPAGE;
|
||||
else
|
||||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
|
||||
hynix = kzalloc(sizeof(*hynix), GFP_KERNEL);
|
||||
if (!hynix)
|
||||
return -ENOMEM;
|
||||
|
||||
nand_set_manufacturer_data(chip, hynix);
|
||||
|
||||
ret = hynix_nand_rr_init(chip);
|
||||
if (ret)
|
||||
hynix_nand_cleanup(chip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct nand_manufacturer_ops hynix_nand_manuf_ops = {
|
||||
.detect = hynix_nand_decode_id,
|
||||
.init = hynix_nand_init,
|
||||
.cleanup = hynix_nand_cleanup,
|
||||
};
|
@ -10,7 +10,7 @@
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS
|
||||
#define LP_OPTIONS 0
|
||||
#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
|
||||
|
||||
#define SP_OPTIONS NAND_NEED_READRDY
|
||||
@ -169,29 +169,40 @@ struct nand_flash_dev nand_flash_ids[] = {
|
||||
};
|
||||
|
||||
/* Manufacturer IDs */
|
||||
struct nand_manufacturers nand_manuf_ids[] = {
|
||||
{NAND_MFR_TOSHIBA, "Toshiba"},
|
||||
static const struct nand_manufacturer nand_manufacturers[] = {
|
||||
{NAND_MFR_TOSHIBA, "Toshiba", &toshiba_nand_manuf_ops},
|
||||
{NAND_MFR_ESMT, "ESMT"},
|
||||
{NAND_MFR_SAMSUNG, "Samsung"},
|
||||
{NAND_MFR_SAMSUNG, "Samsung", &samsung_nand_manuf_ops},
|
||||
{NAND_MFR_FUJITSU, "Fujitsu"},
|
||||
{NAND_MFR_NATIONAL, "National"},
|
||||
{NAND_MFR_RENESAS, "Renesas"},
|
||||
{NAND_MFR_STMICRO, "ST Micro"},
|
||||
{NAND_MFR_HYNIX, "Hynix"},
|
||||
{NAND_MFR_MICRON, "Micron"},
|
||||
{NAND_MFR_AMD, "AMD/Spansion"},
|
||||
{NAND_MFR_MACRONIX, "Macronix"},
|
||||
{NAND_MFR_HYNIX, "Hynix", &hynix_nand_manuf_ops},
|
||||
{NAND_MFR_MICRON, "Micron", µn_nand_manuf_ops},
|
||||
{NAND_MFR_AMD, "AMD/Spansion", &amd_nand_manuf_ops},
|
||||
{NAND_MFR_MACRONIX, "Macronix", ¯onix_nand_manuf_ops},
|
||||
{NAND_MFR_EON, "Eon"},
|
||||
{NAND_MFR_SANDISK, "SanDisk"},
|
||||
{NAND_MFR_INTEL, "Intel"},
|
||||
{NAND_MFR_ATO, "ATO"},
|
||||
{NAND_MFR_WINBOND, "Winbond"},
|
||||
{0x0, "Unknown"}
|
||||
};
|
||||
|
||||
EXPORT_SYMBOL(nand_manuf_ids);
|
||||
EXPORT_SYMBOL(nand_flash_ids);
|
||||
/**
|
||||
* nand_get_manufacturer - Get manufacturer information from the manufacturer
|
||||
* ID
|
||||
* @id: manufacturer ID
|
||||
*
|
||||
* Returns a pointer a nand_manufacturer object if the manufacturer is defined
|
||||
* in the NAND manufacturers database, NULL otherwise.
|
||||
*/
|
||||
const struct nand_manufacturer *nand_get_manufacturer(u8 id)
|
||||
{
|
||||
int i;
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
|
||||
MODULE_DESCRIPTION("Nand device & manufacturer IDs");
|
||||
for (i = 0; i < ARRAY_SIZE(nand_manufacturers); i++)
|
||||
if (nand_manufacturers[i].id == id)
|
||||
return &nand_manufacturers[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
30
drivers/mtd/nand/nand_macronix.c
Normal file
30
drivers/mtd/nand/nand_macronix.c
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Free Electrons
|
||||
* Copyright (C) 2017 NextThing Co
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/mtd/nand.h>
|
||||
|
||||
static int macronix_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
if (nand_is_slc(chip))
|
||||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct nand_manufacturer_ops macronix_nand_manuf_ops = {
|
||||
.init = macronix_nand_init,
|
||||
};
|
86
drivers/mtd/nand/nand_micron.c
Normal file
86
drivers/mtd/nand/nand_micron.c
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Free Electrons
|
||||
* Copyright (C) 2017 NextThing Co
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/mtd/nand.h>
|
||||
|
||||
struct nand_onfi_vendor_micron {
|
||||
u8 two_plane_read;
|
||||
u8 read_cache;
|
||||
u8 read_unique_id;
|
||||
u8 dq_imped;
|
||||
u8 dq_imped_num_settings;
|
||||
u8 dq_imped_feat_addr;
|
||||
u8 rb_pulldown_strength;
|
||||
u8 rb_pulldown_strength_feat_addr;
|
||||
u8 rb_pulldown_strength_num_settings;
|
||||
u8 otp_mode;
|
||||
u8 otp_page_start;
|
||||
u8 otp_data_prot_addr;
|
||||
u8 otp_num_pages;
|
||||
u8 otp_feat_addr;
|
||||
u8 read_retry_options;
|
||||
u8 reserved[72];
|
||||
u8 param_revision;
|
||||
} __packed;
|
||||
|
||||
static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode};
|
||||
|
||||
return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY,
|
||||
feature);
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure chip properties from Micron vendor-specific ONFI table
|
||||
*/
|
||||
static int micron_nand_onfi_init(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_onfi_params *p = &chip->onfi_params;
|
||||
struct nand_onfi_vendor_micron *micron = (void *)p->vendor;
|
||||
|
||||
if (!chip->onfi_version)
|
||||
return 0;
|
||||
|
||||
if (le16_to_cpu(p->vendor_revision) < 1)
|
||||
return 0;
|
||||
|
||||
chip->read_retries = micron->read_retry_options;
|
||||
chip->setup_read_retry = micron_nand_setup_read_retry;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int micron_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int ret;
|
||||
|
||||
ret = micron_nand_onfi_init(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mtd->writesize == 2048)
|
||||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct nand_manufacturer_ops micron_nand_manuf_ops = {
|
||||
.init = micron_nand_init,
|
||||
};
|
112
drivers/mtd/nand/nand_samsung.c
Normal file
112
drivers/mtd/nand/nand_samsung.c
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Free Electrons
|
||||
* Copyright (C) 2017 NextThing Co
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/mtd/nand.h>
|
||||
|
||||
static void samsung_nand_decode_id(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
||||
/* New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44) */
|
||||
if (chip->id.len == 6 && !nand_is_slc(chip) &&
|
||||
chip->id.data[5] != 0x00) {
|
||||
u8 extid = chip->id.data[3];
|
||||
|
||||
/* Get pagesize */
|
||||
mtd->writesize = 2048 << (extid & 0x03);
|
||||
|
||||
extid >>= 2;
|
||||
|
||||
/* Get oobsize */
|
||||
switch (((extid >> 2) & 0x4) | (extid & 0x3)) {
|
||||
case 1:
|
||||
mtd->oobsize = 128;
|
||||
break;
|
||||
case 2:
|
||||
mtd->oobsize = 218;
|
||||
break;
|
||||
case 3:
|
||||
mtd->oobsize = 400;
|
||||
break;
|
||||
case 4:
|
||||
mtd->oobsize = 436;
|
||||
break;
|
||||
case 5:
|
||||
mtd->oobsize = 512;
|
||||
break;
|
||||
case 6:
|
||||
mtd->oobsize = 640;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* We should never reach this case, but if that
|
||||
* happens, this probably means Samsung decided to use
|
||||
* a different extended ID format, and we should find
|
||||
* a way to support it.
|
||||
*/
|
||||
WARN(1, "Invalid OOB size value");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get blocksize */
|
||||
extid >>= 2;
|
||||
mtd->erasesize = (128 * 1024) <<
|
||||
(((extid >> 1) & 0x04) | (extid & 0x03));
|
||||
|
||||
/* Extract ECC requirements from 5th id byte*/
|
||||
extid = (chip->id.data[4] >> 4) & 0x07;
|
||||
if (extid < 5) {
|
||||
chip->ecc_step_ds = 512;
|
||||
chip->ecc_strength_ds = 1 << extid;
|
||||
} else {
|
||||
chip->ecc_step_ds = 1024;
|
||||
switch (extid) {
|
||||
case 5:
|
||||
chip->ecc_strength_ds = 24;
|
||||
break;
|
||||
case 6:
|
||||
chip->ecc_strength_ds = 40;
|
||||
break;
|
||||
case 7:
|
||||
chip->ecc_strength_ds = 60;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nand_decode_ext_id(chip);
|
||||
}
|
||||
}
|
||||
|
||||
static int samsung_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
||||
if (mtd->writesize > 512)
|
||||
chip->options |= NAND_SAMSUNG_LP_OPTIONS;
|
||||
|
||||
if (!nand_is_slc(chip))
|
||||
chip->bbt_options |= NAND_BBT_SCANLASTPAGE;
|
||||
else
|
||||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct nand_manufacturer_ops samsung_nand_manuf_ops = {
|
||||
.detect = samsung_nand_decode_id,
|
||||
.init = samsung_nand_init,
|
||||
};
|
51
drivers/mtd/nand/nand_toshiba.c
Normal file
51
drivers/mtd/nand/nand_toshiba.c
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Free Electrons
|
||||
* Copyright (C) 2017 NextThing Co
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/mtd/nand.h>
|
||||
|
||||
static void toshiba_nand_decode_id(struct nand_chip *chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
||||
nand_decode_ext_id(chip);
|
||||
|
||||
/*
|
||||
* Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per
|
||||
* 512B page. For Toshiba SLC, we decode the 5th/6th byte as
|
||||
* follows:
|
||||
* - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm,
|
||||
* 110b -> 24nm
|
||||
* - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC
|
||||
*/
|
||||
if (chip->id.len >= 6 && nand_is_slc(chip) &&
|
||||
(chip->id.data[5] & 0x7) == 0x6 /* 24nm */ &&
|
||||
!(chip->id.data[4] & 0x80) /* !BENAND */)
|
||||
mtd->oobsize = 32 * mtd->writesize >> 9;
|
||||
}
|
||||
|
||||
static int toshiba_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
if (nand_is_slc(chip))
|
||||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct nand_manufacturer_ops toshiba_nand_manuf_ops = {
|
||||
.detect = toshiba_nand_decode_id,
|
||||
.init = toshiba_nand_init,
|
||||
};
|
@ -902,7 +902,7 @@ static int parse_weakpages(void)
|
||||
zero_ok = (*w == '0' ? 1 : 0);
|
||||
page_no = simple_strtoul(w, &w, 0);
|
||||
if (!zero_ok && !page_no) {
|
||||
NS_ERR("invalid weakpagess.\n");
|
||||
NS_ERR("invalid weakpages.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
max_writes = 3;
|
||||
|
@ -1856,6 +1856,15 @@ static int omap_nand_probe(struct platform_device *pdev)
|
||||
nand_chip->ecc.priv = NULL;
|
||||
nand_set_flash_node(nand_chip, dev->of_node);
|
||||
|
||||
if (!mtd->name) {
|
||||
mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
|
||||
"omap2-nand.%d", info->gpmc_cs);
|
||||
if (!mtd->name) {
|
||||
dev_err(&pdev->dev, "Failed to set MTD name\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(nand_chip->IO_ADDR_R))
|
||||
|
@ -23,6 +23,11 @@
|
||||
#include <asm/sizes.h>
|
||||
#include <linux/platform_data/mtd-orion_nand.h>
|
||||
|
||||
struct orion_nand_info {
|
||||
struct nand_chip chip;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *nc = mtd_to_nand(mtd);
|
||||
@ -75,20 +80,21 @@ static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||
|
||||
static int __init orion_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct orion_nand_info *info;
|
||||
struct mtd_info *mtd;
|
||||
struct nand_chip *nc;
|
||||
struct orion_nand_data *board;
|
||||
struct resource *res;
|
||||
struct clk *clk;
|
||||
void __iomem *io_base;
|
||||
int ret = 0;
|
||||
u32 val = 0;
|
||||
|
||||
nc = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct nand_chip),
|
||||
info = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct orion_nand_info),
|
||||
GFP_KERNEL);
|
||||
if (!nc)
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
nc = &info->chip;
|
||||
mtd = nand_to_mtd(nc);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
@ -145,16 +151,23 @@ static int __init orion_nand_probe(struct platform_device *pdev)
|
||||
if (board->dev_ready)
|
||||
nc->dev_ready = board->dev_ready;
|
||||
|
||||
platform_set_drvdata(pdev, mtd);
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
/* Not all platforms can gate the clock, so it is not
|
||||
an error if the clock does not exists. */
|
||||
clk = clk_get(&pdev->dev, NULL);
|
||||
if (!IS_ERR(clk)) {
|
||||
clk_prepare_enable(clk);
|
||||
clk_put(clk);
|
||||
info->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(info->clk)) {
|
||||
ret = PTR_ERR(info->clk);
|
||||
if (ret == -ENOENT) {
|
||||
info->clk = NULL;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "failed to get clock!\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
clk_prepare_enable(info->clk);
|
||||
|
||||
ret = nand_scan(mtd, 1);
|
||||
if (ret)
|
||||
goto no_dev;
|
||||
@ -169,26 +182,19 @@ static int __init orion_nand_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
no_dev:
|
||||
if (!IS_ERR(clk)) {
|
||||
clk_disable_unprepare(clk);
|
||||
clk_put(clk);
|
||||
}
|
||||
|
||||
clk_disable_unprepare(info->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int orion_nand_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mtd_info *mtd = platform_get_drvdata(pdev);
|
||||
struct clk *clk;
|
||||
struct orion_nand_info *info = platform_get_drvdata(pdev);
|
||||
struct nand_chip *chip = &info->chip;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
||||
nand_release(mtd);
|
||||
|
||||
clk = clk_get(&pdev->dev, NULL);
|
||||
if (!IS_ERR(clk)) {
|
||||
clk_disable_unprepare(clk);
|
||||
clk_put(clk);
|
||||
}
|
||||
clk_disable_unprepare(info->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ static int oxnas_nand_probe(struct platform_device *pdev)
|
||||
int err = 0;
|
||||
|
||||
/* Allocate memory for the device structure (and zero it) */
|
||||
oxnas = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip),
|
||||
oxnas = devm_kzalloc(&pdev->dev, sizeof(*oxnas),
|
||||
GFP_KERNEL);
|
||||
if (!oxnas)
|
||||
return -ENOMEM;
|
||||
|
@ -2212,17 +2212,17 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
|
||||
goto out_ahb_clk_unprepare;
|
||||
|
||||
nfc->reset = devm_reset_control_get_optional(dev, "ahb");
|
||||
if (!IS_ERR(nfc->reset)) {
|
||||
ret = reset_control_deassert(nfc->reset);
|
||||
if (ret) {
|
||||
dev_err(dev, "reset err %d\n", ret);
|
||||
goto out_mod_clk_unprepare;
|
||||
}
|
||||
} else if (PTR_ERR(nfc->reset) != -ENOENT) {
|
||||
if (IS_ERR(nfc->reset)) {
|
||||
ret = PTR_ERR(nfc->reset);
|
||||
goto out_mod_clk_unprepare;
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(nfc->reset);
|
||||
if (ret) {
|
||||
dev_err(dev, "reset err %d\n", ret);
|
||||
goto out_mod_clk_unprepare;
|
||||
}
|
||||
|
||||
ret = sunxi_nfc_rst(nfc);
|
||||
if (ret)
|
||||
goto out_ahb_reset_reassert;
|
||||
@ -2262,8 +2262,7 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
|
||||
if (nfc->dmac)
|
||||
dma_release_channel(nfc->dmac);
|
||||
out_ahb_reset_reassert:
|
||||
if (!IS_ERR(nfc->reset))
|
||||
reset_control_assert(nfc->reset);
|
||||
reset_control_assert(nfc->reset);
|
||||
out_mod_clk_unprepare:
|
||||
clk_disable_unprepare(nfc->mod_clk);
|
||||
out_ahb_clk_unprepare:
|
||||
@ -2278,8 +2277,7 @@ static int sunxi_nfc_remove(struct platform_device *pdev)
|
||||
|
||||
sunxi_nand_chips_cleanup(nfc);
|
||||
|
||||
if (!IS_ERR(nfc->reset))
|
||||
reset_control_assert(nfc->reset);
|
||||
reset_control_assert(nfc->reset);
|
||||
|
||||
if (nfc->dmac)
|
||||
dma_release_channel(nfc->dmac);
|
||||
|
@ -223,12 +223,13 @@ static void tango_dma_callback(void *arg)
|
||||
complete(arg);
|
||||
}
|
||||
|
||||
static int do_dma(struct tango_nfc *nfc, int dir, int cmd, const void *buf,
|
||||
int len, int page)
|
||||
static int do_dma(struct tango_nfc *nfc, enum dma_data_direction dir, int cmd,
|
||||
const void *buf, int len, int page)
|
||||
{
|
||||
void __iomem *addr = nfc->reg_base + NFC_STATUS;
|
||||
struct dma_chan *chan = nfc->chan;
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
enum dma_transfer_direction tdir;
|
||||
struct scatterlist sg;
|
||||
struct completion tx_done;
|
||||
int err = -EIO;
|
||||
@ -238,7 +239,8 @@ static int do_dma(struct tango_nfc *nfc, int dir, int cmd, const void *buf,
|
||||
if (dma_map_sg(chan->device->dev, &sg, 1, dir) != 1)
|
||||
return -EIO;
|
||||
|
||||
desc = dmaengine_prep_slave_sg(chan, &sg, 1, dir, DMA_PREP_INTERRUPT);
|
||||
tdir = dir == DMA_TO_DEVICE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
|
||||
desc = dmaengine_prep_slave_sg(chan, &sg, 1, tdir, DMA_PREP_INTERRUPT);
|
||||
if (!desc)
|
||||
goto dma_unmap;
|
||||
|
||||
|
@ -166,8 +166,8 @@ static int parse_ofoldpart_partitions(struct mtd_info *master,
|
||||
if (!part)
|
||||
return 0; /* No partitions found */
|
||||
|
||||
pr_warning("Device tree uses obsolete partition map binding: %s\n",
|
||||
dp->full_name);
|
||||
pr_warn("Device tree uses obsolete partition map binding: %s\n",
|
||||
dp->full_name);
|
||||
|
||||
nr_parts = plen / sizeof(part[0]);
|
||||
|
||||
|
@ -106,4 +106,11 @@ config SPI_INTEL_SPI_PLATFORM
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called intel-spi-platform.
|
||||
|
||||
config SPI_STM32_QUADSPI
|
||||
tristate "STM32 Quad SPI controller"
|
||||
depends on ARCH_STM32
|
||||
help
|
||||
This enables support for the STM32 Quad SPI controller.
|
||||
We only connect the NOR to this controller.
|
||||
|
||||
endif # MTD_SPI_NOR
|
||||
|
@ -8,3 +8,4 @@ obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o
|
||||
obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o
|
||||
obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o
|
||||
obj-$(CONFIG_SPI_INTEL_SPI_PLATFORM) += intel-spi-platform.o
|
||||
obj-$(CONFIG_SPI_STM32_QUADSPI) += stm32-quadspi.o
|
@ -448,8 +448,11 @@ static int hisi_spi_nor_probe(struct platform_device *pdev)
|
||||
if (!host->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = clk_prepare_enable(host->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_init(&host->lock);
|
||||
clk_prepare_enable(host->clk);
|
||||
hisi_spi_nor_init(host);
|
||||
ret = hisi_spi_nor_register_all(host);
|
||||
if (ret)
|
||||
|
@ -704,7 +704,7 @@ static void intel_spi_fill_partition(struct intel_spi *ispi,
|
||||
* whole partition read-only to be on the safe side.
|
||||
*/
|
||||
if (intel_spi_is_protected(ispi, base, limit))
|
||||
ispi->writeable = 0;
|
||||
ispi->writeable = false;
|
||||
|
||||
end = (limit << 12) + 4096;
|
||||
if (end > part->size)
|
||||
@ -728,7 +728,7 @@ struct intel_spi *intel_spi_probe(struct device *dev,
|
||||
|
||||
ispi->base = devm_ioremap_resource(dev, mem);
|
||||
if (IS_ERR(ispi->base))
|
||||
return ispi->base;
|
||||
return ERR_CAST(ispi->base);
|
||||
|
||||
ispi->dev = dev;
|
||||
ispi->info = info;
|
||||
|
@ -104,6 +104,8 @@
|
||||
#define MTK_NOR_MAX_RX_TX_SHIFT 6
|
||||
/* can shift up to 56 bits (7 bytes) transfer by MTK_NOR_PRG_CMD */
|
||||
#define MTK_NOR_MAX_SHIFT 7
|
||||
/* nor controller 4-byte address mode enable bit */
|
||||
#define MTK_NOR_4B_ADDR_EN BIT(4)
|
||||
|
||||
/* Helpers for accessing the program data / shift data registers */
|
||||
#define MTK_NOR_PRG_REG(n) (MTK_NOR_PRGDATA0_REG + 4 * (n))
|
||||
@ -230,10 +232,35 @@ static int mt8173_nor_write_buffer_disable(struct mt8173_nor *mt8173_nor)
|
||||
10000);
|
||||
}
|
||||
|
||||
static void mt8173_nor_set_addr_width(struct mt8173_nor *mt8173_nor)
|
||||
{
|
||||
u8 val;
|
||||
struct spi_nor *nor = &mt8173_nor->nor;
|
||||
|
||||
val = readb(mt8173_nor->base + MTK_NOR_DUAL_REG);
|
||||
|
||||
switch (nor->addr_width) {
|
||||
case 3:
|
||||
val &= ~MTK_NOR_4B_ADDR_EN;
|
||||
break;
|
||||
case 4:
|
||||
val |= MTK_NOR_4B_ADDR_EN;
|
||||
break;
|
||||
default:
|
||||
dev_warn(mt8173_nor->dev, "Unexpected address width %u.\n",
|
||||
nor->addr_width);
|
||||
break;
|
||||
}
|
||||
|
||||
writeb(val, mt8173_nor->base + MTK_NOR_DUAL_REG);
|
||||
}
|
||||
|
||||
static void mt8173_nor_set_addr(struct mt8173_nor *mt8173_nor, u32 addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
mt8173_nor_set_addr_width(mt8173_nor);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
writeb(addr & 0xff, mt8173_nor->base + MTK_NOR_RADR0_REG + i * 4);
|
||||
addr >>= 8;
|
||||
|
@ -85,6 +85,7 @@ struct flash_info {
|
||||
* Use dedicated 4byte address op codes
|
||||
* to support memory size above 128Mib.
|
||||
*/
|
||||
#define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */
|
||||
};
|
||||
|
||||
#define JEDEC_MFR(info) ((info)->id[0])
|
||||
@ -960,6 +961,8 @@ static const struct flash_info spi_nor_ids[] = {
|
||||
|
||||
/* ESMT */
|
||||
{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) },
|
||||
{ "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) },
|
||||
{ "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_LOCK) },
|
||||
|
||||
/* Everspin */
|
||||
{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
|
||||
@ -1013,11 +1016,14 @@ static const struct flash_info spi_nor_ids[] = {
|
||||
{ "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "mx25u2033e", INFO(0xc22532, 0, 64 * 1024, 4, SECT_4K) },
|
||||
{ "mx25u4035", INFO(0xc22533, 0, 64 * 1024, 8, SECT_4K) },
|
||||
{ "mx25u8035", INFO(0xc22534, 0, 64 * 1024, 16, SECT_4K) },
|
||||
{ "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
|
||||
{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
|
||||
{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
|
||||
{ "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K) },
|
||||
{ "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) },
|
||||
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
|
||||
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
|
||||
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
|
||||
@ -1031,10 +1037,11 @@ static const struct flash_info spi_nor_ids[] = {
|
||||
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q256ax1", INFO(0x20bb19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
|
||||
{ "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
|
||||
|
||||
/* PMC */
|
||||
{ "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },
|
||||
@ -1128,6 +1135,9 @@ static const struct flash_info spi_nor_ids[] = {
|
||||
{ "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) },
|
||||
{ "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) },
|
||||
{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "w25q20cl", INFO(0xef4012, 0, 64 * 1024, 4, SECT_4K) },
|
||||
{ "w25q20bw", INFO(0xef5012, 0, 64 * 1024, 4, SECT_4K) },
|
||||
{ "w25q20ew", INFO(0xef6012, 0, 64 * 1024, 4, SECT_4K) },
|
||||
{ "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{
|
||||
"w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64,
|
||||
@ -1629,6 +1639,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||
nor->flags |= SNOR_F_USE_FSR;
|
||||
if (info->flags & SPI_NOR_HAS_TB)
|
||||
nor->flags |= SNOR_F_HAS_SR_TB;
|
||||
if (info->flags & NO_CHIP_ERASE)
|
||||
nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
|
||||
|
||||
#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
|
||||
/* prefer "small sector" erase if possible */
|
||||
|
693
drivers/mtd/spi-nor/stm32-quadspi.c
Normal file
693
drivers/mtd/spi-nor/stm32-quadspi.c
Normal file
@ -0,0 +1,693 @@
|
||||
/*
|
||||
* stm32_quadspi.c
|
||||
*
|
||||
* Copyright (C) 2017, Ludovic Barre
|
||||
*
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/spi-nor.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#define QUADSPI_CR 0x00
|
||||
#define CR_EN BIT(0)
|
||||
#define CR_ABORT BIT(1)
|
||||
#define CR_DMAEN BIT(2)
|
||||
#define CR_TCEN BIT(3)
|
||||
#define CR_SSHIFT BIT(4)
|
||||
#define CR_DFM BIT(6)
|
||||
#define CR_FSEL BIT(7)
|
||||
#define CR_FTHRES_SHIFT 8
|
||||
#define CR_FTHRES_MASK GENMASK(12, 8)
|
||||
#define CR_FTHRES(n) (((n) << CR_FTHRES_SHIFT) & CR_FTHRES_MASK)
|
||||
#define CR_TEIE BIT(16)
|
||||
#define CR_TCIE BIT(17)
|
||||
#define CR_FTIE BIT(18)
|
||||
#define CR_SMIE BIT(19)
|
||||
#define CR_TOIE BIT(20)
|
||||
#define CR_PRESC_SHIFT 24
|
||||
#define CR_PRESC_MASK GENMASK(31, 24)
|
||||
#define CR_PRESC(n) (((n) << CR_PRESC_SHIFT) & CR_PRESC_MASK)
|
||||
|
||||
#define QUADSPI_DCR 0x04
|
||||
#define DCR_CSHT_SHIFT 8
|
||||
#define DCR_CSHT_MASK GENMASK(10, 8)
|
||||
#define DCR_CSHT(n) (((n) << DCR_CSHT_SHIFT) & DCR_CSHT_MASK)
|
||||
#define DCR_FSIZE_SHIFT 16
|
||||
#define DCR_FSIZE_MASK GENMASK(20, 16)
|
||||
#define DCR_FSIZE(n) (((n) << DCR_FSIZE_SHIFT) & DCR_FSIZE_MASK)
|
||||
|
||||
#define QUADSPI_SR 0x08
|
||||
#define SR_TEF BIT(0)
|
||||
#define SR_TCF BIT(1)
|
||||
#define SR_FTF BIT(2)
|
||||
#define SR_SMF BIT(3)
|
||||
#define SR_TOF BIT(4)
|
||||
#define SR_BUSY BIT(5)
|
||||
#define SR_FLEVEL_SHIFT 8
|
||||
#define SR_FLEVEL_MASK GENMASK(13, 8)
|
||||
|
||||
#define QUADSPI_FCR 0x0c
|
||||
#define FCR_CTCF BIT(1)
|
||||
|
||||
#define QUADSPI_DLR 0x10
|
||||
|
||||
#define QUADSPI_CCR 0x14
|
||||
#define CCR_INST_SHIFT 0
|
||||
#define CCR_INST_MASK GENMASK(7, 0)
|
||||
#define CCR_INST(n) (((n) << CCR_INST_SHIFT) & CCR_INST_MASK)
|
||||
#define CCR_IMODE_NONE (0U << 8)
|
||||
#define CCR_IMODE_1 (1U << 8)
|
||||
#define CCR_IMODE_2 (2U << 8)
|
||||
#define CCR_IMODE_4 (3U << 8)
|
||||
#define CCR_ADMODE_NONE (0U << 10)
|
||||
#define CCR_ADMODE_1 (1U << 10)
|
||||
#define CCR_ADMODE_2 (2U << 10)
|
||||
#define CCR_ADMODE_4 (3U << 10)
|
||||
#define CCR_ADSIZE_SHIFT 12
|
||||
#define CCR_ADSIZE_MASK GENMASK(13, 12)
|
||||
#define CCR_ADSIZE(n) (((n) << CCR_ADSIZE_SHIFT) & CCR_ADSIZE_MASK)
|
||||
#define CCR_ABMODE_NONE (0U << 14)
|
||||
#define CCR_ABMODE_1 (1U << 14)
|
||||
#define CCR_ABMODE_2 (2U << 14)
|
||||
#define CCR_ABMODE_4 (3U << 14)
|
||||
#define CCR_ABSIZE_8 (0U << 16)
|
||||
#define CCR_ABSIZE_16 (1U << 16)
|
||||
#define CCR_ABSIZE_24 (2U << 16)
|
||||
#define CCR_ABSIZE_32 (3U << 16)
|
||||
#define CCR_DCYC_SHIFT 18
|
||||
#define CCR_DCYC_MASK GENMASK(22, 18)
|
||||
#define CCR_DCYC(n) (((n) << CCR_DCYC_SHIFT) & CCR_DCYC_MASK)
|
||||
#define CCR_DMODE_NONE (0U << 24)
|
||||
#define CCR_DMODE_1 (1U << 24)
|
||||
#define CCR_DMODE_2 (2U << 24)
|
||||
#define CCR_DMODE_4 (3U << 24)
|
||||
#define CCR_FMODE_INDW (0U << 26)
|
||||
#define CCR_FMODE_INDR (1U << 26)
|
||||
#define CCR_FMODE_APM (2U << 26)
|
||||
#define CCR_FMODE_MM (3U << 26)
|
||||
|
||||
#define QUADSPI_AR 0x18
|
||||
#define QUADSPI_ABR 0x1c
|
||||
#define QUADSPI_DR 0x20
|
||||
#define QUADSPI_PSMKR 0x24
|
||||
#define QUADSPI_PSMAR 0x28
|
||||
#define QUADSPI_PIR 0x2c
|
||||
#define QUADSPI_LPTR 0x30
|
||||
#define LPTR_DFT_TIMEOUT 0x10
|
||||
|
||||
#define FSIZE_VAL(size) (__fls(size) - 1)
|
||||
|
||||
#define STM32_MAX_MMAP_SZ SZ_256M
|
||||
#define STM32_MAX_NORCHIP 2
|
||||
|
||||
#define STM32_QSPI_FIFO_TIMEOUT_US 30000
|
||||
#define STM32_QSPI_BUSY_TIMEOUT_US 100000
|
||||
|
||||
struct stm32_qspi_flash {
|
||||
struct spi_nor nor;
|
||||
struct stm32_qspi *qspi;
|
||||
u32 cs;
|
||||
u32 fsize;
|
||||
u32 presc;
|
||||
u32 read_mode;
|
||||
bool registered;
|
||||
};
|
||||
|
||||
struct stm32_qspi {
|
||||
struct device *dev;
|
||||
void __iomem *io_base;
|
||||
void __iomem *mm_base;
|
||||
resource_size_t mm_size;
|
||||
u32 nor_num;
|
||||
struct clk *clk;
|
||||
u32 clk_rate;
|
||||
struct stm32_qspi_flash flash[STM32_MAX_NORCHIP];
|
||||
struct completion cmd_completion;
|
||||
|
||||
/*
|
||||
* to protect device configuration, could be different between
|
||||
* 2 flash access (bk1, bk2)
|
||||
*/
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
struct stm32_qspi_cmd {
|
||||
u8 addr_width;
|
||||
u8 dummy;
|
||||
bool tx_data;
|
||||
u8 opcode;
|
||||
u32 framemode;
|
||||
u32 qspimode;
|
||||
u32 addr;
|
||||
size_t len;
|
||||
void *buf;
|
||||
};
|
||||
|
||||
static int stm32_qspi_wait_cmd(struct stm32_qspi *qspi)
|
||||
{
|
||||
u32 cr;
|
||||
int err = 0;
|
||||
|
||||
if (readl_relaxed(qspi->io_base + QUADSPI_SR) & SR_TCF)
|
||||
return 0;
|
||||
|
||||
reinit_completion(&qspi->cmd_completion);
|
||||
cr = readl_relaxed(qspi->io_base + QUADSPI_CR);
|
||||
writel_relaxed(cr | CR_TCIE, qspi->io_base + QUADSPI_CR);
|
||||
|
||||
if (!wait_for_completion_interruptible_timeout(&qspi->cmd_completion,
|
||||
msecs_to_jiffies(1000)))
|
||||
err = -ETIMEDOUT;
|
||||
|
||||
writel_relaxed(cr, qspi->io_base + QUADSPI_CR);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int stm32_qspi_wait_nobusy(struct stm32_qspi *qspi)
|
||||
{
|
||||
u32 sr;
|
||||
|
||||
return readl_relaxed_poll_timeout(qspi->io_base + QUADSPI_SR, sr,
|
||||
!(sr & SR_BUSY), 10,
|
||||
STM32_QSPI_BUSY_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static void stm32_qspi_set_framemode(struct spi_nor *nor,
|
||||
struct stm32_qspi_cmd *cmd, bool read)
|
||||
{
|
||||
u32 dmode = CCR_DMODE_1;
|
||||
|
||||
cmd->framemode = CCR_IMODE_1;
|
||||
|
||||
if (read) {
|
||||
switch (nor->flash_read) {
|
||||
case SPI_NOR_NORMAL:
|
||||
case SPI_NOR_FAST:
|
||||
dmode = CCR_DMODE_1;
|
||||
break;
|
||||
case SPI_NOR_DUAL:
|
||||
dmode = CCR_DMODE_2;
|
||||
break;
|
||||
case SPI_NOR_QUAD:
|
||||
dmode = CCR_DMODE_4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cmd->framemode |= cmd->tx_data ? dmode : 0;
|
||||
cmd->framemode |= cmd->addr_width ? CCR_ADMODE_1 : 0;
|
||||
}
|
||||
|
||||
static void stm32_qspi_read_fifo(u8 *val, void __iomem *addr)
|
||||
{
|
||||
*val = readb_relaxed(addr);
|
||||
}
|
||||
|
||||
static void stm32_qspi_write_fifo(u8 *val, void __iomem *addr)
|
||||
{
|
||||
writeb_relaxed(*val, addr);
|
||||
}
|
||||
|
||||
static int stm32_qspi_tx_poll(struct stm32_qspi *qspi,
|
||||
const struct stm32_qspi_cmd *cmd)
|
||||
{
|
||||
void (*tx_fifo)(u8 *, void __iomem *);
|
||||
u32 len = cmd->len, sr;
|
||||
u8 *buf = cmd->buf;
|
||||
int ret;
|
||||
|
||||
if (cmd->qspimode == CCR_FMODE_INDW)
|
||||
tx_fifo = stm32_qspi_write_fifo;
|
||||
else
|
||||
tx_fifo = stm32_qspi_read_fifo;
|
||||
|
||||
while (len--) {
|
||||
ret = readl_relaxed_poll_timeout(qspi->io_base + QUADSPI_SR,
|
||||
sr, (sr & SR_FTF), 10,
|
||||
STM32_QSPI_FIFO_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(qspi->dev, "fifo timeout (stat:%#x)\n", sr);
|
||||
break;
|
||||
}
|
||||
tx_fifo(buf++, qspi->io_base + QUADSPI_DR);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_qspi_tx_mm(struct stm32_qspi *qspi,
|
||||
const struct stm32_qspi_cmd *cmd)
|
||||
{
|
||||
memcpy_fromio(cmd->buf, qspi->mm_base + cmd->addr, cmd->len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_qspi_tx(struct stm32_qspi *qspi,
|
||||
const struct stm32_qspi_cmd *cmd)
|
||||
{
|
||||
if (!cmd->tx_data)
|
||||
return 0;
|
||||
|
||||
if (cmd->qspimode == CCR_FMODE_MM)
|
||||
return stm32_qspi_tx_mm(qspi, cmd);
|
||||
|
||||
return stm32_qspi_tx_poll(qspi, cmd);
|
||||
}
|
||||
|
||||
static int stm32_qspi_send(struct stm32_qspi_flash *flash,
|
||||
const struct stm32_qspi_cmd *cmd)
|
||||
{
|
||||
struct stm32_qspi *qspi = flash->qspi;
|
||||
u32 ccr, dcr, cr;
|
||||
int err;
|
||||
|
||||
err = stm32_qspi_wait_nobusy(qspi);
|
||||
if (err)
|
||||
goto abort;
|
||||
|
||||
dcr = readl_relaxed(qspi->io_base + QUADSPI_DCR) & ~DCR_FSIZE_MASK;
|
||||
dcr |= DCR_FSIZE(flash->fsize);
|
||||
writel_relaxed(dcr, qspi->io_base + QUADSPI_DCR);
|
||||
|
||||
cr = readl_relaxed(qspi->io_base + QUADSPI_CR);
|
||||
cr &= ~CR_PRESC_MASK & ~CR_FSEL;
|
||||
cr |= CR_PRESC(flash->presc);
|
||||
cr |= flash->cs ? CR_FSEL : 0;
|
||||
writel_relaxed(cr, qspi->io_base + QUADSPI_CR);
|
||||
|
||||
if (cmd->tx_data)
|
||||
writel_relaxed(cmd->len - 1, qspi->io_base + QUADSPI_DLR);
|
||||
|
||||
ccr = cmd->framemode | cmd->qspimode;
|
||||
|
||||
if (cmd->dummy)
|
||||
ccr |= CCR_DCYC(cmd->dummy);
|
||||
|
||||
if (cmd->addr_width)
|
||||
ccr |= CCR_ADSIZE(cmd->addr_width - 1);
|
||||
|
||||
ccr |= CCR_INST(cmd->opcode);
|
||||
writel_relaxed(ccr, qspi->io_base + QUADSPI_CCR);
|
||||
|
||||
if (cmd->addr_width && cmd->qspimode != CCR_FMODE_MM)
|
||||
writel_relaxed(cmd->addr, qspi->io_base + QUADSPI_AR);
|
||||
|
||||
err = stm32_qspi_tx(qspi, cmd);
|
||||
if (err)
|
||||
goto abort;
|
||||
|
||||
if (cmd->qspimode != CCR_FMODE_MM) {
|
||||
err = stm32_qspi_wait_cmd(qspi);
|
||||
if (err)
|
||||
goto abort;
|
||||
writel_relaxed(FCR_CTCF, qspi->io_base + QUADSPI_FCR);
|
||||
}
|
||||
|
||||
return err;
|
||||
|
||||
abort:
|
||||
cr = readl_relaxed(qspi->io_base + QUADSPI_CR) | CR_ABORT;
|
||||
writel_relaxed(cr, qspi->io_base + QUADSPI_CR);
|
||||
|
||||
dev_err(qspi->dev, "%s abort err:%d\n", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int stm32_qspi_read_reg(struct spi_nor *nor,
|
||||
u8 opcode, u8 *buf, int len)
|
||||
{
|
||||
struct stm32_qspi_flash *flash = nor->priv;
|
||||
struct device *dev = flash->qspi->dev;
|
||||
struct stm32_qspi_cmd cmd;
|
||||
|
||||
dev_dbg(dev, "read_reg: cmd:%#.2x buf:%p len:%#x\n", opcode, buf, len);
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.opcode = opcode;
|
||||
cmd.tx_data = true;
|
||||
cmd.len = len;
|
||||
cmd.buf = buf;
|
||||
cmd.qspimode = CCR_FMODE_INDR;
|
||||
|
||||
stm32_qspi_set_framemode(nor, &cmd, false);
|
||||
|
||||
return stm32_qspi_send(flash, &cmd);
|
||||
}
|
||||
|
||||
static int stm32_qspi_write_reg(struct spi_nor *nor, u8 opcode,
|
||||
u8 *buf, int len)
|
||||
{
|
||||
struct stm32_qspi_flash *flash = nor->priv;
|
||||
struct device *dev = flash->qspi->dev;
|
||||
struct stm32_qspi_cmd cmd;
|
||||
|
||||
dev_dbg(dev, "write_reg: cmd:%#.2x buf:%p len:%#x\n", opcode, buf, len);
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.opcode = opcode;
|
||||
cmd.tx_data = !!(buf && len > 0);
|
||||
cmd.len = len;
|
||||
cmd.buf = buf;
|
||||
cmd.qspimode = CCR_FMODE_INDW;
|
||||
|
||||
stm32_qspi_set_framemode(nor, &cmd, false);
|
||||
|
||||
return stm32_qspi_send(flash, &cmd);
|
||||
}
|
||||
|
||||
static ssize_t stm32_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
u_char *buf)
|
||||
{
|
||||
struct stm32_qspi_flash *flash = nor->priv;
|
||||
struct stm32_qspi *qspi = flash->qspi;
|
||||
struct stm32_qspi_cmd cmd;
|
||||
int err;
|
||||
|
||||
dev_dbg(qspi->dev, "read(%#.2x): buf:%p from:%#.8x len:%#x\n",
|
||||
nor->read_opcode, buf, (u32)from, len);
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.opcode = nor->read_opcode;
|
||||
cmd.addr_width = nor->addr_width;
|
||||
cmd.addr = (u32)from;
|
||||
cmd.tx_data = true;
|
||||
cmd.dummy = nor->read_dummy;
|
||||
cmd.len = len;
|
||||
cmd.buf = buf;
|
||||
cmd.qspimode = flash->read_mode;
|
||||
|
||||
stm32_qspi_set_framemode(nor, &cmd, true);
|
||||
err = stm32_qspi_send(flash, &cmd);
|
||||
|
||||
return err ? err : len;
|
||||
}
|
||||
|
||||
static ssize_t stm32_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
const u_char *buf)
|
||||
{
|
||||
struct stm32_qspi_flash *flash = nor->priv;
|
||||
struct device *dev = flash->qspi->dev;
|
||||
struct stm32_qspi_cmd cmd;
|
||||
int err;
|
||||
|
||||
dev_dbg(dev, "write(%#.2x): buf:%p to:%#.8x len:%#x\n",
|
||||
nor->program_opcode, buf, (u32)to, len);
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.opcode = nor->program_opcode;
|
||||
cmd.addr_width = nor->addr_width;
|
||||
cmd.addr = (u32)to;
|
||||
cmd.tx_data = true;
|
||||
cmd.len = len;
|
||||
cmd.buf = (void *)buf;
|
||||
cmd.qspimode = CCR_FMODE_INDW;
|
||||
|
||||
stm32_qspi_set_framemode(nor, &cmd, false);
|
||||
err = stm32_qspi_send(flash, &cmd);
|
||||
|
||||
return err ? err : len;
|
||||
}
|
||||
|
||||
static int stm32_qspi_erase(struct spi_nor *nor, loff_t offs)
|
||||
{
|
||||
struct stm32_qspi_flash *flash = nor->priv;
|
||||
struct device *dev = flash->qspi->dev;
|
||||
struct stm32_qspi_cmd cmd;
|
||||
|
||||
dev_dbg(dev, "erase(%#.2x):offs:%#x\n", nor->erase_opcode, (u32)offs);
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.opcode = nor->erase_opcode;
|
||||
cmd.addr_width = nor->addr_width;
|
||||
cmd.addr = (u32)offs;
|
||||
cmd.qspimode = CCR_FMODE_INDW;
|
||||
|
||||
stm32_qspi_set_framemode(nor, &cmd, false);
|
||||
|
||||
return stm32_qspi_send(flash, &cmd);
|
||||
}
|
||||
|
||||
static irqreturn_t stm32_qspi_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct stm32_qspi *qspi = (struct stm32_qspi *)dev_id;
|
||||
u32 cr, sr, fcr = 0;
|
||||
|
||||
cr = readl_relaxed(qspi->io_base + QUADSPI_CR);
|
||||
sr = readl_relaxed(qspi->io_base + QUADSPI_SR);
|
||||
|
||||
if ((cr & CR_TCIE) && (sr & SR_TCF)) {
|
||||
/* tx complete */
|
||||
fcr |= FCR_CTCF;
|
||||
complete(&qspi->cmd_completion);
|
||||
} else {
|
||||
dev_info_ratelimited(qspi->dev, "spurious interrupt\n");
|
||||
}
|
||||
|
||||
writel_relaxed(fcr, qspi->io_base + QUADSPI_FCR);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int stm32_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops)
|
||||
{
|
||||
struct stm32_qspi_flash *flash = nor->priv;
|
||||
struct stm32_qspi *qspi = flash->qspi;
|
||||
|
||||
mutex_lock(&qspi->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
|
||||
{
|
||||
struct stm32_qspi_flash *flash = nor->priv;
|
||||
struct stm32_qspi *qspi = flash->qspi;
|
||||
|
||||
mutex_unlock(&qspi->lock);
|
||||
}
|
||||
|
||||
static int stm32_qspi_flash_setup(struct stm32_qspi *qspi,
|
||||
struct device_node *np)
|
||||
{
|
||||
u32 width, flash_read, presc, cs_num, max_rate = 0;
|
||||
struct stm32_qspi_flash *flash;
|
||||
struct mtd_info *mtd;
|
||||
int ret;
|
||||
|
||||
of_property_read_u32(np, "reg", &cs_num);
|
||||
if (cs_num >= STM32_MAX_NORCHIP)
|
||||
return -EINVAL;
|
||||
|
||||
of_property_read_u32(np, "spi-max-frequency", &max_rate);
|
||||
if (!max_rate)
|
||||
return -EINVAL;
|
||||
|
||||
presc = DIV_ROUND_UP(qspi->clk_rate, max_rate) - 1;
|
||||
|
||||
if (of_property_read_u32(np, "spi-rx-bus-width", &width))
|
||||
width = 1;
|
||||
|
||||
if (width == 4)
|
||||
flash_read = SPI_NOR_QUAD;
|
||||
else if (width == 2)
|
||||
flash_read = SPI_NOR_DUAL;
|
||||
else if (width == 1)
|
||||
flash_read = SPI_NOR_NORMAL;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
flash = &qspi->flash[cs_num];
|
||||
flash->qspi = qspi;
|
||||
flash->cs = cs_num;
|
||||
flash->presc = presc;
|
||||
|
||||
flash->nor.dev = qspi->dev;
|
||||
spi_nor_set_flash_node(&flash->nor, np);
|
||||
flash->nor.priv = flash;
|
||||
mtd = &flash->nor.mtd;
|
||||
|
||||
flash->nor.read = stm32_qspi_read;
|
||||
flash->nor.write = stm32_qspi_write;
|
||||
flash->nor.erase = stm32_qspi_erase;
|
||||
flash->nor.read_reg = stm32_qspi_read_reg;
|
||||
flash->nor.write_reg = stm32_qspi_write_reg;
|
||||
flash->nor.prepare = stm32_qspi_prep;
|
||||
flash->nor.unprepare = stm32_qspi_unprep;
|
||||
|
||||
writel_relaxed(LPTR_DFT_TIMEOUT, qspi->io_base + QUADSPI_LPTR);
|
||||
|
||||
writel_relaxed(CR_PRESC(presc) | CR_FTHRES(3) | CR_TCEN | CR_SSHIFT
|
||||
| CR_EN, qspi->io_base + QUADSPI_CR);
|
||||
|
||||
/*
|
||||
* in stm32 qspi controller, QUADSPI_DCR register has a fsize field
|
||||
* which define the size of nor flash.
|
||||
* if fsize is NULL, the controller can't sent spi-nor command.
|
||||
* set a temporary value just to discover the nor flash with
|
||||
* "spi_nor_scan". After, the right value (mtd->size) can be set.
|
||||
*/
|
||||
flash->fsize = FSIZE_VAL(SZ_1K);
|
||||
|
||||
ret = spi_nor_scan(&flash->nor, NULL, flash_read);
|
||||
if (ret) {
|
||||
dev_err(qspi->dev, "device scan failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
flash->fsize = FSIZE_VAL(mtd->size);
|
||||
|
||||
flash->read_mode = CCR_FMODE_MM;
|
||||
if (mtd->size > qspi->mm_size)
|
||||
flash->read_mode = CCR_FMODE_INDR;
|
||||
|
||||
writel_relaxed(DCR_CSHT(1), qspi->io_base + QUADSPI_DCR);
|
||||
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(qspi->dev, "mtd device parse failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
flash->registered = true;
|
||||
|
||||
dev_dbg(qspi->dev, "read mm:%s cs:%d bus:%d\n",
|
||||
flash->read_mode == CCR_FMODE_MM ? "yes" : "no", cs_num, width);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_qspi_mtd_free(struct stm32_qspi *qspi)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < STM32_MAX_NORCHIP; i++)
|
||||
if (qspi->flash[i].registered)
|
||||
mtd_device_unregister(&qspi->flash[i].nor.mtd);
|
||||
}
|
||||
|
||||
static int stm32_qspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *flash_np;
|
||||
struct reset_control *rstc;
|
||||
struct stm32_qspi *qspi;
|
||||
struct resource *res;
|
||||
int ret, irq;
|
||||
|
||||
qspi = devm_kzalloc(dev, sizeof(*qspi), GFP_KERNEL);
|
||||
if (!qspi)
|
||||
return -ENOMEM;
|
||||
|
||||
qspi->nor_num = of_get_child_count(dev->of_node);
|
||||
if (!qspi->nor_num || qspi->nor_num > STM32_MAX_NORCHIP)
|
||||
return -ENODEV;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi");
|
||||
qspi->io_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(qspi->io_base))
|
||||
return PTR_ERR(qspi->io_base);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_mm");
|
||||
qspi->mm_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(qspi->mm_base))
|
||||
return PTR_ERR(qspi->mm_base);
|
||||
|
||||
qspi->mm_size = resource_size(res);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
ret = devm_request_irq(dev, irq, stm32_qspi_irq, 0,
|
||||
dev_name(dev), qspi);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request irq\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
init_completion(&qspi->cmd_completion);
|
||||
|
||||
qspi->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(qspi->clk))
|
||||
return PTR_ERR(qspi->clk);
|
||||
|
||||
qspi->clk_rate = clk_get_rate(qspi->clk);
|
||||
if (!qspi->clk_rate)
|
||||
return -EINVAL;
|
||||
|
||||
ret = clk_prepare_enable(qspi->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "can not enable the clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
rstc = devm_reset_control_get(dev, NULL);
|
||||
if (!IS_ERR(rstc)) {
|
||||
reset_control_assert(rstc);
|
||||
udelay(2);
|
||||
reset_control_deassert(rstc);
|
||||
}
|
||||
|
||||
qspi->dev = dev;
|
||||
platform_set_drvdata(pdev, qspi);
|
||||
mutex_init(&qspi->lock);
|
||||
|
||||
for_each_available_child_of_node(dev->of_node, flash_np) {
|
||||
ret = stm32_qspi_flash_setup(qspi, flash_np);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to setup flash chip\n");
|
||||
goto err_flash;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_flash:
|
||||
mutex_destroy(&qspi->lock);
|
||||
stm32_qspi_mtd_free(qspi);
|
||||
|
||||
clk_disable_unprepare(qspi->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_qspi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32_qspi *qspi = platform_get_drvdata(pdev);
|
||||
|
||||
/* disable qspi */
|
||||
writel_relaxed(0, qspi->io_base + QUADSPI_CR);
|
||||
|
||||
stm32_qspi_mtd_free(qspi);
|
||||
mutex_destroy(&qspi->lock);
|
||||
|
||||
clk_disable_unprepare(qspi->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id stm32_qspi_match[] = {
|
||||
{.compatible = "st,stm32f469-qspi"},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_qspi_match);
|
||||
|
||||
static struct platform_driver stm32_qspi_driver = {
|
||||
.probe = stm32_qspi_probe,
|
||||
.remove = stm32_qspi_remove,
|
||||
.driver = {
|
||||
.name = "stm32-quadspi",
|
||||
.of_match_table = stm32_qspi_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(stm32_qspi_driver);
|
||||
|
||||
MODULE_AUTHOR("Ludovic Barre <ludovic.barre@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics STM32 quad spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1366,7 +1366,7 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
jffs2_add_ino_cache(c, f->inocache);
|
||||
}
|
||||
if (!f->inocache) {
|
||||
JFFS2_ERROR("requestied to read an nonexistent ino %u\n", ino);
|
||||
JFFS2_ERROR("requested to read a nonexistent ino %u\n", ino);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
@ -388,7 +388,7 @@ static inline void mtd_set_of_node(struct mtd_info *mtd,
|
||||
|
||||
static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd)
|
||||
{
|
||||
return mtd->dev.of_node;
|
||||
return dev_of_node(&mtd->dev);
|
||||
}
|
||||
|
||||
static inline int mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops)
|
||||
|
@ -366,26 +366,6 @@ struct onfi_ext_param_page {
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct nand_onfi_vendor_micron {
|
||||
u8 two_plane_read;
|
||||
u8 read_cache;
|
||||
u8 read_unique_id;
|
||||
u8 dq_imped;
|
||||
u8 dq_imped_num_settings;
|
||||
u8 dq_imped_feat_addr;
|
||||
u8 rb_pulldown_strength;
|
||||
u8 rb_pulldown_strength_feat_addr;
|
||||
u8 rb_pulldown_strength_num_settings;
|
||||
u8 otp_mode;
|
||||
u8 otp_page_start;
|
||||
u8 otp_data_prot_addr;
|
||||
u8 otp_num_pages;
|
||||
u8 otp_feat_addr;
|
||||
u8 read_retry_options;
|
||||
u8 reserved[72];
|
||||
u8 param_revision;
|
||||
} __packed;
|
||||
|
||||
struct jedec_ecc_info {
|
||||
u8 ecc_bits;
|
||||
u8 codeword_size;
|
||||
@ -464,6 +444,17 @@ struct nand_jedec_params {
|
||||
__le16 crc;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct nand_id - NAND id structure
|
||||
* @data: buffer containing the id bytes. Currently 8 bytes large, but can
|
||||
* be extended if required.
|
||||
* @len: ID length.
|
||||
*/
|
||||
struct nand_id {
|
||||
u8 data[8];
|
||||
int len;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
|
||||
* @lock: protection lock
|
||||
@ -525,7 +516,7 @@ static inline void nand_hw_control_init(struct nand_hw_control *nfc)
|
||||
* out-of-band data).
|
||||
* @read_page: function to read a page according to the ECC generator
|
||||
* requirements; returns maximum number of bitflips corrected in
|
||||
* any single ECC step, 0 if bitflips uncorrectable, -EIO hw error
|
||||
* any single ECC step, -EIO hw error
|
||||
* @read_subpage: function to read parts of the page covered by ECC;
|
||||
* returns same as read_page()
|
||||
* @write_subpage: function to write parts of the page covered by ECC.
|
||||
@ -720,6 +711,20 @@ nand_get_sdr_timings(const struct nand_data_interface *conf)
|
||||
return &conf->timings.sdr;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct nand_manufacturer_ops - NAND Manufacturer operations
|
||||
* @detect: detect the NAND memory organization and capabilities
|
||||
* @init: initialize all vendor specific fields (like the ->read_retry()
|
||||
* implementation) if any.
|
||||
* @cleanup: the ->init() function may have allocated resources, ->cleanup()
|
||||
* is here to let vendor specific code release those resources.
|
||||
*/
|
||||
struct nand_manufacturer_ops {
|
||||
void (*detect)(struct nand_chip *chip);
|
||||
int (*init)(struct nand_chip *chip);
|
||||
void (*cleanup)(struct nand_chip *chip);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_chip - NAND Private Flash Chip Data
|
||||
* @mtd: MTD device registered to the MTD framework
|
||||
@ -750,6 +755,7 @@ nand_get_sdr_timings(const struct nand_data_interface *conf)
|
||||
* setting the read-retry mode. Mostly needed for MLC NAND.
|
||||
* @ecc: [BOARDSPECIFIC] ECC control structure
|
||||
* @buffers: buffer structure for read/write
|
||||
* @buf_align: minimum buffer alignment required by a platform
|
||||
* @hwcontrol: platform-specific hardware control structure
|
||||
* @erase: [REPLACEABLE] erase function
|
||||
* @scan_bbt: [REPLACEABLE] function to scan bad block table
|
||||
@ -793,6 +799,7 @@ nand_get_sdr_timings(const struct nand_data_interface *conf)
|
||||
* @pagebuf_bitflips: [INTERN] holds the bitflip count for the page which is
|
||||
* currently in data_buf.
|
||||
* @subpagesize: [INTERN] holds the subpagesize
|
||||
* @id: [INTERN] holds NAND ID
|
||||
* @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded),
|
||||
* non 0 if ONFI supported.
|
||||
* @jedec_version: [INTERN] holds the chip JEDEC version (BCD encoded),
|
||||
@ -822,7 +829,7 @@ nand_get_sdr_timings(const struct nand_data_interface *conf)
|
||||
* @errstat: [OPTIONAL] hardware specific function to perform
|
||||
* additional error status checks (determine if errors are
|
||||
* correctable).
|
||||
* @write_page: [REPLACEABLE] High-level page write function
|
||||
* @manufacturer: [INTERN] Contains manufacturer information
|
||||
*/
|
||||
|
||||
struct nand_chip {
|
||||
@ -847,9 +854,6 @@ struct nand_chip {
|
||||
int (*scan_bbt)(struct mtd_info *mtd);
|
||||
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
|
||||
int status, int page);
|
||||
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offset, int data_len, const uint8_t *buf,
|
||||
int oob_required, int page, int cached, int raw);
|
||||
int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int feature_addr, uint8_t *subfeature_para);
|
||||
int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
@ -881,6 +885,7 @@ struct nand_chip {
|
||||
int badblockpos;
|
||||
int badblockbits;
|
||||
|
||||
struct nand_id id;
|
||||
int onfi_version;
|
||||
int jedec_version;
|
||||
union {
|
||||
@ -901,6 +906,7 @@ struct nand_chip {
|
||||
|
||||
struct nand_ecc_ctrl ecc;
|
||||
struct nand_buffers *buffers;
|
||||
unsigned long buf_align;
|
||||
struct nand_hw_control hwcontrol;
|
||||
|
||||
uint8_t *bbt;
|
||||
@ -910,6 +916,11 @@ struct nand_chip {
|
||||
struct nand_bbt_descr *badblock_pattern;
|
||||
|
||||
void *priv;
|
||||
|
||||
struct {
|
||||
const struct nand_manufacturer *desc;
|
||||
void *priv;
|
||||
} manufacturer;
|
||||
};
|
||||
|
||||
extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops;
|
||||
@ -946,6 +957,17 @@ static inline void nand_set_controller_data(struct nand_chip *chip, void *priv)
|
||||
chip->priv = priv;
|
||||
}
|
||||
|
||||
static inline void nand_set_manufacturer_data(struct nand_chip *chip,
|
||||
void *priv)
|
||||
{
|
||||
chip->manufacturer.priv = priv;
|
||||
}
|
||||
|
||||
static inline void *nand_get_manufacturer_data(struct nand_chip *chip)
|
||||
{
|
||||
return chip->manufacturer.priv;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAND Flash Manufacturer ID Codes
|
||||
*/
|
||||
@ -1049,17 +1071,33 @@ struct nand_flash_dev {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nand_manufacturers - NAND Flash Manufacturer ID Structure
|
||||
* struct nand_manufacturer - NAND Flash Manufacturer structure
|
||||
* @name: Manufacturer name
|
||||
* @id: manufacturer ID code of device.
|
||||
* @ops: manufacturer operations
|
||||
*/
|
||||
struct nand_manufacturers {
|
||||
struct nand_manufacturer {
|
||||
int id;
|
||||
char *name;
|
||||
const struct nand_manufacturer_ops *ops;
|
||||
};
|
||||
|
||||
const struct nand_manufacturer *nand_get_manufacturer(u8 id);
|
||||
|
||||
static inline const char *
|
||||
nand_manufacturer_name(const struct nand_manufacturer *manufacturer)
|
||||
{
|
||||
return manufacturer ? manufacturer->name : "Unknown";
|
||||
}
|
||||
|
||||
extern struct nand_flash_dev nand_flash_ids[];
|
||||
extern struct nand_manufacturers nand_manuf_ids[];
|
||||
|
||||
extern const struct nand_manufacturer_ops toshiba_nand_manuf_ops;
|
||||
extern const struct nand_manufacturer_ops samsung_nand_manuf_ops;
|
||||
extern const struct nand_manufacturer_ops hynix_nand_manuf_ops;
|
||||
extern const struct nand_manufacturer_ops micron_nand_manuf_ops;
|
||||
extern const struct nand_manufacturer_ops amd_nand_manuf_ops;
|
||||
extern const struct nand_manufacturer_ops macronix_nand_manuf_ops;
|
||||
|
||||
int nand_default_bbt(struct mtd_info *mtd);
|
||||
int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs);
|
||||
@ -1226,4 +1264,6 @@ int nand_reset(struct nand_chip *chip, int chipnr);
|
||||
/* Free resources held by the NAND device */
|
||||
void nand_cleanup(struct nand_chip *chip);
|
||||
|
||||
/* Default extended ID decoding function */
|
||||
void nand_decode_ext_id(struct nand_chip *chip);
|
||||
#endif /* __LINUX_MTD_NAND_H */
|
||||
|
Loading…
Reference in New Issue
Block a user