Re: [PATCH] x86/mm: fix compilation error for unknown type name pgprot_t
On 4/27/2020 8:41 PM, Abhishek Sahu wrote: > On 4/21/2020 11:18 AM, Abhishek Sahu wrote: >> On 4/20/2020 10:14 PM, Logan Gunthorpe wrote: >>> On 2020-04-20 2:22 a.m., Abhishek Sahu wrote: >>>> commit 30796e18c299 ("x86/mm: introduce __set_memory_prot()") >>>> added the function __set_memory_prot() which uses pgprot_t in >>>> function prototype. The currently included header files in >>>> do not include type name >>>> pgprot_t. The pgprot_t is defined in . >>>> >>>> Fixes: 30796e18c299 ("x86/mm: introduce __set_memory_prot()") >>>> Signed-off-by: Abhishek Sahu >>> >>> I assume this is only hit with a specific driver or something that >>> includes set_memory.h without including pgtable_types.h before hand. >>> >>> It certainly compiles fine for me and the kbuild robot has had lots of >>> time to test it with random configs. >>> >>> Reviewed-by: Logan Gunthorpe >> >> Thanks Logan. >> >> Yes. The mainline is compiling fine with default config. >> This problem is coming while compiling Out of tree NVIDIA driver. >> But this problem can happen for the drivers which don't include >> . I am not sure if this problem will come with >> some other configs. Normally, the header files should not be >> dependent upon the callers. >> > > Is it possible to get this patch merged for next rc of 5.7. > This issue started coming from 5.7-rc1. > Could someone help in reviewing and getting this merged in the next rc of 5.7 version. The NVIDIA driver compilation is failing from 5.7-rc1. This issue can happen for any other driver also which is not including in the driver. Thanks, Abhishek
Re: [PATCH v2 0/2] PCI: device link quirk for NVIDIA GPU
On 6/14/2019 2:27 AM, Bjorn Helgaas wrote: > On Thu, Jun 06, 2019 at 02:52:23PM +0530, Abhishek Sahu wrote: >> * v2: >> >> 1. Make the pci device link helper function generic which can be >>used for other multi-function PCI devices also. >> 2. Minor changes in comments and commit logs. >> >> * v1: >> >> NVIDIA Turing GPU [1] has hardware support for USB Type-C and >> VirtualLink [2]. The Turing GPU is a multi-function PCI device >> which has the following four functions: >> >> - VGA display controller (Function 0) >> - Audio controller (Function 1) >> - USB xHCI Host controller (Function 2) >> - USB Type-C USCI controller (Function 3) >> >> Currently NVIDIA and Nouveau GPU drivers only manage function 0. >> Rest of the functions are managed by other drivers. These functions >> internally in the hardware are tightly coupled. When function 0 goes >> in runtime suspended state, then it will do power gating for most of >> the hardware blocks. Some of these hardware blocks are used by >> the other PCI functions, which leads to functional failure. In the >> mainline kernel, the device link is present between >> function 0 and function 1. This patch series deals with creating >> a similar kind of device link between function 0 and >> functions 2 and 3. >> >> [1] >> https://www.nvidia.com/content/dam/en-zz/Solutions/design-visualization/technologies/turing-architecture/NVIDIA-Turing-Architecture-Whitepaper.pdf >> [2] https://en.wikipedia.org/wiki/VirtualLink >> >> Abhishek Sahu (2): >> PCI: Code reorganization for creating device link >> PCI: Create device link for NVIDIA GPU > > Applied to pci/misc for v5.3, thanks! Thanks Bjorn for your review and support! The runtime PM changes in USB Type-C USCI driver is also applied for v5.3 https://marc.info/?l=linux-usb=155994544705901=2 It will help in achieving run-time PM for Turing GPUs in v5.3. Regards, Abhishek
[PATCH v2 2/2] PCI: Create device link for NVIDIA GPU
NVIDIA Turing GPUs include hardware support for USB Type-C and VirtualLink. It helps in delivering the power, display, and data required to power VR headsets through a single USB Type-C connector. The Turing GPU is a multi-function PCI device. It has the following four functions: - VGA display controller (Function 0) - Audio controller (Function 1) - USB xHCI Host controller (Function 2) - USB Type-C USCI controller (Function 3) The function 0 is tightly coupled with other functions in the hardware. When function 0 goes in D3 state, then it will do power gating for most of the hardware blocks. Some of these hardware blocks are being used by other functions which leads to functional failure. So if any of these functions (1/2/3) are in D0 state, then function 0 should also be in D0 state. 'commit 07f4f97d7b4b ("vga_switcheroo: Use device link for HDA controller")' creates the device link from function 1 to function 0. A similar kind of device link needs to be created between function 0 and functions 2 and 3 for NVIDIA Turing GPU. This patch does the same and creates the required device links. It will make function 0 to be D0 state if any other function is in D0 state. Signed-off-by: Abhishek Sahu --- * Changes from v1: 1. Minor changes in commit log 2. used pci_create_device_link() helper function drivers/pci/quirks.c | 26 ++ 1 file changed, 26 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 379cd7fbcb12..b9182c4e5e42 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4966,6 +4966,32 @@ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_AMD, PCI_ANY_ID, DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda); +/* + * Create device link for NVIDIA GPU with integrated USB xHCI Host + * controller to VGA. + */ +static void quirk_gpu_usb(struct pci_dev *usb) +{ + pci_create_device_link(usb, 2, 0, PCI_BASE_CLASS_DISPLAY, 16); +} +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, + PCI_CLASS_SERIAL_USB, 8, quirk_gpu_usb); + +/* + * Create device link for NVIDIA GPU with integrated Type-C UCSI controller + * to VGA. Currently there is no class code defined for UCSI device over PCI + * so using UNKNOWN class for now and it will be updated when UCSI + * over PCI gets a class code. + */ +#define PCI_CLASS_SERIAL_UNKNOWN 0x0c80 +static void quirk_gpu_usb_typec_ucsi(struct pci_dev *ucsi) +{ + pci_create_device_link(ucsi, 3, 0, PCI_BASE_CLASS_DISPLAY, 16); +} +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, + PCI_CLASS_SERIAL_UNKNOWN, 8, + quirk_gpu_usb_typec_ucsi); + /* * Some IDT switches incorrectly flag an ACS Source Validation error on * completions for config read requests even though PCIe r4.0, sec -- 2.17.1
[PATCH v2 1/2] PCI: Code reorganization for creating device link
In Multi-function PCI device, one function (consumer) can have hardware functional dependencies on another function (supplier). Whenever the consumer is active and in D0 state, the supplier should also be in D0 state. Currently, the device link is being created from HDA function to VGA function for GPU's. This patch does minor code reorganization. It introduces a helper function which creates device link from consumer pci device to supplier pci device and uses this helper function for creating device link from HDA to VGA. This helper function can be used in future for creating device link from one function to another function. Signed-off-by: Abhishek Sahu --- * Changes from v1: 1. Make the helper function generic which takes supplier class, class shift and function number also. 2. Minor changes in commit log drivers/pci/quirks.c | 53 +++- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index a077f67fe1da..379cd7fbcb12 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4916,35 +4916,48 @@ static void quirk_fsl_no_msi(struct pci_dev *pdev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, quirk_fsl_no_msi); /* - * GPUs with integrated HDA controller for streaming audio to attached displays - * need a device link from the HDA controller (consumer) to the GPU (supplier) - * so that the GPU is powered up whenever the HDA controller is accessed. - * The GPU and HDA controller are functions 0 and 1 of the same PCI device. - * The device link stays in place until shutdown (or removal of the PCI device - * if it's hotplugged). Runtime PM is allowed by default on the HDA controller - * to prevent it from permanently keeping the GPU awake. + * Multi-function PCI devices can have hardware functional dependencies from + * one function (consumer) to another function (supplier). Whenever the + * consumer is in D0 state, the supplier should also be in D0 state. This is + * a helper function which creates device link from the consumer to the + * supplier. The device link stays in place until shutdown (or removal of + * the PCI device if it's hotplugged). Runtime PM is allowed by default on + * consumers to prevent it from permanently keeping the supplier awake. */ -static void quirk_gpu_hda(struct pci_dev *hda) +static void pci_create_device_link(struct pci_dev *pdev, unsigned int consumer, + unsigned int supplier, unsigned int class, + unsigned int class_shift) { - struct pci_dev *gpu; + struct pci_dev *supplier_pdev; - if (PCI_FUNC(hda->devfn) != 1) + if (PCI_FUNC(pdev->devfn) != consumer) return; - gpu = pci_get_domain_bus_and_slot(pci_domain_nr(hda->bus), - hda->bus->number, - PCI_DEVFN(PCI_SLOT(hda->devfn), 0)); - if (!gpu || (gpu->class >> 16) != PCI_BASE_CLASS_DISPLAY) { - pci_dev_put(gpu); + supplier_pdev = pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus), + pdev->bus->number, + PCI_DEVFN(PCI_SLOT(pdev->devfn), supplier)); + if (!supplier_pdev || (supplier_pdev->class >> class_shift) != class) { + pci_dev_put(supplier_pdev); return; } - if (!device_link_add(>dev, >dev, -DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) - pci_err(hda, "cannot link HDA to GPU %s\n", pci_name(gpu)); + if (device_link_add(>dev, _pdev->dev, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) + pci_info(pdev, "Linked with %s\n", pci_name(supplier_pdev)); + else + pci_err(pdev, "Cannot link with %s\n", pci_name(supplier_pdev)); + + pm_runtime_allow(>dev); + pci_dev_put(supplier_pdev); +} - pm_runtime_allow(>dev); - pci_dev_put(gpu); +/* + * Create device link for GPUs with integrated HDA controller for streaming + * audio to attached displays. + */ +static void quirk_gpu_hda(struct pci_dev *hda) +{ + pci_create_device_link(hda, 1, 0, PCI_BASE_CLASS_DISPLAY, 16); } DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_ATI, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda); -- 2.17.1
[PATCH v2 0/2] PCI: device link quirk for NVIDIA GPU
* v2: 1. Make the pci device link helper function generic which can be used for other multi-function PCI devices also. 2. Minor changes in comments and commit logs. * v1: NVIDIA Turing GPU [1] has hardware support for USB Type-C and VirtualLink [2]. The Turing GPU is a multi-function PCI device which has the following four functions: - VGA display controller (Function 0) - Audio controller (Function 1) - USB xHCI Host controller (Function 2) - USB Type-C USCI controller (Function 3) Currently NVIDIA and Nouveau GPU drivers only manage function 0. Rest of the functions are managed by other drivers. These functions internally in the hardware are tightly coupled. When function 0 goes in runtime suspended state, then it will do power gating for most of the hardware blocks. Some of these hardware blocks are used by the other PCI functions, which leads to functional failure. In the mainline kernel, the device link is present between function 0 and function 1. This patch series deals with creating a similar kind of device link between function 0 and functions 2 and 3. [1] https://www.nvidia.com/content/dam/en-zz/Solutions/design-visualization/technologies/turing-architecture/NVIDIA-Turing-Architecture-Whitepaper.pdf [2] https://en.wikipedia.org/wiki/VirtualLink Abhishek Sahu (2): PCI: Code reorganization for creating device link PCI: Create device link for NVIDIA GPU drivers/pci/quirks.c | 79 +--- 1 file changed, 59 insertions(+), 20 deletions(-) -- 2.17.1
Re: [PATCH 2/2] PCI: Create device link for NVIDIA GPU
On 6/3/2019 10:52 PM, Bjorn Helgaas wrote: > [+cc Rafael, just FYI] > > On Mon, Jun 03, 2019 at 01:30:51PM +0530, Abhishek Sahu wrote: >> On 6/1/2019 2:09 AM, Bjorn Helgaas wrote: >>> On Fri, May 31, 2019 at 10:31:09AM +0530, Abhishek Sahu wrote: >>>> NVIDIA Turing GPUs include hardware support for USB Type-C and >>>> VirtualLink. It helps in delivering the power, display, and data >>>> required to power VR headsets through a single USB Type-C connector. >>>> The Turing GPU is a multi-function PCI device has the following >>>> four functions: >>>> >>>>- VGA display controller (Function 0) >>>>- Audio controller (Function 1) >>>>- USB xHCI Host controller (Function 2) >>>>- USB Type-C USCI controller (Function 3) >>>> >>>> The function 0 is tightly coupled with other functions in the >>>> hardware. When function 0 goes in runtime suspended state, >>>> then it will do power gating for most of the hardware blocks. >>>> Some of these hardware blocks are used by other functions which >>>> leads to functional failure. So if any of these functions (1/2/3) >>>> are active, then function 0 should also be in active state. >>> >>>> 'commit 07f4f97d7b4b ("vga_switcheroo: Use device link for >>>> HDA controller")' creates the device link from function 1 to >>>> function 0. A similar kind of device link needs to be created >>>> between function 0 and functions 2 and 3 for NVIDIA Turing GPU. >>> >>> I can't point to language that addresses this, but this sounds like a >>> case of the GPU not conforming to the PCI spec. The general >>> assumption is that the OS should be able to discover everything it >>> needs to do power management directly from the architected PCI config >>> space. >> >> The GPU is following PCIe spec but following is the implementation >> from HW side > > Unless you can find spec language that talks about D-state > dependencies between functions, I claim this is not following the > PCIe spec. For example, PCIe r5.0, sec 1.4, says "the PCI/PCIe > hardware/software model includes architectural constructs necessary to > discover, configure, and use a Function, without needing Function- > specific knowledge." Sec 5.1 says "D states are associated with a > particular Function" and "PM provides ... a mechanism to identify > power management capabilities of a given Function [and] the ability to > transition a Function into a certain power management state." > Thanks Bjorn. Here in case of GPU's these functions are not completely independent so it is not following PCIe spec in that aspect. > If there *is* something about dependencies between functions in the > spec, we should improve the generic PCI core to pay attention to that, > and then we wouldn't need this quirk. > > If the spec doesn't provide a way to discover them, these dependencies > are exceptions from the spec, and we have to handle them as hardware > defects, using quirks like this. That's fine, but let's not pretend > that this is a conforming device and that adding quirks is the > expected process. Just call a spade a spade and say we're working > around a defect in this particular device. > Yes. I am agree with that we need to be very careful in adding quirks like this. I will communicate the same to HW team so they can explore other options to handle this in HW design side for future chips. > I think the best path forward would be to add this quirk for the > existing device, and then pursue a spec change to add something like > a new PCIe capability to describe the dependencies. Then we could > enhance the PCI core once and power management for future devices > would "Just Work" without having to add quirks. > Yes. It will be long term process. If other HW has similar requirement then it would be good to have this. Regards, Abhishek
Re: [PATCH 1/2] PCI: Code reorganization for VGA device link
On 6/3/2019 10:45 PM, Bjorn Helgaas wrote: > [+cc Lukas] > > On Fri, May 31, 2019 at 10:31:08AM +0530, Abhishek Sahu wrote: >> This patch does minor code reorganization. It introduces a helper >> function which creates device link from the non-VGA controller >> (consumer) to the VGA (supplier) and uses this helper function for >> creating device link from integrated HDA controller to VGA. It will >> help in subsequent patches which require a similar kind of device >> link from USB/Type-C USCI controller to VGA. >> >> Signed-off-by: Abhishek Sahu >> --- >> drivers/pci/quirks.c | 44 +--- >> 1 file changed, 29 insertions(+), 15 deletions(-) >> >> diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c >> index a077f67fe1da..a20f7771a323 100644 >> --- a/drivers/pci/quirks.c >> +++ b/drivers/pci/quirks.c >> @@ -4916,36 +4916,50 @@ static void quirk_fsl_no_msi(struct pci_dev *pdev) >> DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, >> quirk_fsl_no_msi); >> >> /* >> - * GPUs with integrated HDA controller for streaming audio to attached >> displays >> - * need a device link from the HDA controller (consumer) to the GPU >> (supplier) >> - * so that the GPU is powered up whenever the HDA controller is accessed. >> - * The GPU and HDA controller are functions 0 and 1 of the same PCI device. >> - * The device link stays in place until shutdown (or removal of the PCI >> device >> - * if it's hotplugged). Runtime PM is allowed by default on the HDA >> controller >> - * to prevent it from permanently keeping the GPU awake. >> + * GPUs can be multi-function PCI device which can contain controllers other >> + * than VGA (like Audio, USB, etc.). Internally in the hardware, these >> non-VGA >> + * controllers are tightly coupled with VGA controller. Whenever these >> + * controllers are runtime active, the VGA controller should also be in >> active >> + * state. Normally, in these GPUs, the VGA controller is present at >> function 0. >> + * >> + * This is a helper function which creates device link from the non-VGA >> + * controller (consumer) to the VGA (supplier). The device link stays in >> place >> + * until shutdown (or removal of the PCI device if it's hotplugged). >> + * Runtime PM is allowed by default on these non-VGA controllers to prevent >> + * it from permanently keeping the GPU awake. >> */ >> -static void quirk_gpu_hda(struct pci_dev *hda) >> +static void >> +pci_create_device_link_with_vga(struct pci_dev *pdev, unsigned int devfn) > > There's nothing in this functionality that depends on VGA, so let's > remove "GPU, "VGA", etc from the description, the function name, the > local variable name, and the log message. Maybe you need to allow the Thanks. Then we can make this function generic where we can pass device link supplier and supplier pci class mask. It will help in creating device link from one function (other than 0 also) to any another function. Later on, same can be used by non GPUs devices also, if required. > caller to supply the class type (PCI_BASE_CLASS_DISPLAY for current > users, but Lukas mentioned a NIC that might be able to use this too). > > Follow the prevailing indentation style, with return type and function > name on the same line, i.e., > I will fix in v2. > static void pci_create_device_link(...) > >> { >> struct pci_dev *gpu; >> >> -if (PCI_FUNC(hda->devfn) != 1) >> +if (PCI_FUNC(pdev->devfn) != devfn) >> return; >> >> -gpu = pci_get_domain_bus_and_slot(pci_domain_nr(hda->bus), >> - hda->bus->number, >> - PCI_DEVFN(PCI_SLOT(hda->devfn), 0)); >> +gpu = pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus), >> + pdev->bus->number, >> + PCI_DEVFN(PCI_SLOT(pdev->devfn), 0)); >> if (!gpu || (gpu->class >> 16) != PCI_BASE_CLASS_DISPLAY) { >> pci_dev_put(gpu); >> return; >> } >> >> -if (!device_link_add(>dev, >dev, >> +if (!device_link_add(>dev, >dev, >> DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) >> -pci_err(hda, "cannot link HDA to GPU %s\n", pci_name(gpu)); >> +pci_err(pdev, "cannot link with VGA %s\n", pci_name(gpu)); > > I think we
Re: [PATCH 2/2] PCI: Create device link for NVIDIA GPU
Thanks Bjorn for your review. On 6/1/2019 2:09 AM, Bjorn Helgaas wrote: > [+cc Lukas, author of 07f4f97d7b4b ("vga_switcheroo: Use device link > for HDA controller")] > > On Fri, May 31, 2019 at 10:31:09AM +0530, Abhishek Sahu wrote: >> NVIDIA Turing GPUs include hardware support for USB Type-C and >> VirtualLink. It helps in delivering the power, display, and data >> required to power VR headsets through a single USB Type-C connector. >> The Turing GPU is a multi-function PCI device has the following >> four functions: >> >> - VGA display controller (Function 0) >> - Audio controller (Function 1) >> - USB xHCI Host controller (Function 2) >> - USB Type-C USCI controller (Function 3) >> >> The function 0 is tightly coupled with other functions in the >> hardware. When function 0 goes in runtime suspended state, > > "Runtime suspended" is a Linux concept, not a PCI concept. Please > replace this with the appropriate PCI term, e.g., "D3hot" or whatever > it is. Sure. I will change this. > >> then it will do power gating for most of the hardware blocks. >> Some of these hardware blocks are used by other functions which >> leads to functional failure. So if any of these functions (1/2/3) >> are active, then function 0 should also be in active state. > > Instead of "active" and "active state", please use the specific states > required in terms of PCI. Sure. I will use specific states name. > >> 'commit 07f4f97d7b4b ("vga_switcheroo: Use device link for >> HDA controller")' creates the device link from function 1 to >> function 0. A similar kind of device link needs to be created >> between function 0 and functions 2 and 3 for NVIDIA Turing GPU. > > I can't point to language that addresses this, but this sounds like a > case of the GPU not conforming to the PCI spec. The general > assumption is that the OS should be able to discover everything it > needs to do power management directly from the architected PCI config > space. The GPU is following PCIe spec but following is the implementation from HW side Normal GPU has VGA and Audio controller in which Audio is dependent upon VGA. For these GPU, the VGA is managed by GPU driver and Audio is managed by sound driver. Now the VGA driver can go to D3hot while Audio still in D0 if there was no device link (added in commit 07f4f97d7b4b). It would lead to Audio functionality failure. The device link is making sure that GPU is in D0 while Audio is D0. Now the NVIDIA Turing GPU has one USB Type-C port mainly to support virtual reality headset. With default mode, this USB port will act as normal USB port and any USB device can be connected over it. It will be managed with PCI xHCI USB controller driver. Now, to support VR headset, This USB Type-C alternate mode is going to be used. This alternate mode setting will be managed with [1] and [2] which is part of 5.2-rc1. More detail for this is available in [3] and virtual link open industry standard [4]. These VR frame-buffers are internally going to be rendered by GPU only and managed by function 0 (VGA) driver. Now, from HW side, we need to make sure VGA is in D0 while any of other functions are in D0. [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/usb/typec/altmodes/nvidia.c [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/i2c/busses/i2c-nvidia-gpu.c [3] https://fullcirclemagazine.org/2019/04/30/nvidia-creates-free-virtual-link-driver-for-linux/ [4] https://sites.google.com/view/virtuallink-consortium/home > > It is definitely not ideal to have to add quirks like this for devices > designed this way. Such quirks force us to do otherwise unnecessary > OS updates as new devices are released. I can understand but this is the HW requirement. To support, VR headset in GPU, the HW has provided USB Type-C. Now, from SW side, we are working on supporting this with runtime PM. Currently runtime PM is not possible without adding the dependencies between different functions. > > If all the devices in a multi-function device were connected > intimately enough that they all had to be managed by the same driver, > I could imagine putting these non-discoverable dependencies in the > driver. But these devices don't seem to be related in that way. > > If there *is* spec language that allows dependencies like this, please > include the citation in your commit log. > The PCIe specification treats each function separately but GPU case is different. So, it won't be part of PCIe spec. in GPU, the different kind of devices are internally coupled in HW but still needs to be managed by different driver. >&
[PATCH 1/2] PCI: Code reorganization for VGA device link
This patch does minor code reorganization. It introduces a helper function which creates device link from the non-VGA controller (consumer) to the VGA (supplier) and uses this helper function for creating device link from integrated HDA controller to VGA. It will help in subsequent patches which require a similar kind of device link from USB/Type-C USCI controller to VGA. Signed-off-by: Abhishek Sahu --- drivers/pci/quirks.c | 44 +--- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index a077f67fe1da..a20f7771a323 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4916,36 +4916,50 @@ static void quirk_fsl_no_msi(struct pci_dev *pdev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, quirk_fsl_no_msi); /* - * GPUs with integrated HDA controller for streaming audio to attached displays - * need a device link from the HDA controller (consumer) to the GPU (supplier) - * so that the GPU is powered up whenever the HDA controller is accessed. - * The GPU and HDA controller are functions 0 and 1 of the same PCI device. - * The device link stays in place until shutdown (or removal of the PCI device - * if it's hotplugged). Runtime PM is allowed by default on the HDA controller - * to prevent it from permanently keeping the GPU awake. + * GPUs can be multi-function PCI device which can contain controllers other + * than VGA (like Audio, USB, etc.). Internally in the hardware, these non-VGA + * controllers are tightly coupled with VGA controller. Whenever these + * controllers are runtime active, the VGA controller should also be in active + * state. Normally, in these GPUs, the VGA controller is present at function 0. + * + * This is a helper function which creates device link from the non-VGA + * controller (consumer) to the VGA (supplier). The device link stays in place + * until shutdown (or removal of the PCI device if it's hotplugged). + * Runtime PM is allowed by default on these non-VGA controllers to prevent + * it from permanently keeping the GPU awake. */ -static void quirk_gpu_hda(struct pci_dev *hda) +static void +pci_create_device_link_with_vga(struct pci_dev *pdev, unsigned int devfn) { struct pci_dev *gpu; - if (PCI_FUNC(hda->devfn) != 1) + if (PCI_FUNC(pdev->devfn) != devfn) return; - gpu = pci_get_domain_bus_and_slot(pci_domain_nr(hda->bus), - hda->bus->number, - PCI_DEVFN(PCI_SLOT(hda->devfn), 0)); + gpu = pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus), + pdev->bus->number, + PCI_DEVFN(PCI_SLOT(pdev->devfn), 0)); if (!gpu || (gpu->class >> 16) != PCI_BASE_CLASS_DISPLAY) { pci_dev_put(gpu); return; } - if (!device_link_add(>dev, >dev, + if (!device_link_add(>dev, >dev, DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) - pci_err(hda, "cannot link HDA to GPU %s\n", pci_name(gpu)); + pci_err(pdev, "cannot link with VGA %s\n", pci_name(gpu)); - pm_runtime_allow(>dev); + pm_runtime_allow(>dev); pci_dev_put(gpu); } + +/* + * Create device link for GPUs with integrated HDA controller for streaming + * audio to attached displays. + */ +static void quirk_gpu_hda(struct pci_dev *hda) +{ + pci_create_device_link_with_vga(hda, 1); +} DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_ATI, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda); DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_AMD, PCI_ANY_ID, -- 2.17.1
[PATCH 2/2] PCI: Create device link for NVIDIA GPU
NVIDIA Turing GPUs include hardware support for USB Type-C and VirtualLink. It helps in delivering the power, display, and data required to power VR headsets through a single USB Type-C connector. The Turing GPU is a multi-function PCI device has the following four functions: - VGA display controller (Function 0) - Audio controller (Function 1) - USB xHCI Host controller (Function 2) - USB Type-C USCI controller (Function 3) The function 0 is tightly coupled with other functions in the hardware. When function 0 goes in runtime suspended state, then it will do power gating for most of the hardware blocks. Some of these hardware blocks are used by other functions which leads to functional failure. So if any of these functions (1/2/3) are active, then function 0 should also be in active state. 'commit 07f4f97d7b4b ("vga_switcheroo: Use device link for HDA controller")' creates the device link from function 1 to function 0. A similar kind of device link needs to be created between function 0 and functions 2 and 3 for NVIDIA Turing GPU. This patch does the same and create the required device links. It will make function 0 to be runtime PM active if other functions are still active. Signed-off-by: Abhishek Sahu --- drivers/pci/quirks.c | 23 +++ 1 file changed, 23 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index a20f7771a323..afdbc199efc5 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4967,6 +4967,29 @@ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_AMD, PCI_ANY_ID, DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda); +/* Create device link for NVIDIA GPU with integrated USB controller to VGA. */ +static void quirk_gpu_usb(struct pci_dev *usb) +{ + pci_create_device_link_with_vga(usb, 2); +} +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, + PCI_CLASS_SERIAL_USB, 8, quirk_gpu_usb); + +/* + * Create device link for NVIDIA GPU with integrated Type-C UCSI controller + * to VGA. Currently there is no class code defined for UCSI device over PCI + * so using UNKNOWN class for now and it will be updated when UCSI + * over PCI gets a class code. + */ +#define PCI_CLASS_SERIAL_UNKNOWN 0x0c80 +static void quirk_gpu_usb_typec_ucsi(struct pci_dev *ucsi) +{ + pci_create_device_link_with_vga(ucsi, 3); +} +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, + PCI_CLASS_SERIAL_UNKNOWN, 8, + quirk_gpu_usb_typec_ucsi); + /* * Some IDT switches incorrectly flag an ACS Source Validation error on * completions for config read requests even though PCIe r4.0, sec -- 2.17.1
[PATCH 0/2] PCI: device link quirk for NVIDIA GPU
NVIDIA Turing GPU [1] has hardware support for USB Type-C and VirtualLink [2]. The Turing GPU is a multi-function PCI device which has the following four functions: - VGA display controller (Function 0) - Audio controller (Function 1) - USB xHCI Host controller (Function 2) - USB Type-C USCI controller (Function 3) Currently NVIDIA and Nouveau GPU drivers only manage function 0. Rest of the functions are managed by other drivers. These functions internally in the hardware are tightly coupled. When function 0 goes in runtime suspended state, then it will do power gating for most of the hardware blocks. Some of these hardware blocks are used by the other PCI functions, which leads to functional failure. In the mainline kernel, the device link is present between function 0 and function 1. This patch series deals with creating a similar kind of device link between function 0 and functions 2 and 3. [1] https://www.nvidia.com/content/dam/en-zz/Solutions/design-visualization/technologies/turing-architecture/NVIDIA-Turing-Architecture-Whitepaper.pdf [2] https://en.wikipedia.org/wiki/VirtualLink Abhishek Sahu (2): PCI: Code reorganization for VGA device link PCI: Create device link for NVIDIA GPU drivers/pci/quirks.c | 67 ++-- 1 file changed, 52 insertions(+), 15 deletions(-) -- 2.17.1
Re: [PATCH 2/5] mtd: rawnand: qcom: remove driver specific block_markbad function
On 2018-11-04 21:26, Boris Brezillon wrote: Hi Abhishek, On Fri, 20 Jul 2018 15:03:48 +0200 Boris Brezillon wrote: On Fri, 20 Jul 2018 17:46:38 +0530 Abhishek Sahu wrote: > Hi Boris, > > On 2018-07-19 03:13, Boris Brezillon wrote: > > On Wed, 18 Jul 2018 23:23:50 +0200 > > Miquel Raynal wrote: > > > >> Boris, > >> > >> Can you please check the change in qcom_nandc_write_oob() is > >> valid? I think it is but as this is a bit of a hack I prefer double > >> checking. > > > > Indeed, it's hack-ish. > > > >> > >> Thanks, > >> Miquèl > >> > >> > >> Abhishek Sahu wrote on Fri, 6 Jul 2018 > >> 13:21:56 +0530: > >> > >> > The NAND base layer calls write_oob() by setting bytes at > >> > chip->badblockpos with value non 0xFF for updating bad block status. > >> > The QCOM NAND controller skips the bad block bytes while doing normal > >> > write with ECC enabled. When initial support for this driver was > >> > added, the driver specific function was added temporarily for > >> > block_markbad() with assumption to change for raw read in NAND base > >> > layer. Moving to raw read for block_markbad() seems to take more time > >> > so this patch removes driver specific block_markbad() function by > >> > using following HACK in write_oob() function. > >> > > >> > Check for BBM bytes in OOB and accordingly do raw write for updating > >> > BBM bytes in NAND flash or normal write for updating available OOB > >> > bytes. > > > > Why don't we change that instead of patching the qcom driver to guess > > when the core tries to mark a block bad? If you're afraid of breaking > > existing drivers that might rely on the "write/read BBM in non-raw > > mode" solution (I'm sure some drivers are), you can always add a new > > flag in chip->options (NAND_ACCESS_BBM_IN_RAW_MODE) and only use raw > > accessors when this flag is set. > > > > We started with that Only > > http://patchwork.ozlabs.org/patch/508565/ > > and since we didn't conclude, we went for driver > specific bad block check and mark bad block functions. > > Now, we wanted to get rid of driver specific functions > > 1. For bad block check, we found the way to get the BBM bytes > with ECC read. Controller updates BBM in separate register > which we can read and update the same in OOB. Patch #1 of > series does the same. > > 2. For bad block mark, there is no way to update in ECC mode > that's why we went for HACK to get rid of driver specific > function. > > If adding flag is fine now then this HACK won't be required. Yep. I'm fine with that. Can you rebase the patch you pointed out on top of nand/next and move the flag to chip->options instead of chip->bbt_options + prefix it with NAND_ instead of NAND_BBT_? I'm currently trying to get rid of chip->block_bad() (now placed in chip->legacy.block_bad()), and I wanted to know if you were still planning to submit the changes we discussed in this thread. If you don't have time, please let me know and I'll try to do it. Sorry Boris, I couldn't work on these patches. Currently, I am working on non open source projects so I can't submit any patches in open source till this project completion due to legal guidelines. If this is urgent then you can try. I will help in QCOM related stuffs and testing. Thanks, Abhishek
Re: [PATCH 2/5] mtd: rawnand: qcom: remove driver specific block_markbad function
On 2018-11-04 21:26, Boris Brezillon wrote: Hi Abhishek, On Fri, 20 Jul 2018 15:03:48 +0200 Boris Brezillon wrote: On Fri, 20 Jul 2018 17:46:38 +0530 Abhishek Sahu wrote: > Hi Boris, > > On 2018-07-19 03:13, Boris Brezillon wrote: > > On Wed, 18 Jul 2018 23:23:50 +0200 > > Miquel Raynal wrote: > > > >> Boris, > >> > >> Can you please check the change in qcom_nandc_write_oob() is > >> valid? I think it is but as this is a bit of a hack I prefer double > >> checking. > > > > Indeed, it's hack-ish. > > > >> > >> Thanks, > >> Miquèl > >> > >> > >> Abhishek Sahu wrote on Fri, 6 Jul 2018 > >> 13:21:56 +0530: > >> > >> > The NAND base layer calls write_oob() by setting bytes at > >> > chip->badblockpos with value non 0xFF for updating bad block status. > >> > The QCOM NAND controller skips the bad block bytes while doing normal > >> > write with ECC enabled. When initial support for this driver was > >> > added, the driver specific function was added temporarily for > >> > block_markbad() with assumption to change for raw read in NAND base > >> > layer. Moving to raw read for block_markbad() seems to take more time > >> > so this patch removes driver specific block_markbad() function by > >> > using following HACK in write_oob() function. > >> > > >> > Check for BBM bytes in OOB and accordingly do raw write for updating > >> > BBM bytes in NAND flash or normal write for updating available OOB > >> > bytes. > > > > Why don't we change that instead of patching the qcom driver to guess > > when the core tries to mark a block bad? If you're afraid of breaking > > existing drivers that might rely on the "write/read BBM in non-raw > > mode" solution (I'm sure some drivers are), you can always add a new > > flag in chip->options (NAND_ACCESS_BBM_IN_RAW_MODE) and only use raw > > accessors when this flag is set. > > > > We started with that Only > > http://patchwork.ozlabs.org/patch/508565/ > > and since we didn't conclude, we went for driver > specific bad block check and mark bad block functions. > > Now, we wanted to get rid of driver specific functions > > 1. For bad block check, we found the way to get the BBM bytes > with ECC read. Controller updates BBM in separate register > which we can read and update the same in OOB. Patch #1 of > series does the same. > > 2. For bad block mark, there is no way to update in ECC mode > that's why we went for HACK to get rid of driver specific > function. > > If adding flag is fine now then this HACK won't be required. Yep. I'm fine with that. Can you rebase the patch you pointed out on top of nand/next and move the flag to chip->options instead of chip->bbt_options + prefix it with NAND_ instead of NAND_BBT_? I'm currently trying to get rid of chip->block_bad() (now placed in chip->legacy.block_bad()), and I wanted to know if you were still planning to submit the changes we discussed in this thread. If you don't have time, please let me know and I'll try to do it. Sorry Boris, I couldn't work on these patches. Currently, I am working on non open source projects so I can't submit any patches in open source till this project completion due to legal guidelines. If this is urgent then you can try. I will help in QCOM related stuffs and testing. Thanks, Abhishek
Re: [PATCH 5/5] mtd: rawnand: qcom: reorganization by removing read/write helpers
On 2018-07-19 03:24, Boris Brezillon wrote: On Fri, 6 Jul 2018 13:21:59 +0530 Abhishek Sahu wrote: Driver does not send the commands to NAND device for page read/write operations in ->cmdfunc(). It just does some minor variable initialization and rest of the things are being done in actual ->read/write_oob[_raw]. The generic helper function calls for invoking commands during page read/write are making this driver complicated. For QCOM NAND driver, ->cmdfunc() does minor part of initialization and rest of the initialization is performed by actual page read/write functions. Also, ->read/write_oob() does not calls helper function and all the initialization is being done in ->read/write_oob() itself. This sounds hazardous in the long run. Some vendor specific commands are re-using the READ0/READSTART semantic to read particular registers/OTP sections programmed at flash production time. For these operations, we don't want to go through the regular chip->ecc.read_page[_raw]() hooks, and instead use ->cmdfunc()/->exec_op(). You probably don't have setups with such NANDs yet, but that might be the case at some point. Thanks Boris and Miquel for pointing this out. Yes. We don't have that setup yet and the current driver code also can't handle that condition since the command function was not doing anything in this regard. Since the current driver code was complicated with lot of code duplication, so this patch was raised to cleanup few things. As already suggested by Miqule, I strongly recommend that you work on supporting ->exec_op() instead of trying to clean things up prematurely. I did some analysis and it seems moving to ->exec_op() will help in long run. I will start working on that but it will take some time for us since It requires major code changes with lot of testing. I will update you when we have RFC patch ready for this. Meanwhile, I will address review comments in other patches of this series and will post v2 after removing this patch from patch series. Regards, Abhishek Since after 'commit 25f815f66a14 ("mtd: nand: force drivers to explicitly send READ/PROG commands")', sending of commands has been moved to driver for page read/write, so this patch does following changes to make code more readable: 1. Introduce qcom_nand_init_page_op() and qcom_nand_start_page_op() helper functions which helps in removing code duplication in each operation. 2. After issuing PROGRAM PAGE/BLOCK ERASE, QCOM NAND controller waits for BUSY signal to be de asserted and automatically issues the READ STATUS command. Currently, driver is storing this status privately and returns the same when status command comes from helper function after program/erase operation. Now, for write operations, the status can be returned from main function itself, so storing status can be removed for program operations. Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/raw/qcom_nandc.c | 222 -- 1 file changed, 91 insertions(+), 131 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 6fb85d3..f73ee0e 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1382,39 +1382,37 @@ static void pre_command(struct qcom_nand_host *host, int command) host->last_command = command; clear_read_regs(nandc); - - if (command == NAND_CMD_RESET || command == NAND_CMD_READID || - command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) - clear_bam_transaction(nandc); + clear_bam_transaction(nandc); } /* - * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our - * privately maintained status byte, this status byte can be read after - * NAND_CMD_STATUS is called + * QCOM NAND controller by default issues READ STATUS command after PROGRAM + * PAGE/BLOCK ERASE operation and updates the same in its internal status + * register for last codeword. This function parses status for each CW and + * return actual status byte for write/erase operation. */ -static void parse_erase_write_errors(struct qcom_nand_host *host, int command) +static u8 parse_erase_write_errors(struct qcom_nand_host *host, int num_cw) { struct nand_chip *chip = >chip; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = >ecc; - int num_cw; int i; + u8 status = 0; - num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1; nandc_read_buffer_sync(nandc, true); for (i = 0; i < num_cw; i++) { u32 flash_status = le32_to_cpu(nandc->reg_read_buf[i]); if (flash_status & FS_MPU_ERR) - host->status &= ~NAND_STATUS_WP; + status &= ~NAND_STAT
Re: [PATCH 5/5] mtd: rawnand: qcom: reorganization by removing read/write helpers
On 2018-07-19 03:24, Boris Brezillon wrote: On Fri, 6 Jul 2018 13:21:59 +0530 Abhishek Sahu wrote: Driver does not send the commands to NAND device for page read/write operations in ->cmdfunc(). It just does some minor variable initialization and rest of the things are being done in actual ->read/write_oob[_raw]. The generic helper function calls for invoking commands during page read/write are making this driver complicated. For QCOM NAND driver, ->cmdfunc() does minor part of initialization and rest of the initialization is performed by actual page read/write functions. Also, ->read/write_oob() does not calls helper function and all the initialization is being done in ->read/write_oob() itself. This sounds hazardous in the long run. Some vendor specific commands are re-using the READ0/READSTART semantic to read particular registers/OTP sections programmed at flash production time. For these operations, we don't want to go through the regular chip->ecc.read_page[_raw]() hooks, and instead use ->cmdfunc()/->exec_op(). You probably don't have setups with such NANDs yet, but that might be the case at some point. Thanks Boris and Miquel for pointing this out. Yes. We don't have that setup yet and the current driver code also can't handle that condition since the command function was not doing anything in this regard. Since the current driver code was complicated with lot of code duplication, so this patch was raised to cleanup few things. As already suggested by Miqule, I strongly recommend that you work on supporting ->exec_op() instead of trying to clean things up prematurely. I did some analysis and it seems moving to ->exec_op() will help in long run. I will start working on that but it will take some time for us since It requires major code changes with lot of testing. I will update you when we have RFC patch ready for this. Meanwhile, I will address review comments in other patches of this series and will post v2 after removing this patch from patch series. Regards, Abhishek Since after 'commit 25f815f66a14 ("mtd: nand: force drivers to explicitly send READ/PROG commands")', sending of commands has been moved to driver for page read/write, so this patch does following changes to make code more readable: 1. Introduce qcom_nand_init_page_op() and qcom_nand_start_page_op() helper functions which helps in removing code duplication in each operation. 2. After issuing PROGRAM PAGE/BLOCK ERASE, QCOM NAND controller waits for BUSY signal to be de asserted and automatically issues the READ STATUS command. Currently, driver is storing this status privately and returns the same when status command comes from helper function after program/erase operation. Now, for write operations, the status can be returned from main function itself, so storing status can be removed for program operations. Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/raw/qcom_nandc.c | 222 -- 1 file changed, 91 insertions(+), 131 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 6fb85d3..f73ee0e 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1382,39 +1382,37 @@ static void pre_command(struct qcom_nand_host *host, int command) host->last_command = command; clear_read_regs(nandc); - - if (command == NAND_CMD_RESET || command == NAND_CMD_READID || - command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) - clear_bam_transaction(nandc); + clear_bam_transaction(nandc); } /* - * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our - * privately maintained status byte, this status byte can be read after - * NAND_CMD_STATUS is called + * QCOM NAND controller by default issues READ STATUS command after PROGRAM + * PAGE/BLOCK ERASE operation and updates the same in its internal status + * register for last codeword. This function parses status for each CW and + * return actual status byte for write/erase operation. */ -static void parse_erase_write_errors(struct qcom_nand_host *host, int command) +static u8 parse_erase_write_errors(struct qcom_nand_host *host, int num_cw) { struct nand_chip *chip = >chip; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = >ecc; - int num_cw; int i; + u8 status = 0; - num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1; nandc_read_buffer_sync(nandc, true); for (i = 0; i < num_cw; i++) { u32 flash_status = le32_to_cpu(nandc->reg_read_buf[i]); if (flash_status & FS_MPU_ERR) - host->status &= ~NAND_STATUS_WP; + status &= ~NAND_STAT
Re: [PATCH 2/5] mtd: rawnand: qcom: remove driver specific block_markbad function
Hi Boris, On 2018-07-19 03:13, Boris Brezillon wrote: On Wed, 18 Jul 2018 23:23:50 +0200 Miquel Raynal wrote: Boris, Can you please check the change in qcom_nandc_write_oob() is valid? I think it is but as this is a bit of a hack I prefer double checking. Indeed, it's hack-ish. Thanks, Miquèl Abhishek Sahu wrote on Fri, 6 Jul 2018 13:21:56 +0530: > The NAND base layer calls write_oob() by setting bytes at > chip->badblockpos with value non 0xFF for updating bad block status. > The QCOM NAND controller skips the bad block bytes while doing normal > write with ECC enabled. When initial support for this driver was > added, the driver specific function was added temporarily for > block_markbad() with assumption to change for raw read in NAND base > layer. Moving to raw read for block_markbad() seems to take more time > so this patch removes driver specific block_markbad() function by > using following HACK in write_oob() function. > > Check for BBM bytes in OOB and accordingly do raw write for updating > BBM bytes in NAND flash or normal write for updating available OOB > bytes. Why don't we change that instead of patching the qcom driver to guess when the core tries to mark a block bad? If you're afraid of breaking existing drivers that might rely on the "write/read BBM in non-raw mode" solution (I'm sure some drivers are), you can always add a new flag in chip->options (NAND_ACCESS_BBM_IN_RAW_MODE) and only use raw accessors when this flag is set. We started with that Only http://patchwork.ozlabs.org/patch/508565/ and since we didn't conclude, we went for driver specific bad block check and mark bad block functions. Now, we wanted to get rid of driver specific functions 1. For bad block check, we found the way to get the BBM bytes with ECC read. Controller updates BBM in separate register which we can read and update the same in OOB. Patch #1 of series does the same. 2. For bad block mark, there is no way to update in ECC mode that's why we went for HACK to get rid of driver specific function. If adding flag is fine now then this HACK won't be required. Moving to RAW mode for every one still looks risky. Thanks, Abhishek
Re: [PATCH 2/5] mtd: rawnand: qcom: remove driver specific block_markbad function
Hi Boris, On 2018-07-19 03:13, Boris Brezillon wrote: On Wed, 18 Jul 2018 23:23:50 +0200 Miquel Raynal wrote: Boris, Can you please check the change in qcom_nandc_write_oob() is valid? I think it is but as this is a bit of a hack I prefer double checking. Indeed, it's hack-ish. Thanks, Miquèl Abhishek Sahu wrote on Fri, 6 Jul 2018 13:21:56 +0530: > The NAND base layer calls write_oob() by setting bytes at > chip->badblockpos with value non 0xFF for updating bad block status. > The QCOM NAND controller skips the bad block bytes while doing normal > write with ECC enabled. When initial support for this driver was > added, the driver specific function was added temporarily for > block_markbad() with assumption to change for raw read in NAND base > layer. Moving to raw read for block_markbad() seems to take more time > so this patch removes driver specific block_markbad() function by > using following HACK in write_oob() function. > > Check for BBM bytes in OOB and accordingly do raw write for updating > BBM bytes in NAND flash or normal write for updating available OOB > bytes. Why don't we change that instead of patching the qcom driver to guess when the core tries to mark a block bad? If you're afraid of breaking existing drivers that might rely on the "write/read BBM in non-raw mode" solution (I'm sure some drivers are), you can always add a new flag in chip->options (NAND_ACCESS_BBM_IN_RAW_MODE) and only use raw accessors when this flag is set. We started with that Only http://patchwork.ozlabs.org/patch/508565/ and since we didn't conclude, we went for driver specific bad block check and mark bad block functions. Now, we wanted to get rid of driver specific functions 1. For bad block check, we found the way to get the BBM bytes with ECC read. Controller updates BBM in separate register which we can read and update the same in OOB. Patch #1 of series does the same. 2. For bad block mark, there is no way to update in ECC mode that's why we went for HACK to get rid of driver specific function. If adding flag is fine now then this HACK won't be required. Moving to RAW mode for every one still looks risky. Thanks, Abhishek
Re: [PATCH 4/5] mtd: rawnand: qcom: update BBT related flags
On 2018-07-19 03:12, Miquel Raynal wrote: Abhishek, Miquel Raynal wrote on Wed, 18 Jul 2018 23:41:44 +0200: Hi Boris, Boris Brezillon wrote on Wed, 18 Jul 2018 23:36:37 +0200: > On Wed, 18 Jul 2018 23:15:26 +0200 > Miquel Raynal wrote: > > > Hi Abhishek, > > > > Abhishek Sahu wrote on Fri, 6 Jul 2018 > > 13:21:58 +0530: > > > > > Remove the NAND_SKIP_BBTSCAN to use RAM based BBT. > > > > Unless I am understanding it the wrong way, NAND_SKIP_BBTSCAN will skip > > the scan of the on-chip BBT and will scan every block to construct a > > RAM, based BBT thanks to the BBM. > > > > So flash based BBT is already unused and removing this flag is a > > mistake, right? > > ->scan_bbt() is also taking care of building the in-RAM BBT based on > BBM when no on-flash BBT is provided, so I think it's the right thing > to do. Oh right. Then doing so is harmless. Could you please update the commit log to reflect this aspect? Thanks Miquel and Boris. I will update the commit log. Regards, Abhishek
Re: [PATCH 4/5] mtd: rawnand: qcom: update BBT related flags
On 2018-07-19 03:12, Miquel Raynal wrote: Abhishek, Miquel Raynal wrote on Wed, 18 Jul 2018 23:41:44 +0200: Hi Boris, Boris Brezillon wrote on Wed, 18 Jul 2018 23:36:37 +0200: > On Wed, 18 Jul 2018 23:15:26 +0200 > Miquel Raynal wrote: > > > Hi Abhishek, > > > > Abhishek Sahu wrote on Fri, 6 Jul 2018 > > 13:21:58 +0530: > > > > > Remove the NAND_SKIP_BBTSCAN to use RAM based BBT. > > > > Unless I am understanding it the wrong way, NAND_SKIP_BBTSCAN will skip > > the scan of the on-chip BBT and will scan every block to construct a > > RAM, based BBT thanks to the BBM. > > > > So flash based BBT is already unused and removing this flag is a > > mistake, right? > > ->scan_bbt() is also taking care of building the in-RAM BBT based on > BBM when no on-flash BBT is provided, so I think it's the right thing > to do. Oh right. Then doing so is harmless. Could you please update the commit log to reflect this aspect? Thanks Miquel and Boris. I will update the commit log. Regards, Abhishek
[PATCH] arm64: defconfig: Enable CONFIG_MTD_NAND_QCOM for IPQ8074
QCOM IPQ8074 boards contain NAND flash memory for which this config needs to be enabled. Signed-off-by: Abhishek Sahu --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 6739fc7..339f3dd 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -179,6 +179,7 @@ CONFIG_MTD_M25P80=y CONFIG_MTD_NAND=y CONFIG_MTD_NAND_DENALI_DT=y CONFIG_MTD_NAND_MARVELL=y +CONFIG_MTD_NAND_QCOM=y CONFIG_MTD_SPI_NOR=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_NBD=m -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH] arm64: defconfig: Enable CONFIG_MTD_NAND_QCOM for IPQ8074
QCOM IPQ8074 boards contain NAND flash memory for which this config needs to be enabled. Signed-off-by: Abhishek Sahu --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 6739fc7..339f3dd 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -179,6 +179,7 @@ CONFIG_MTD_M25P80=y CONFIG_MTD_NAND=y CONFIG_MTD_NAND_DENALI_DT=y CONFIG_MTD_NAND_MARVELL=y +CONFIG_MTD_NAND_QCOM=y CONFIG_MTD_SPI_NOR=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_NBD=m -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH] ARM: qcom_defconfig: Enable QCOM NAND related configs
IPQ8064 and IPQ4019 boards contain NAND flash memory for which these configs need to be enabled. Signed-off-by: Abhishek Sahu --- arch/arm/configs/qcom_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig index dd2a089..6aa7046 100644 --- a/arch/arm/configs/qcom_defconfig +++ b/arch/arm/configs/qcom_defconfig @@ -57,6 +57,8 @@ CONFIG_DEVTMPFS_MOUNT=y CONFIG_MTD=y CONFIG_MTD_BLOCK=y CONFIG_MTD_M25P80=y +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_QCOM=y CONFIG_MTD_SPI_NOR=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH] ARM: qcom_defconfig: Enable QCOM NAND related configs
IPQ8064 and IPQ4019 boards contain NAND flash memory for which these configs need to be enabled. Signed-off-by: Abhishek Sahu --- arch/arm/configs/qcom_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig index dd2a089..6aa7046 100644 --- a/arch/arm/configs/qcom_defconfig +++ b/arch/arm/configs/qcom_defconfig @@ -57,6 +57,8 @@ CONFIG_DEVTMPFS_MOUNT=y CONFIG_MTD=y CONFIG_MTD_BLOCK=y CONFIG_MTD_M25P80=y +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_QCOM=y CONFIG_MTD_SPI_NOR=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH 0/5] Update for removing driver specific BBM functions
This patch series deals mainly with removing driver specific bad block functions and some minor fixing/cleanup. 1. When initial support for this driver was added [1], then the plan was to remove these driver specific functions when NAND base layer has support for doing raw read/write for bad block related things. Moving to raw read/write seems to take more time so this patch series implemented alternate logic for reading/writing bad block with normal read/write itself. This alternate logic relies on some assumption which seems to be better than driver specific functions and provide more functionality. Some discussion regarding it has already happened in [2]. 2. Some part of code is duplicated in every read/write function so clean up the code by making helper function. 3. Bootloaders does access protection for some of the NAND registers which generates few write errors if it is enabled. 4. Create RAM based BBT table. Flash based BBT table can’t be used since our bootloader does not have support for the same. **This patch series is rebased over linux-next with PATCH [3] [1]: http://patchwork.ozlabs.org/patch/508565/ [2]: https://patchwork.ozlabs.org/patch/920563/ [3]: https://patchwork.ozlabs.org/patch/938631/ Abhishek Sahu (5): mtd: rawnand: qcom: remove driver specific bad block check function mtd: rawnand: qcom: remove driver specific block_markbad function mtd: rawnand: qcom: fix NAND register write errors mtd: rawnand: qcom: update BBT related flags mtd: rawnand: qcom: reorganization by removing read/write helpers drivers/mtd/nand/raw/qcom_nandc.c | 439 ++ 1 file changed, 162 insertions(+), 277 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH 0/5] Update for removing driver specific BBM functions
This patch series deals mainly with removing driver specific bad block functions and some minor fixing/cleanup. 1. When initial support for this driver was added [1], then the plan was to remove these driver specific functions when NAND base layer has support for doing raw read/write for bad block related things. Moving to raw read/write seems to take more time so this patch series implemented alternate logic for reading/writing bad block with normal read/write itself. This alternate logic relies on some assumption which seems to be better than driver specific functions and provide more functionality. Some discussion regarding it has already happened in [2]. 2. Some part of code is duplicated in every read/write function so clean up the code by making helper function. 3. Bootloaders does access protection for some of the NAND registers which generates few write errors if it is enabled. 4. Create RAM based BBT table. Flash based BBT table can’t be used since our bootloader does not have support for the same. **This patch series is rebased over linux-next with PATCH [3] [1]: http://patchwork.ozlabs.org/patch/508565/ [2]: https://patchwork.ozlabs.org/patch/920563/ [3]: https://patchwork.ozlabs.org/patch/938631/ Abhishek Sahu (5): mtd: rawnand: qcom: remove driver specific bad block check function mtd: rawnand: qcom: remove driver specific block_markbad function mtd: rawnand: qcom: fix NAND register write errors mtd: rawnand: qcom: update BBT related flags mtd: rawnand: qcom: reorganization by removing read/write helpers drivers/mtd/nand/raw/qcom_nandc.c | 439 ++ 1 file changed, 162 insertions(+), 277 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH 3/5] mtd: rawnand: qcom: fix NAND register write errors
Fix the following NAND register write errors which will be generated if access protection is enabled. 1. SFLASHC_BURST_CFG register is not available for supported NAND contollers by this driver, so this can be removed. 2. NAND_CTRL is operational register and register writes to operational registers should always be done through command descriptors if BAM_MODE is already enabled. With full boot chain, bootloader already enables BAM_MODE so read the NAND_CTRL register value and write only if BAM_MODE is not set. Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/raw/qcom_nandc.c | 11 --- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index df12cf3..9e6b383 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2693,15 +2693,20 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc) { u32 nand_ctrl; - /* kill onenand */ - nandc_write(nandc, SFLASHC_BURST_CFG, 0); nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD), NAND_DEV_CMD_VLD_VAL); /* enable ADM or BAM DMA */ if (nandc->props->is_bam) { nand_ctrl = nandc_read(nandc, NAND_CTRL); - nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN); + /* +* Once BAM_MODE_EN bit is set then QPIC_NAND_CTRL register +* should be written with BAM instead of writel. +* Check if BAM_MODE_EN is already set by bootloader and write +* only if this bit is not set. +*/ + if (!(nand_ctrl & BAM_MODE_EN)) + nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN); } else { nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); } -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH 2/5] mtd: rawnand: qcom: remove driver specific block_markbad function
The NAND base layer calls write_oob() by setting bytes at chip->badblockpos with value non 0xFF for updating bad block status. The QCOM NAND controller skips the bad block bytes while doing normal write with ECC enabled. When initial support for this driver was added, the driver specific function was added temporarily for block_markbad() with assumption to change for raw read in NAND base layer. Moving to raw read for block_markbad() seems to take more time so this patch removes driver specific block_markbad() function by using following HACK in write_oob() function. Check for BBM bytes in OOB and accordingly do raw write for updating BBM bytes in NAND flash or normal write for updating available OOB bytes. Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/raw/qcom_nandc.c | 103 +++--- 1 file changed, 40 insertions(+), 63 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index ea253ac..df12cf3 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2138,28 +2138,57 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, struct qcom_nand_host *host = to_qcom_nand_host(chip); struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct nand_ecc_ctrl *ecc = >ecc; - u8 *oob = chip->oob_poi; - int data_size, oob_size; + u8 *oob = chip->oob_poi, bbm_byte; + int data_size, oob_size, bbm_offset, write_size; int ret; - host->use_ecc = true; clear_bam_transaction(nandc); - /* calculate the data and oob size for the last codeword/step */ - data_size = ecc->size - ((ecc->steps - 1) << 2); - oob_size = mtd->oobavail; + /* +* The NAND base layer calls ecc->write_oob() by setting bytes at +* chip->badblockpos (chip->badblockpos will be 0 for QCOM NAND +* controller layout) in OOB buffer with value other that 0xFF +* for updating bad block status. QCOM NAND controller skips +* BBM bytes while writing with ECC, so following HACK has been +* added in this function for using generic block_markbad() function. +* +* Check for BBM bytes in OOB and accordingly do raw write for +* updating BBM bytes in NAND flash or normal write with ECC for +* updating available OOB bytes. +*/ + bbm_byte = oob[0]; + if (chip->options & NAND_BUSWIDTH_16) + bbm_byte &= oob[1]; - memset(nandc->data_buffer, 0xff, host->cw_data); - /* override new oob content to last codeword */ - mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob, - 0, mtd->oobavail); + /* Write BBM bytes by doing raw write. */ + if (bbm_byte != 0xff) { + host->use_ecc = false; + memset(nandc->data_buffer, 0xff, host->cw_size); + /* Determine the BBM bytes position and update the same */ + bbm_offset = mtd->writesize - host->cw_size * (ecc->steps - 1); + memcpy(nandc->data_buffer + bbm_offset, oob, host->bbm_size); + write_size = host->cw_size; + /* Write OOB bytes by doing normal write with ECC */ + } else { + host->use_ecc = true; + /* calculate the data and oob size for the last codeword/step */ + data_size = ecc->size - ((ecc->steps - 1) << 2); + oob_size = mtd->oobavail; + + memset(nandc->data_buffer, 0xff, host->cw_data); + /* override new oob content to last codeword */ + mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, + oob, 0, mtd->oobavail); + + write_size = data_size + oob_size; + } set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, false); config_nand_page_write(nandc); write_data_dma(nandc, FLASH_BUF_ACC, - nandc->data_buffer, data_size + oob_size, 0); + nandc->data_buffer, write_size, 0); config_nand_cw_write(nandc); ret = submit_descs(nandc); @@ -2174,48 +2203,6 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, return nand_prog_page_end_op(chip); } -static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = >ecc; - int page, ret; - - clear_read_regs(nandc); - clear_bam_transaction(nand
[PATCH 3/5] mtd: rawnand: qcom: fix NAND register write errors
Fix the following NAND register write errors which will be generated if access protection is enabled. 1. SFLASHC_BURST_CFG register is not available for supported NAND contollers by this driver, so this can be removed. 2. NAND_CTRL is operational register and register writes to operational registers should always be done through command descriptors if BAM_MODE is already enabled. With full boot chain, bootloader already enables BAM_MODE so read the NAND_CTRL register value and write only if BAM_MODE is not set. Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/raw/qcom_nandc.c | 11 --- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index df12cf3..9e6b383 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2693,15 +2693,20 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc) { u32 nand_ctrl; - /* kill onenand */ - nandc_write(nandc, SFLASHC_BURST_CFG, 0); nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD), NAND_DEV_CMD_VLD_VAL); /* enable ADM or BAM DMA */ if (nandc->props->is_bam) { nand_ctrl = nandc_read(nandc, NAND_CTRL); - nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN); + /* +* Once BAM_MODE_EN bit is set then QPIC_NAND_CTRL register +* should be written with BAM instead of writel. +* Check if BAM_MODE_EN is already set by bootloader and write +* only if this bit is not set. +*/ + if (!(nand_ctrl & BAM_MODE_EN)) + nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN); } else { nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); } -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH 2/5] mtd: rawnand: qcom: remove driver specific block_markbad function
The NAND base layer calls write_oob() by setting bytes at chip->badblockpos with value non 0xFF for updating bad block status. The QCOM NAND controller skips the bad block bytes while doing normal write with ECC enabled. When initial support for this driver was added, the driver specific function was added temporarily for block_markbad() with assumption to change for raw read in NAND base layer. Moving to raw read for block_markbad() seems to take more time so this patch removes driver specific block_markbad() function by using following HACK in write_oob() function. Check for BBM bytes in OOB and accordingly do raw write for updating BBM bytes in NAND flash or normal write for updating available OOB bytes. Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/raw/qcom_nandc.c | 103 +++--- 1 file changed, 40 insertions(+), 63 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index ea253ac..df12cf3 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2138,28 +2138,57 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, struct qcom_nand_host *host = to_qcom_nand_host(chip); struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct nand_ecc_ctrl *ecc = >ecc; - u8 *oob = chip->oob_poi; - int data_size, oob_size; + u8 *oob = chip->oob_poi, bbm_byte; + int data_size, oob_size, bbm_offset, write_size; int ret; - host->use_ecc = true; clear_bam_transaction(nandc); - /* calculate the data and oob size for the last codeword/step */ - data_size = ecc->size - ((ecc->steps - 1) << 2); - oob_size = mtd->oobavail; + /* +* The NAND base layer calls ecc->write_oob() by setting bytes at +* chip->badblockpos (chip->badblockpos will be 0 for QCOM NAND +* controller layout) in OOB buffer with value other that 0xFF +* for updating bad block status. QCOM NAND controller skips +* BBM bytes while writing with ECC, so following HACK has been +* added in this function for using generic block_markbad() function. +* +* Check for BBM bytes in OOB and accordingly do raw write for +* updating BBM bytes in NAND flash or normal write with ECC for +* updating available OOB bytes. +*/ + bbm_byte = oob[0]; + if (chip->options & NAND_BUSWIDTH_16) + bbm_byte &= oob[1]; - memset(nandc->data_buffer, 0xff, host->cw_data); - /* override new oob content to last codeword */ - mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob, - 0, mtd->oobavail); + /* Write BBM bytes by doing raw write. */ + if (bbm_byte != 0xff) { + host->use_ecc = false; + memset(nandc->data_buffer, 0xff, host->cw_size); + /* Determine the BBM bytes position and update the same */ + bbm_offset = mtd->writesize - host->cw_size * (ecc->steps - 1); + memcpy(nandc->data_buffer + bbm_offset, oob, host->bbm_size); + write_size = host->cw_size; + /* Write OOB bytes by doing normal write with ECC */ + } else { + host->use_ecc = true; + /* calculate the data and oob size for the last codeword/step */ + data_size = ecc->size - ((ecc->steps - 1) << 2); + oob_size = mtd->oobavail; + + memset(nandc->data_buffer, 0xff, host->cw_data); + /* override new oob content to last codeword */ + mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, + oob, 0, mtd->oobavail); + + write_size = data_size + oob_size; + } set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, false); config_nand_page_write(nandc); write_data_dma(nandc, FLASH_BUF_ACC, - nandc->data_buffer, data_size + oob_size, 0); + nandc->data_buffer, write_size, 0); config_nand_cw_write(nandc); ret = submit_descs(nandc); @@ -2174,48 +2203,6 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, return nand_prog_page_end_op(chip); } -static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = >ecc; - int page, ret; - - clear_read_regs(nandc); - clear_bam_transaction(nand
[PATCH 4/5] mtd: rawnand: qcom: update BBT related flags
Remove the NAND_SKIP_BBTSCAN to use RAM based BBT. Flash based BBT is not used since bootloaders doesn't have support for the same. Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/raw/qcom_nandc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 9e6b383..6fb85d3 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2750,8 +2750,7 @@ static int qcom_nand_host_init(struct qcom_nand_controller *nandc, chip->get_features = nand_get_set_features_notsupp; chip->controller = >controller; - chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER | -NAND_SKIP_BBTSCAN; + chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER; /* set up initial status value */ host->status = NAND_STATUS_READY | NAND_STATUS_WP; -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH 4/5] mtd: rawnand: qcom: update BBT related flags
Remove the NAND_SKIP_BBTSCAN to use RAM based BBT. Flash based BBT is not used since bootloaders doesn't have support for the same. Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/raw/qcom_nandc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 9e6b383..6fb85d3 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2750,8 +2750,7 @@ static int qcom_nand_host_init(struct qcom_nand_controller *nandc, chip->get_features = nand_get_set_features_notsupp; chip->controller = >controller; - chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER | -NAND_SKIP_BBTSCAN; + chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER; /* set up initial status value */ host->status = NAND_STATUS_READY | NAND_STATUS_WP; -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH 5/5] mtd: rawnand: qcom: reorganization by removing read/write helpers
Driver does not send the commands to NAND device for page read/write operations in ->cmdfunc(). It just does some minor variable initialization and rest of the things are being done in actual ->read/write_oob[_raw]. The generic helper function calls for invoking commands during page read/write are making this driver complicated. For QCOM NAND driver, ->cmdfunc() does minor part of initialization and rest of the initialization is performed by actual page read/write functions. Also, ->read/write_oob() does not calls helper function and all the initialization is being done in ->read/write_oob() itself. Since after 'commit 25f815f66a14 ("mtd: nand: force drivers to explicitly send READ/PROG commands")', sending of commands has been moved to driver for page read/write, so this patch does following changes to make code more readable: 1. Introduce qcom_nand_init_page_op() and qcom_nand_start_page_op() helper functions which helps in removing code duplication in each operation. 2. After issuing PROGRAM PAGE/BLOCK ERASE, QCOM NAND controller waits for BUSY signal to be de asserted and automatically issues the READ STATUS command. Currently, driver is storing this status privately and returns the same when status command comes from helper function after program/erase operation. Now, for write operations, the status can be returned from main function itself, so storing status can be removed for program operations. Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/raw/qcom_nandc.c | 222 -- 1 file changed, 91 insertions(+), 131 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 6fb85d3..f73ee0e 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1382,39 +1382,37 @@ static void pre_command(struct qcom_nand_host *host, int command) host->last_command = command; clear_read_regs(nandc); - - if (command == NAND_CMD_RESET || command == NAND_CMD_READID || - command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) - clear_bam_transaction(nandc); + clear_bam_transaction(nandc); } /* - * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our - * privately maintained status byte, this status byte can be read after - * NAND_CMD_STATUS is called + * QCOM NAND controller by default issues READ STATUS command after PROGRAM + * PAGE/BLOCK ERASE operation and updates the same in its internal status + * register for last codeword. This function parses status for each CW and + * return actual status byte for write/erase operation. */ -static void parse_erase_write_errors(struct qcom_nand_host *host, int command) +static u8 parse_erase_write_errors(struct qcom_nand_host *host, int num_cw) { struct nand_chip *chip = >chip; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = >ecc; - int num_cw; int i; + u8 status = 0; - num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1; nandc_read_buffer_sync(nandc, true); for (i = 0; i < num_cw; i++) { u32 flash_status = le32_to_cpu(nandc->reg_read_buf[i]); if (flash_status & FS_MPU_ERR) - host->status &= ~NAND_STATUS_WP; + status &= ~NAND_STATUS_WP; if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && (flash_status & FS_DEVICE_STS_ERR))) - host->status |= NAND_STATUS_FAIL; + status |= NAND_STATUS_FAIL; } + + return status; } static void post_command(struct qcom_nand_host *host, int command) @@ -1428,9 +1426,12 @@ static void post_command(struct qcom_nand_host *host, int command) memcpy(nandc->data_buffer, nandc->reg_read_buf, nandc->buf_count); break; - case NAND_CMD_PAGEPROG: case NAND_CMD_ERASE1: - parse_erase_write_errors(host, command); + /* +* update privately maintained status byte, this status byte can +* be read after NAND_CMD_STATUS is called. +*/ + host->status = parse_erase_write_errors(host, 1); break; default: break; @@ -1448,7 +1449,6 @@ static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command, { struct nand_chip *chip = mtd_to_nand(mtd); struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct nand_ecc_ctrl *ecc = >ecc; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); bool wait = false; int ret = 0; @@ -1
[PATCH 5/5] mtd: rawnand: qcom: reorganization by removing read/write helpers
Driver does not send the commands to NAND device for page read/write operations in ->cmdfunc(). It just does some minor variable initialization and rest of the things are being done in actual ->read/write_oob[_raw]. The generic helper function calls for invoking commands during page read/write are making this driver complicated. For QCOM NAND driver, ->cmdfunc() does minor part of initialization and rest of the initialization is performed by actual page read/write functions. Also, ->read/write_oob() does not calls helper function and all the initialization is being done in ->read/write_oob() itself. Since after 'commit 25f815f66a14 ("mtd: nand: force drivers to explicitly send READ/PROG commands")', sending of commands has been moved to driver for page read/write, so this patch does following changes to make code more readable: 1. Introduce qcom_nand_init_page_op() and qcom_nand_start_page_op() helper functions which helps in removing code duplication in each operation. 2. After issuing PROGRAM PAGE/BLOCK ERASE, QCOM NAND controller waits for BUSY signal to be de asserted and automatically issues the READ STATUS command. Currently, driver is storing this status privately and returns the same when status command comes from helper function after program/erase operation. Now, for write operations, the status can be returned from main function itself, so storing status can be removed for program operations. Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/raw/qcom_nandc.c | 222 -- 1 file changed, 91 insertions(+), 131 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 6fb85d3..f73ee0e 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1382,39 +1382,37 @@ static void pre_command(struct qcom_nand_host *host, int command) host->last_command = command; clear_read_regs(nandc); - - if (command == NAND_CMD_RESET || command == NAND_CMD_READID || - command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) - clear_bam_transaction(nandc); + clear_bam_transaction(nandc); } /* - * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our - * privately maintained status byte, this status byte can be read after - * NAND_CMD_STATUS is called + * QCOM NAND controller by default issues READ STATUS command after PROGRAM + * PAGE/BLOCK ERASE operation and updates the same in its internal status + * register for last codeword. This function parses status for each CW and + * return actual status byte for write/erase operation. */ -static void parse_erase_write_errors(struct qcom_nand_host *host, int command) +static u8 parse_erase_write_errors(struct qcom_nand_host *host, int num_cw) { struct nand_chip *chip = >chip; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = >ecc; - int num_cw; int i; + u8 status = 0; - num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1; nandc_read_buffer_sync(nandc, true); for (i = 0; i < num_cw; i++) { u32 flash_status = le32_to_cpu(nandc->reg_read_buf[i]); if (flash_status & FS_MPU_ERR) - host->status &= ~NAND_STATUS_WP; + status &= ~NAND_STATUS_WP; if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && (flash_status & FS_DEVICE_STS_ERR))) - host->status |= NAND_STATUS_FAIL; + status |= NAND_STATUS_FAIL; } + + return status; } static void post_command(struct qcom_nand_host *host, int command) @@ -1428,9 +1426,12 @@ static void post_command(struct qcom_nand_host *host, int command) memcpy(nandc->data_buffer, nandc->reg_read_buf, nandc->buf_count); break; - case NAND_CMD_PAGEPROG: case NAND_CMD_ERASE1: - parse_erase_write_errors(host, command); + /* +* update privately maintained status byte, this status byte can +* be read after NAND_CMD_STATUS is called. +*/ + host->status = parse_erase_write_errors(host, 1); break; default: break; @@ -1448,7 +1449,6 @@ static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command, { struct nand_chip *chip = mtd_to_nand(mtd); struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct nand_ecc_ctrl *ecc = >ecc; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); bool wait = false; int ret = 0; @@ -1
[PATCH 1/5] mtd: rawnand: qcom: remove driver specific bad block check function
The QCOM NAND controller skips the bad block bytes when reading with ECC enabled. The NAND base layer invokes ->read_oob() function for bad block check which expects the BBM bytes to be copied at badblockpos offset in OOB buffer. When initial support for this driver was added, the driver specific function was added temporarily for bad block check with assumption to change for raw read in NAND base layer. Moving to raw read for bad block check seems to take more time so this patch removes driver specific bad block check function and uses following logic. The QCOM NAND controller updates the BAD_BLOCK_STATUS bits value in NAND_BUFFER_STATUS register with every page read operation. The OOB buffer can be updated with this value while checking the status for last codeword. QCOM NAND controller layout does not support BBM byte at offset other than zero so check the chip’s badblockpos and return probe failure for all these chips. Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/raw/qcom_nandc.c | 118 ++ 1 file changed, 31 insertions(+), 87 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 994f980..ea253ac 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -80,6 +80,8 @@ /* NAND_BUFFER_STATUS bits */ #defineBS_UNCORRECTABLE_BITBIT(8) #defineBS_CORRECTABLE_ERR_MSK 0x1f +#define BAD_BLK_STATUS 16 +#define BAD_BLK_STATUS_2 24 /* NAND_DEVn_CFG0 bits */ #defineDISABLE_STATUS_AFTER_WRITE 4 @@ -1742,16 +1744,14 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, unsigned int max_bitflips = 0, uncorrectable_cws = 0; struct read_stats *buf; bool flash_op_err = false, erased; - int i; + int i, data_len, oob_len; + u32 flash, erased_cw, buffer = 0; u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; buf = (struct read_stats *)nandc->reg_read_buf; nandc_read_buffer_sync(nandc, true); for (i = 0; i < ecc->steps; i++, buf++) { - u32 flash, buffer, erased_cw; - int data_len, oob_len; - if (i == (ecc->steps - 1)) { data_len = ecc->size - ((ecc->steps - 1) << 2); oob_len = ecc->steps << 2; @@ -1824,12 +1824,24 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, if (flash_op_err) return -EIO; - if (!uncorrectable_cws) - return max_bitflips; + if (uncorrectable_cws) { + max_bitflips = check_for_erased_page(host, data_buf_start, + oob_buf_start, uncorrectable_cws, + page, max_bitflips); + } - return check_for_erased_page(host, data_buf_start, oob_buf_start, -uncorrectable_cws, page, -max_bitflips); + /* +* Updates the BBM bytes in OOB buffer with BAD_BLK_STATUS bits value +* of NAND_BUFFER_STATUS register. With every page read operation, +* controller updates the bad block status data into this register. +*/ + if (oob_buf_start) { + oob_buf_start[0] = (buffer >> BAD_BLK_STATUS) & 0xff; + if (chip->options & NAND_BUSWIDTH_16) + oob_buf_start[1] = (buffer >> BAD_BLK_STATUS_2) & 0xff; + } + + return max_bitflips; } /* @@ -1913,41 +1925,6 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, return parse_read_errors(host, data_buf_start, oob_buf_start, page); } -/* - * a helper that copies the last step/codeword of a page (containing free oob) - * into our local buffer - */ -static int copy_last_cw(struct qcom_nand_host *host, int page) -{ - struct nand_chip *chip = >chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = >ecc; - int size; - int ret; - - clear_read_regs(nandc); - - size = host->use_ecc ? host->cw_data : host->cw_size; - - /* prepare a clean read buffer */ - memset(nandc->data_buffer, 0xff, size); - - set_address(host, host->cw_size * (ecc->steps - 1), page); - update_rw_regs(host, 1, true); - - config_nand_single_cw_page_read(nandc, host->use_ecc); - - read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); - - ret = submit_descs(nandc); - if (ret) - dev_err(nandc->dev, "failed to copy last codeword\n"); - - free_descs(nandc); - - return ret; -} - /* implements ecc->read_page() */ static int qcom_nandc_read_page(struct mtd_info *mtd,
[PATCH 1/5] mtd: rawnand: qcom: remove driver specific bad block check function
The QCOM NAND controller skips the bad block bytes when reading with ECC enabled. The NAND base layer invokes ->read_oob() function for bad block check which expects the BBM bytes to be copied at badblockpos offset in OOB buffer. When initial support for this driver was added, the driver specific function was added temporarily for bad block check with assumption to change for raw read in NAND base layer. Moving to raw read for bad block check seems to take more time so this patch removes driver specific bad block check function and uses following logic. The QCOM NAND controller updates the BAD_BLOCK_STATUS bits value in NAND_BUFFER_STATUS register with every page read operation. The OOB buffer can be updated with this value while checking the status for last codeword. QCOM NAND controller layout does not support BBM byte at offset other than zero so check the chip’s badblockpos and return probe failure for all these chips. Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/raw/qcom_nandc.c | 118 ++ 1 file changed, 31 insertions(+), 87 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 994f980..ea253ac 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -80,6 +80,8 @@ /* NAND_BUFFER_STATUS bits */ #defineBS_UNCORRECTABLE_BITBIT(8) #defineBS_CORRECTABLE_ERR_MSK 0x1f +#define BAD_BLK_STATUS 16 +#define BAD_BLK_STATUS_2 24 /* NAND_DEVn_CFG0 bits */ #defineDISABLE_STATUS_AFTER_WRITE 4 @@ -1742,16 +1744,14 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, unsigned int max_bitflips = 0, uncorrectable_cws = 0; struct read_stats *buf; bool flash_op_err = false, erased; - int i; + int i, data_len, oob_len; + u32 flash, erased_cw, buffer = 0; u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; buf = (struct read_stats *)nandc->reg_read_buf; nandc_read_buffer_sync(nandc, true); for (i = 0; i < ecc->steps; i++, buf++) { - u32 flash, buffer, erased_cw; - int data_len, oob_len; - if (i == (ecc->steps - 1)) { data_len = ecc->size - ((ecc->steps - 1) << 2); oob_len = ecc->steps << 2; @@ -1824,12 +1824,24 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, if (flash_op_err) return -EIO; - if (!uncorrectable_cws) - return max_bitflips; + if (uncorrectable_cws) { + max_bitflips = check_for_erased_page(host, data_buf_start, + oob_buf_start, uncorrectable_cws, + page, max_bitflips); + } - return check_for_erased_page(host, data_buf_start, oob_buf_start, -uncorrectable_cws, page, -max_bitflips); + /* +* Updates the BBM bytes in OOB buffer with BAD_BLK_STATUS bits value +* of NAND_BUFFER_STATUS register. With every page read operation, +* controller updates the bad block status data into this register. +*/ + if (oob_buf_start) { + oob_buf_start[0] = (buffer >> BAD_BLK_STATUS) & 0xff; + if (chip->options & NAND_BUSWIDTH_16) + oob_buf_start[1] = (buffer >> BAD_BLK_STATUS_2) & 0xff; + } + + return max_bitflips; } /* @@ -1913,41 +1925,6 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, return parse_read_errors(host, data_buf_start, oob_buf_start, page); } -/* - * a helper that copies the last step/codeword of a page (containing free oob) - * into our local buffer - */ -static int copy_last_cw(struct qcom_nand_host *host, int page) -{ - struct nand_chip *chip = >chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = >ecc; - int size; - int ret; - - clear_read_regs(nandc); - - size = host->use_ecc ? host->cw_data : host->cw_size; - - /* prepare a clean read buffer */ - memset(nandc->data_buffer, 0xff, size); - - set_address(host, host->cw_size * (ecc->steps - 1), page); - update_rw_regs(host, 1, true); - - config_nand_single_cw_page_read(nandc, host->use_ecc); - - read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); - - ret = submit_descs(nandc); - if (ret) - dev_err(nandc->dev, "failed to copy last codeword\n"); - - free_descs(nandc); - - return ret; -} - /* implements ecc->read_page() */ static int qcom_nandc_read_page(struct mtd_info *mtd,
[PATCH v5] mtd: rawnand: qcom: erased page bitflips detection
NAND parts can have bitflips in an erased page due to the process technology used. In this case, QCOM NAND controller is not able to identify that page as an erased page. Currently the driver calls nand_check_erased_ecc_chunk() for identifying the erased pages but this won’t work always since the checking is being with ECC engine returned data. In case of bitflips, the ECC engine tries to correct the data and then it generates the uncorrectable error. Now, this data is not equal to original raw data. For erased CW identification, the raw data should be read again from NAND device and this nand_check_erased_ecc_chunk function() should be called for raw data only. Now following logic is being added to identify the erased codeword bitflips. 1. In most of the cases, not all the codewords will have bitflips and only single CW will have bitflips. So, there is no need to read the complete raw page data. The NAND raw read can be scheduled for any CW in page. The NAND controller works on CW basis and it will update the status register after each CW read. Maintain the bitmask for the CW which generated the uncorrectable error. 2. Do raw read for all the CW's which generated the uncorrectable error. 3. Both DATA and OOB need to be checked for number of 0. The top-level API can be called with only data buf or OOB buf so use chip->databuf if data buf is null and chip->oob_poi if OOB buf is null for copying the raw bytes temporarily. 4. For each CW, check the number of 0 in cw_data and usable oob bytes, The bbm and spare (unused) bytes bit flip won’t affect the ECC so don’t check the number of bitflips in this area. Signed-off-by: Abhishek Sahu --- * Changes from v4: 1. Used for_each_set_bit for determining CW’s which generated uncorrectable errors. 2. Introduced cw_data_buf and cw_oob_buf which will have starting buffer address for current codeword and used the same in helper functions. 3. Added new line before calling of nand_check_erased_ecc_chunk for better code readability. * Changes from v3: 1. Major changes in erased codeword detection for raw read function * Changes from v2: NONE * Changes from v1: 1. Minor change in commit message 2. invalidate pagebuf if databuf or oob_poi is used drivers/mtd/nand/raw/qcom_nandc.c | 126 +++--- 1 file changed, 89 insertions(+), 37 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 160acdf..994f980 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1656,20 +1656,94 @@ static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) } /* + * Bitflips can happen in erased codewords also so this function counts the + * number of 0 in each CW for which ECC engine returns the uncorrectable + * error. The page will be assumed as erased if this count is less than or + * equal to the ecc->strength for each CW. + * + * 1. Both DATA and OOB need to be checked for number of 0. The + *top-level API can be called with only data buf or OOB buf so use + *chip->data_buf if data buf is null and chip->oob_poi if oob buf + *is null for copying the raw bytes. + * 2. Perform raw read for all the CW which has uncorrectable errors. + * 3. For each CW, check the number of 0 in cw_data and usable OOB bytes. + *The BBM and spare bytes bit flip won’t affect the ECC so don’t check + *the number of bitflips in this area. + */ +static int +check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf, + u8 *oob_buf, unsigned long uncorrectable_cws, + int page, unsigned int max_bitflips) +{ + struct nand_chip *chip = >chip; + struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_ecc_ctrl *ecc = >ecc; + u8 *cw_data_buf, *cw_oob_buf; + int cw, data_size, oob_size, ret = 0; + + if (!data_buf) { + data_buf = chip->data_buf; + chip->pagebuf = -1; + } + + if (!oob_buf) { + oob_buf = chip->oob_poi; + chip->pagebuf = -1; + } + + for_each_set_bit(cw, _cws, ecc->steps) { + if (cw == (ecc->steps - 1)) { + data_size = ecc->size - ((ecc->steps - 1) * 4); + oob_size = (ecc->steps * 4) + host->ecc_bytes_hw; + } else { + data_size = host->cw_data; + oob_size = host->ecc_bytes_hw; + } + + /* determine starting buffer address for current CW */ + cw_data_buf = data_buf + (cw * host->cw_data); + cw_oob_buf = oob_buf + (cw * ecc->bytes); + + ret = qcom_nandc_read_cw_raw(mtd, chip, cw_data_buf, +cw_oob_buf, page, c
[PATCH v5] mtd: rawnand: qcom: erased page bitflips detection
NAND parts can have bitflips in an erased page due to the process technology used. In this case, QCOM NAND controller is not able to identify that page as an erased page. Currently the driver calls nand_check_erased_ecc_chunk() for identifying the erased pages but this won’t work always since the checking is being with ECC engine returned data. In case of bitflips, the ECC engine tries to correct the data and then it generates the uncorrectable error. Now, this data is not equal to original raw data. For erased CW identification, the raw data should be read again from NAND device and this nand_check_erased_ecc_chunk function() should be called for raw data only. Now following logic is being added to identify the erased codeword bitflips. 1. In most of the cases, not all the codewords will have bitflips and only single CW will have bitflips. So, there is no need to read the complete raw page data. The NAND raw read can be scheduled for any CW in page. The NAND controller works on CW basis and it will update the status register after each CW read. Maintain the bitmask for the CW which generated the uncorrectable error. 2. Do raw read for all the CW's which generated the uncorrectable error. 3. Both DATA and OOB need to be checked for number of 0. The top-level API can be called with only data buf or OOB buf so use chip->databuf if data buf is null and chip->oob_poi if OOB buf is null for copying the raw bytes temporarily. 4. For each CW, check the number of 0 in cw_data and usable oob bytes, The bbm and spare (unused) bytes bit flip won’t affect the ECC so don’t check the number of bitflips in this area. Signed-off-by: Abhishek Sahu --- * Changes from v4: 1. Used for_each_set_bit for determining CW’s which generated uncorrectable errors. 2. Introduced cw_data_buf and cw_oob_buf which will have starting buffer address for current codeword and used the same in helper functions. 3. Added new line before calling of nand_check_erased_ecc_chunk for better code readability. * Changes from v3: 1. Major changes in erased codeword detection for raw read function * Changes from v2: NONE * Changes from v1: 1. Minor change in commit message 2. invalidate pagebuf if databuf or oob_poi is used drivers/mtd/nand/raw/qcom_nandc.c | 126 +++--- 1 file changed, 89 insertions(+), 37 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 160acdf..994f980 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1656,20 +1656,94 @@ static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) } /* + * Bitflips can happen in erased codewords also so this function counts the + * number of 0 in each CW for which ECC engine returns the uncorrectable + * error. The page will be assumed as erased if this count is less than or + * equal to the ecc->strength for each CW. + * + * 1. Both DATA and OOB need to be checked for number of 0. The + *top-level API can be called with only data buf or OOB buf so use + *chip->data_buf if data buf is null and chip->oob_poi if oob buf + *is null for copying the raw bytes. + * 2. Perform raw read for all the CW which has uncorrectable errors. + * 3. For each CW, check the number of 0 in cw_data and usable OOB bytes. + *The BBM and spare bytes bit flip won’t affect the ECC so don’t check + *the number of bitflips in this area. + */ +static int +check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf, + u8 *oob_buf, unsigned long uncorrectable_cws, + int page, unsigned int max_bitflips) +{ + struct nand_chip *chip = >chip; + struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_ecc_ctrl *ecc = >ecc; + u8 *cw_data_buf, *cw_oob_buf; + int cw, data_size, oob_size, ret = 0; + + if (!data_buf) { + data_buf = chip->data_buf; + chip->pagebuf = -1; + } + + if (!oob_buf) { + oob_buf = chip->oob_poi; + chip->pagebuf = -1; + } + + for_each_set_bit(cw, _cws, ecc->steps) { + if (cw == (ecc->steps - 1)) { + data_size = ecc->size - ((ecc->steps - 1) * 4); + oob_size = (ecc->steps * 4) + host->ecc_bytes_hw; + } else { + data_size = host->cw_data; + oob_size = host->ecc_bytes_hw; + } + + /* determine starting buffer address for current CW */ + cw_data_buf = data_buf + (cw * host->cw_data); + cw_oob_buf = oob_buf + (cw * ecc->bytes); + + ret = qcom_nandc_read_cw_raw(mtd, chip, cw_data_buf, +cw_oob_buf, page, c
Re: [PATCH v4 00/15] Update for QCOM NAND driver
On 2018-07-01 23:39, Miquel Raynal wrote: Hi Abhishek, Abhishek Sahu wrote on Wed, 20 Jun 2018 12:57:27 +0530: * v4: 1. Added patch to make other ECC configurations function static. 2. Clubbed the DT update patches. 3. Removed the bad block related patch. Discussion is going on related with for proper solution so planning to submit separate patch series for all bad block related changes. 4. Made the single codeword raw read function and used the same for raw page read. 5. Changes in erased codeword detection to raw read function. * v3: 1. Addressed all review comments in v2. 2. Added patch for removing redundant nand-ecc-step-size DT property. 3. Renamed ECC configuration setup function with minor code changes. 4. Modified comments and commit message for few patches. * v2: 1. Addressed all review comments in v1. 1. Make the generic helper function for NAND ECC parameters setup and used this helper function for QCOM and Denali nand driver for ECC setup. 2. Modified commit message for some of the patches and added more comments. 3. Added new patch for fixing ‘return 0’ for raw read. 4. Removed the read last codeword part for nand oob write. 5. Reorganized bad block check function and removed the read_last_cw function completely. * v1: This patch series mainly deals with error handling and erased page bitflip detection for QCOM NAND driver. 1. The error handling was missing for some of the cases so fixed the same. 2. Add the support for taking ECC strength from ONFI parameter. The earlier QCOM boards were coming with 4-bit ECC chip but now the same boards are coming with 8-bit ECC chip since the earlier 4-bit parts are obsolete from some vendors. 3. We got few issues related with NAND erased page bitflips. The QCOM NAND controller can’t detect the bitflip in completely erased page so added the support to detect the same. It implemented the logic mentioned in patch [1] which didn’t go in mainline and later the generic functions were provided [2] to count the number of bitflips and make all 0xff. This patch series did some optimization logic to prevent the unnecessary full page raw read and data copy from QCOM NAND controller to DMA. 4. Following are the testing done for these patches in QCOM IPQ8074 HK01 (4-bit and 8-bit ECC chip) and IPQ806x AP148 boards. a. Run all mtd test and check if it passes b. Introduce custom bitflips in erased page and check if it returns no error/EUCLEAN/EBADMSG depending upon number of bitflips and position. c. Introduce failure condition for operational failure and check if it detects the same. [1]: https://patchwork.ozlabs.org/patch/328994/ [2]: https://patchwork.ozlabs.org/patch/509970/ Abhishek Sahu (15): mtd: rawnand: helper function for setting up ECC configuration mtd: rawnand: denali: use helper function for ecc setup dt-bindings: qcom_nandc: update for ECC strength and step size mtd: rawnand: qcom: remove dt property nand-ecc-step-size mtd: rawnand: qcom: use the ecc strength from device parameter mtd: rawnand: qcom: wait for desc completion in all BAM channels mtd: rawnand: qcom: erased page detection for uncorrectable errors only mtd: rawnand: qcom: fix null pointer access for erased page detection mtd: rawnand: qcom: parse read errors for read oob also mtd: rawnand: qcom: modify write_oob to remove read codeword part mtd: rawnand: qcom: fix return value for raw page read mtd: rawnand: qcom: check for operation errors in case of raw read mtd: rawnand: qcom: code reorganization for raw read mtd: rawnand: qcom: erased page bitflips detection mtd: rawnand: provide only single helper function for ECC conf .../devicetree/bindings/mtd/qcom_nandc.txt | 7 +- drivers/mtd/nand/raw/denali.c | 30 +- drivers/mtd/nand/raw/nand_base.c | 72 ++- drivers/mtd/nand/raw/qcom_nandc.c | 491 ++--- include/linux/mtd/rawnand.h| 10 +- 5 files changed, 380 insertions(+), 230 deletions(-) Thank you very much for the series and the changes you have done. Thanks a lot Miquel for your great support and help in reviewing and merging these patches :-) Applied all patches but "erased page bitflips detection" because I'm waiting for one small modification on this one. Sure. I will send that updated patch. Regards, Abhishek
Re: [PATCH v4 00/15] Update for QCOM NAND driver
On 2018-07-01 23:39, Miquel Raynal wrote: Hi Abhishek, Abhishek Sahu wrote on Wed, 20 Jun 2018 12:57:27 +0530: * v4: 1. Added patch to make other ECC configurations function static. 2. Clubbed the DT update patches. 3. Removed the bad block related patch. Discussion is going on related with for proper solution so planning to submit separate patch series for all bad block related changes. 4. Made the single codeword raw read function and used the same for raw page read. 5. Changes in erased codeword detection to raw read function. * v3: 1. Addressed all review comments in v2. 2. Added patch for removing redundant nand-ecc-step-size DT property. 3. Renamed ECC configuration setup function with minor code changes. 4. Modified comments and commit message for few patches. * v2: 1. Addressed all review comments in v1. 1. Make the generic helper function for NAND ECC parameters setup and used this helper function for QCOM and Denali nand driver for ECC setup. 2. Modified commit message for some of the patches and added more comments. 3. Added new patch for fixing ‘return 0’ for raw read. 4. Removed the read last codeword part for nand oob write. 5. Reorganized bad block check function and removed the read_last_cw function completely. * v1: This patch series mainly deals with error handling and erased page bitflip detection for QCOM NAND driver. 1. The error handling was missing for some of the cases so fixed the same. 2. Add the support for taking ECC strength from ONFI parameter. The earlier QCOM boards were coming with 4-bit ECC chip but now the same boards are coming with 8-bit ECC chip since the earlier 4-bit parts are obsolete from some vendors. 3. We got few issues related with NAND erased page bitflips. The QCOM NAND controller can’t detect the bitflip in completely erased page so added the support to detect the same. It implemented the logic mentioned in patch [1] which didn’t go in mainline and later the generic functions were provided [2] to count the number of bitflips and make all 0xff. This patch series did some optimization logic to prevent the unnecessary full page raw read and data copy from QCOM NAND controller to DMA. 4. Following are the testing done for these patches in QCOM IPQ8074 HK01 (4-bit and 8-bit ECC chip) and IPQ806x AP148 boards. a. Run all mtd test and check if it passes b. Introduce custom bitflips in erased page and check if it returns no error/EUCLEAN/EBADMSG depending upon number of bitflips and position. c. Introduce failure condition for operational failure and check if it detects the same. [1]: https://patchwork.ozlabs.org/patch/328994/ [2]: https://patchwork.ozlabs.org/patch/509970/ Abhishek Sahu (15): mtd: rawnand: helper function for setting up ECC configuration mtd: rawnand: denali: use helper function for ecc setup dt-bindings: qcom_nandc: update for ECC strength and step size mtd: rawnand: qcom: remove dt property nand-ecc-step-size mtd: rawnand: qcom: use the ecc strength from device parameter mtd: rawnand: qcom: wait for desc completion in all BAM channels mtd: rawnand: qcom: erased page detection for uncorrectable errors only mtd: rawnand: qcom: fix null pointer access for erased page detection mtd: rawnand: qcom: parse read errors for read oob also mtd: rawnand: qcom: modify write_oob to remove read codeword part mtd: rawnand: qcom: fix return value for raw page read mtd: rawnand: qcom: check for operation errors in case of raw read mtd: rawnand: qcom: code reorganization for raw read mtd: rawnand: qcom: erased page bitflips detection mtd: rawnand: provide only single helper function for ECC conf .../devicetree/bindings/mtd/qcom_nandc.txt | 7 +- drivers/mtd/nand/raw/denali.c | 30 +- drivers/mtd/nand/raw/nand_base.c | 72 ++- drivers/mtd/nand/raw/qcom_nandc.c | 491 ++--- include/linux/mtd/rawnand.h| 10 +- 5 files changed, 380 insertions(+), 230 deletions(-) Thank you very much for the series and the changes you have done. Thanks a lot Miquel for your great support and help in reviewing and merging these patches :-) Applied all patches but "erased page bitflips detection" because I'm waiting for one small modification on this one. Sure. I will send that updated patch. Regards, Abhishek
Re: [PATCH] arm: dts: qcom: Fix 'interrupts = <>' property to use proper macros
On 2018-06-20 14:53, Sricharan R wrote: Fix all nodes to use proper GIC_* macros for the interrupt type and the interrupt trigger settings to avoid the boot warnings. Thanks Sricharan for fixing these warnings. Applied over 4.18 rc1 and tested in IPQ8064 AP148 board. No backtraces are coming during boottime and IRQ seems OK. root@OpenWrt:/# cat /proc/interrupts CPU0 CPU1 16: 2602 4750 GIC-0 18 Edge gp_timer 17: 0 0 GIC-0 26 Level arm-pmu 23: 19 0 GIC-0 241 Level ahci[2900.sata] 24:912 0 GIC-0 184 Level msm_serial0 25:113 0 GIC-0 185 Level i2c_qup 26: 6 0 GIC-0 187 Level 1a28.spi IPI0: 0 0 CPU wakeup interrupts IPI1: 0 0 Timer broadcast interrupts IPI2: 2045 1713 Rescheduling interrupts IPI3: 1 4 Function call interrupts IPI4: 0 0 CPU stop interrupts IPI5: 0 0 IRQ work interrupts IPI6: 0 0 completion interrupts Err: 0 Signed-off-by: Sricharan R Tested-by: Abhishek Sahu
Re: [PATCH] arm: dts: qcom: Fix 'interrupts = <>' property to use proper macros
On 2018-06-20 14:53, Sricharan R wrote: Fix all nodes to use proper GIC_* macros for the interrupt type and the interrupt trigger settings to avoid the boot warnings. Thanks Sricharan for fixing these warnings. Applied over 4.18 rc1 and tested in IPQ8064 AP148 board. No backtraces are coming during boottime and IRQ seems OK. root@OpenWrt:/# cat /proc/interrupts CPU0 CPU1 16: 2602 4750 GIC-0 18 Edge gp_timer 17: 0 0 GIC-0 26 Level arm-pmu 23: 19 0 GIC-0 241 Level ahci[2900.sata] 24:912 0 GIC-0 184 Level msm_serial0 25:113 0 GIC-0 185 Level i2c_qup 26: 6 0 GIC-0 187 Level 1a28.spi IPI0: 0 0 CPU wakeup interrupts IPI1: 0 0 Timer broadcast interrupts IPI2: 2045 1713 Rescheduling interrupts IPI3: 1 4 Function call interrupts IPI4: 0 0 CPU stop interrupts IPI5: 0 0 IRQ work interrupts IPI6: 0 0 completion interrupts Err: 0 Signed-off-by: Sricharan R Tested-by: Abhishek Sahu
Re: [PATCH v3 13/16] mtd: rawnand: qcom: minor code reorganization for bad block check
On 2018-06-18 17:05, Miquel Raynal wrote: Hi Abhishek, Boris, one question for you below :) >> >> >> So for last CW, the 464 is BBM (i.e 2048th byte) in >> >> full page. >> >> >> > >> >> clear_bam_transaction(nandc); >> >> >> -ret = copy_last_cw(host, page); >> >> >> -if (ret) >> >> >> +clear_read_regs(nandc); >> >> >> + >> >> >> +set_address(host, host->cw_size * (ecc->steps - 1), page); >> >> >> +update_rw_regs(host, 1, true); >> >> >> + >> >> >> +/* >> >> >> + * The last codeword data will be copied from NAND device to NAND >> >> >> + * controller internal HW buffer. Copy only required BBM size bytes >> >> >> + * from this HW buffer to bbm_bytes_buf which is present at >> >> >> + * bbpos offset. >> >> >> + */ >> >> >> +nandc_set_read_loc(nandc, 0, bbpos, host->bbm_size, 1); >> >> >> +config_nand_single_cw_page_read(nandc); >> >> >> +read_data_dma(nandc, FLASH_BUF_ACC + bbpos, bbm_bytes_buf, >> >> >> + host->bbm_size, 0); >> >> >> + >> >> >> +ret = submit_descs(nandc); >> >> >> +free_descs(nandc); >> >> >> +if (ret) { >> >> >> +dev_err(nandc->dev, "failed to copy bad block bytes\n"); >> >> >> goto err; >> >> >> +} >> >> >> >> flash_status = le32_to_cpu(nandc->reg_read_buf[0]); >> >> >> >> @@ -2141,12 +2127,10 @@ static int qcom_nandc_block_bad(struct >> mtd_info *mtd, loff_t ofs) >> >> >> goto err; >> >> >> } >> >> >> >> - bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); >> >> >> - >> >> >> -bad = nandc->data_buffer[bbpos] != 0xff; >> >> >> +bad = bbm_bytes_buf[0] != 0xff; >> >> > > This is suspect as it still points to the beginning of the data buffer. >> >> > Can you please check you did not meant bbm_bytes_buf[bbpos]? >> >> > >> >> The main thing here is >> >> nandc_set_read_loc(nandc, 0, bbpos, host->bbm_size, 1); >> >> >> After reading one complete CW from NAND, the data will be still >> >> in NAND HW buffer. >> >> >> The above register tells that we need to read data from >> >> bbpos of size host->bbm_size (which is 1 byte for 8 bus witdh >> >> and 2 byte for 16 bus width) in bbm_bytes_buf. >> > > I see: idx 0 in bbm_bytes_buf is the data at offset bbpos. Then >> > it's ok. >> > >> >> So bbm_bytes_buf[0] will contain the BBM first byte. >> >> and bbm_bytes_buf[1] will contain the BBM second byte. >> >> >> Regards, >> >> Abhishek >> >> >> >> >>if (chip->options & NAND_BUSWIDTH_16) >> >> >> -bad = bad || (nandc->data_buffer[bbpos + 1] != 0xff); >> >> >> +bad = bad || (bbm_bytes_buf[1] != 0xff); >> > > Sorry, my mistake, I did not see the above line. >> > > However, technically, the BBM could be located in the first, second or >> > last page of the block. You should check the three of them are 0xFF >> > before declaring the block is not bad. >> > > The more I look at the function, the more I wonder if you actually need >> > it. Why does the generic nand_block_bad() implementation in the core >> > do not fit? >> >> The BBM bytes can be accessed in raw mode only for QCOM NAND >> Contoller. We started with following patch for initial patches >> >> http://patchwork.ozlabs.org/patch/508565/ >> >> I am also not very much sure, how can we go ahead now. >> Ideally we need to use generic function only which >> requires raw_read. >> > > I see, thanks for pointing this thread. > > Well for now then let's keep our driver-specific implementation. > > I will just ask you to do a consistent check as requested above (you > can copy code from the core) and add a comment above this function > explaining why it is needed (what you just told me). > Hi Miquel, I explored more regarding making custom bad block functions in this thread and it looks like, we can move to generic block_bad function by small changes in QCOM NAND driver only. The main problem was, in read page with ECC, the bad block byte was skipped. But controller is copying the bad block bytes in another register with following status bytes. BAD_BLOCK_STATUS : With every page read operation, when the controller reads a page with a bad block, it writes the bad block status data into this register. We can update the BBM bytes at start of OOB data in read_oob function with these status bytes. It will help in getting rid of driver-specific implementation for chip->block_bad. If think this is acceptable. For chip->block_markbad, if we want to get rid of driver-specific implementation then we can have following logic in write_oob function check for bad block bytes in oob and do the raw write for updating BBM bytes alone in flash if BBM bytes are non 0xff. Ok but this will have to be properly explained in a descriptive comment! Maybe Boris can give its point of view on the subject. Is it worth adding the above 'hacks' in the qcom driver and get rid of the driver-specific
Re: [PATCH v3 13/16] mtd: rawnand: qcom: minor code reorganization for bad block check
On 2018-06-18 17:05, Miquel Raynal wrote: Hi Abhishek, Boris, one question for you below :) >> >> >> So for last CW, the 464 is BBM (i.e 2048th byte) in >> >> full page. >> >> >> > >> >> clear_bam_transaction(nandc); >> >> >> -ret = copy_last_cw(host, page); >> >> >> -if (ret) >> >> >> +clear_read_regs(nandc); >> >> >> + >> >> >> +set_address(host, host->cw_size * (ecc->steps - 1), page); >> >> >> +update_rw_regs(host, 1, true); >> >> >> + >> >> >> +/* >> >> >> + * The last codeword data will be copied from NAND device to NAND >> >> >> + * controller internal HW buffer. Copy only required BBM size bytes >> >> >> + * from this HW buffer to bbm_bytes_buf which is present at >> >> >> + * bbpos offset. >> >> >> + */ >> >> >> +nandc_set_read_loc(nandc, 0, bbpos, host->bbm_size, 1); >> >> >> +config_nand_single_cw_page_read(nandc); >> >> >> +read_data_dma(nandc, FLASH_BUF_ACC + bbpos, bbm_bytes_buf, >> >> >> + host->bbm_size, 0); >> >> >> + >> >> >> +ret = submit_descs(nandc); >> >> >> +free_descs(nandc); >> >> >> +if (ret) { >> >> >> +dev_err(nandc->dev, "failed to copy bad block bytes\n"); >> >> >> goto err; >> >> >> +} >> >> >> >> flash_status = le32_to_cpu(nandc->reg_read_buf[0]); >> >> >> >> @@ -2141,12 +2127,10 @@ static int qcom_nandc_block_bad(struct >> mtd_info *mtd, loff_t ofs) >> >> >> goto err; >> >> >> } >> >> >> >> - bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); >> >> >> - >> >> >> -bad = nandc->data_buffer[bbpos] != 0xff; >> >> >> +bad = bbm_bytes_buf[0] != 0xff; >> >> > > This is suspect as it still points to the beginning of the data buffer. >> >> > Can you please check you did not meant bbm_bytes_buf[bbpos]? >> >> > >> >> The main thing here is >> >> nandc_set_read_loc(nandc, 0, bbpos, host->bbm_size, 1); >> >> >> After reading one complete CW from NAND, the data will be still >> >> in NAND HW buffer. >> >> >> The above register tells that we need to read data from >> >> bbpos of size host->bbm_size (which is 1 byte for 8 bus witdh >> >> and 2 byte for 16 bus width) in bbm_bytes_buf. >> > > I see: idx 0 in bbm_bytes_buf is the data at offset bbpos. Then >> > it's ok. >> > >> >> So bbm_bytes_buf[0] will contain the BBM first byte. >> >> and bbm_bytes_buf[1] will contain the BBM second byte. >> >> >> Regards, >> >> Abhishek >> >> >> >> >>if (chip->options & NAND_BUSWIDTH_16) >> >> >> -bad = bad || (nandc->data_buffer[bbpos + 1] != 0xff); >> >> >> +bad = bad || (bbm_bytes_buf[1] != 0xff); >> > > Sorry, my mistake, I did not see the above line. >> > > However, technically, the BBM could be located in the first, second or >> > last page of the block. You should check the three of them are 0xFF >> > before declaring the block is not bad. >> > > The more I look at the function, the more I wonder if you actually need >> > it. Why does the generic nand_block_bad() implementation in the core >> > do not fit? >> >> The BBM bytes can be accessed in raw mode only for QCOM NAND >> Contoller. We started with following patch for initial patches >> >> http://patchwork.ozlabs.org/patch/508565/ >> >> I am also not very much sure, how can we go ahead now. >> Ideally we need to use generic function only which >> requires raw_read. >> > > I see, thanks for pointing this thread. > > Well for now then let's keep our driver-specific implementation. > > I will just ask you to do a consistent check as requested above (you > can copy code from the core) and add a comment above this function > explaining why it is needed (what you just told me). > Hi Miquel, I explored more regarding making custom bad block functions in this thread and it looks like, we can move to generic block_bad function by small changes in QCOM NAND driver only. The main problem was, in read page with ECC, the bad block byte was skipped. But controller is copying the bad block bytes in another register with following status bytes. BAD_BLOCK_STATUS : With every page read operation, when the controller reads a page with a bad block, it writes the bad block status data into this register. We can update the BBM bytes at start of OOB data in read_oob function with these status bytes. It will help in getting rid of driver-specific implementation for chip->block_bad. If think this is acceptable. For chip->block_markbad, if we want to get rid of driver-specific implementation then we can have following logic in write_oob function check for bad block bytes in oob and do the raw write for updating BBM bytes alone in flash if BBM bytes are non 0xff. Ok but this will have to be properly explained in a descriptive comment! Maybe Boris can give its point of view on the subject. Is it worth adding the above 'hacks' in the qcom driver and get rid of the driver-specific
[PATCH v4 00/15] Update for QCOM NAND driver
* v4: 1. Added patch to make other ECC configurations function static. 2. Clubbed the DT update patches. 3. Removed the bad block related patch. Discussion is going on related with for proper solution so planning to submit separate patch series for all bad block related changes. 4. Made the single codeword raw read function and used the same for raw page read. 5. Changes in erased codeword detection to raw read function. * v3: 1. Addressed all review comments in v2. 2. Added patch for removing redundant nand-ecc-step-size DT property. 3. Renamed ECC configuration setup function with minor code changes. 4. Modified comments and commit message for few patches. * v2: 1. Addressed all review comments in v1. 1. Make the generic helper function for NAND ECC parameters setup and used this helper function for QCOM and Denali nand driver for ECC setup. 2. Modified commit message for some of the patches and added more comments. 3. Added new patch for fixing ‘return 0’ for raw read. 4. Removed the read last codeword part for nand oob write. 5. Reorganized bad block check function and removed the read_last_cw function completely. * v1: This patch series mainly deals with error handling and erased page bitflip detection for QCOM NAND driver. 1. The error handling was missing for some of the cases so fixed the same. 2. Add the support for taking ECC strength from ONFI parameter. The earlier QCOM boards were coming with 4-bit ECC chip but now the same boards are coming with 8-bit ECC chip since the earlier 4-bit parts are obsolete from some vendors. 3. We got few issues related with NAND erased page bitflips. The QCOM NAND controller can’t detect the bitflip in completely erased page so added the support to detect the same. It implemented the logic mentioned in patch [1] which didn’t go in mainline and later the generic functions were provided [2] to count the number of bitflips and make all 0xff. This patch series did some optimization logic to prevent the unnecessary full page raw read and data copy from QCOM NAND controller to DMA. 4. Following are the testing done for these patches in QCOM IPQ8074 HK01 (4-bit and 8-bit ECC chip) and IPQ806x AP148 boards. a. Run all mtd test and check if it passes b. Introduce custom bitflips in erased page and check if it returns no error/EUCLEAN/EBADMSG depending upon number of bitflips and position. c. Introduce failure condition for operational failure and check if it detects the same. [1]: https://patchwork.ozlabs.org/patch/328994/ [2]: https://patchwork.ozlabs.org/patch/509970/ Abhishek Sahu (15): mtd: rawnand: helper function for setting up ECC configuration mtd: rawnand: denali: use helper function for ecc setup dt-bindings: qcom_nandc: update for ECC strength and step size mtd: rawnand: qcom: remove dt property nand-ecc-step-size mtd: rawnand: qcom: use the ecc strength from device parameter mtd: rawnand: qcom: wait for desc completion in all BAM channels mtd: rawnand: qcom: erased page detection for uncorrectable errors only mtd: rawnand: qcom: fix null pointer access for erased page detection mtd: rawnand: qcom: parse read errors for read oob also mtd: rawnand: qcom: modify write_oob to remove read codeword part mtd: rawnand: qcom: fix return value for raw page read mtd: rawnand: qcom: check for operation errors in case of raw read mtd: rawnand: qcom: code reorganization for raw read mtd: rawnand: qcom: erased page bitflips detection mtd: rawnand: provide only single helper function for ECC conf .../devicetree/bindings/mtd/qcom_nandc.txt | 7 +- drivers/mtd/nand/raw/denali.c | 30 +- drivers/mtd/nand/raw/nand_base.c | 72 ++- drivers/mtd/nand/raw/qcom_nandc.c | 491 ++--- include/linux/mtd/rawnand.h| 10 +- 5 files changed, 380 insertions(+), 230 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 00/15] Update for QCOM NAND driver
* v4: 1. Added patch to make other ECC configurations function static. 2. Clubbed the DT update patches. 3. Removed the bad block related patch. Discussion is going on related with for proper solution so planning to submit separate patch series for all bad block related changes. 4. Made the single codeword raw read function and used the same for raw page read. 5. Changes in erased codeword detection to raw read function. * v3: 1. Addressed all review comments in v2. 2. Added patch for removing redundant nand-ecc-step-size DT property. 3. Renamed ECC configuration setup function with minor code changes. 4. Modified comments and commit message for few patches. * v2: 1. Addressed all review comments in v1. 1. Make the generic helper function for NAND ECC parameters setup and used this helper function for QCOM and Denali nand driver for ECC setup. 2. Modified commit message for some of the patches and added more comments. 3. Added new patch for fixing ‘return 0’ for raw read. 4. Removed the read last codeword part for nand oob write. 5. Reorganized bad block check function and removed the read_last_cw function completely. * v1: This patch series mainly deals with error handling and erased page bitflip detection for QCOM NAND driver. 1. The error handling was missing for some of the cases so fixed the same. 2. Add the support for taking ECC strength from ONFI parameter. The earlier QCOM boards were coming with 4-bit ECC chip but now the same boards are coming with 8-bit ECC chip since the earlier 4-bit parts are obsolete from some vendors. 3. We got few issues related with NAND erased page bitflips. The QCOM NAND controller can’t detect the bitflip in completely erased page so added the support to detect the same. It implemented the logic mentioned in patch [1] which didn’t go in mainline and later the generic functions were provided [2] to count the number of bitflips and make all 0xff. This patch series did some optimization logic to prevent the unnecessary full page raw read and data copy from QCOM NAND controller to DMA. 4. Following are the testing done for these patches in QCOM IPQ8074 HK01 (4-bit and 8-bit ECC chip) and IPQ806x AP148 boards. a. Run all mtd test and check if it passes b. Introduce custom bitflips in erased page and check if it returns no error/EUCLEAN/EBADMSG depending upon number of bitflips and position. c. Introduce failure condition for operational failure and check if it detects the same. [1]: https://patchwork.ozlabs.org/patch/328994/ [2]: https://patchwork.ozlabs.org/patch/509970/ Abhishek Sahu (15): mtd: rawnand: helper function for setting up ECC configuration mtd: rawnand: denali: use helper function for ecc setup dt-bindings: qcom_nandc: update for ECC strength and step size mtd: rawnand: qcom: remove dt property nand-ecc-step-size mtd: rawnand: qcom: use the ecc strength from device parameter mtd: rawnand: qcom: wait for desc completion in all BAM channels mtd: rawnand: qcom: erased page detection for uncorrectable errors only mtd: rawnand: qcom: fix null pointer access for erased page detection mtd: rawnand: qcom: parse read errors for read oob also mtd: rawnand: qcom: modify write_oob to remove read codeword part mtd: rawnand: qcom: fix return value for raw page read mtd: rawnand: qcom: check for operation errors in case of raw read mtd: rawnand: qcom: code reorganization for raw read mtd: rawnand: qcom: erased page bitflips detection mtd: rawnand: provide only single helper function for ECC conf .../devicetree/bindings/mtd/qcom_nandc.txt | 7 +- drivers/mtd/nand/raw/denali.c | 30 +- drivers/mtd/nand/raw/nand_base.c | 72 ++- drivers/mtd/nand/raw/qcom_nandc.c | 491 ++--- include/linux/mtd/rawnand.h| 10 +- 5 files changed, 380 insertions(+), 230 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 06/15] mtd: rawnand: qcom: wait for desc completion in all BAM channels
The BAM has 3 channels - tx, rx and command. command channel is used for register read/writes, tx channel for data writes and rx channel for data reads. Currently, the driver assumes the transfer completion once it gets all the command descriptors completed. Sometimes, there is race condition between data channel (tx/rx) and command channel completion. In these cases, the data present in buffer is not valid during small window between command descriptor completion and data descriptor completion. This patch generates NAND transfer completion when both (Data and Command) DMA channels have completed all its DMA descriptors. It assigns completion callback in last DMA descriptors of that channel and wait for completion. Fixes: 8d6b6d7e135e ("mtd: nand: qcom: support for command descriptor formation") Cc: sta...@vger.kernel.org Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. NONE * Changes from v2: 1. Changed commit message and comments slightly 2. Renamed wait_second_completion from first_chan_done and set it before submit desc 3. Mark for stable tree * Changes from v1: NONE drivers/mtd/nand/raw/qcom_nandc.c | 53 ++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 2375780..fc20149 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -213,6 +213,8 @@ #define QPIC_PER_CW_CMD_SGL32 #define QPIC_PER_CW_DATA_SGL 8 +#define QPIC_NAND_COMPLETION_TIMEOUT msecs_to_jiffies(2000) + /* * Flags used in DMA descriptor preparation helper functions * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma) @@ -245,6 +247,11 @@ * @tx_sgl_start - start index in data sgl for tx. * @rx_sgl_pos - current index in data sgl for rx. * @rx_sgl_start - start index in data sgl for rx. + * @wait_second_completion - wait for second DMA desc completion before making + * the NAND transfer completion. + * @txn_done - completion for NAND transfer. + * @last_data_desc - last DMA desc in data channel (tx/rx). + * @last_cmd_desc - last DMA desc in command channel. */ struct bam_transaction { struct bam_cmd_element *bam_ce; @@ -258,6 +265,10 @@ struct bam_transaction { u32 tx_sgl_start; u32 rx_sgl_pos; u32 rx_sgl_start; + bool wait_second_completion; + struct completion txn_done; + struct dma_async_tx_descriptor *last_data_desc; + struct dma_async_tx_descriptor *last_cmd_desc; }; /* @@ -504,6 +515,8 @@ static void free_bam_transaction(struct qcom_nand_controller *nandc) bam_txn->data_sgl = bam_txn_buf; + init_completion(_txn->txn_done); + return bam_txn; } @@ -523,11 +536,33 @@ static void clear_bam_transaction(struct qcom_nand_controller *nandc) bam_txn->tx_sgl_start = 0; bam_txn->rx_sgl_pos = 0; bam_txn->rx_sgl_start = 0; + bam_txn->last_data_desc = NULL; + bam_txn->wait_second_completion = false; sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * QPIC_PER_CW_CMD_SGL); sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage * QPIC_PER_CW_DATA_SGL); + + reinit_completion(_txn->txn_done); +} + +/* Callback for DMA descriptor completion */ +static void qpic_bam_dma_done(void *data) +{ + struct bam_transaction *bam_txn = data; + + /* +* In case of data transfer with NAND, 2 callbacks will be generated. +* One for command channel and another one for data channel. +* If current transaction has data descriptors +* (i.e. wait_second_completion is true), then set this to false +* and wait for second DMA descriptor completion. +*/ + if (bam_txn->wait_second_completion) + bam_txn->wait_second_completion = false; + else + complete(_txn->txn_done); } static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) @@ -756,6 +791,12 @@ static int prepare_bam_async_desc(struct qcom_nand_controller *nandc, desc->dma_desc = dma_desc; + /* update last data/command descriptor */ + if (chan == nandc->cmd_chan) + bam_txn->last_cmd_desc = dma_desc; + else + bam_txn->last_data_desc = dma_desc; + list_add_tail(>node, >desc_list); return 0; @@ -1273,10 +1314,20 @@ static int submit_descs(struct qcom_nand_controller *nandc) cookie = dmaengine_submit(desc->dma_desc); if (nandc->props->is_bam) { + bam_txn->last_cmd_desc->callback = qpic_bam_dma_done; + bam_txn->last_cmd_desc->callback_param = bam_txn; + if (bam_txn-&g
[PATCH v4 10/15] mtd: rawnand: qcom: modify write_oob to remove read codeword part
QCOM NAND controller layout protects available OOB data bytes with ECC also so when ecc->write_oob() is being called then it can't update just OOB bytes. Currently, it first reads the last codeword which includes old OOB bytes. Then it updates the old OOB bytes with new ones and then again writes the codeword back. The reading codeword is unnecessary since user is responsible to have these bytes cleared to 0xFF. This patch removes the read part and updates the OOB bytes with data area padded with OxFF. Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: NONE * Changes from v2: 1. Changed commit message and comments slightly 2. Changed host->use_ecc assignment place * Changes from v1: NEW CHANGE drivers/mtd/nand/raw/qcom_nandc.c | 16 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 285b2ad3..28361b5 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2064,11 +2064,9 @@ static int qcom_nandc_write_page_raw(struct mtd_info *mtd, /* * implements ecc->write_oob() * - * the NAND controller cannot write only data or only oob within a codeword, - * since ecc is calculated for the combined codeword. we first copy the - * entire contents for the last codeword(data + oob), replace the old oob - * with the new one in chip->oob_poi, and then write the entire codeword. - * this read-copy-write operation results in a slight performance loss. + * the NAND controller cannot write only data or only OOB within a codeword + * since ECC is calculated for the combined codeword. So update the OOB from + * chip->oob_poi, and pad the data area with OxFF before writing. */ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) @@ -2081,19 +2079,13 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int ret; host->use_ecc = true; - - clear_bam_transaction(nandc); - ret = copy_last_cw(host, page); - if (ret) - return ret; - - clear_read_regs(nandc); clear_bam_transaction(nandc); /* calculate the data and oob size for the last codeword/step */ data_size = ecc->size - ((ecc->steps - 1) << 2); oob_size = mtd->oobavail; + memset(nandc->data_buffer, 0xff, host->cw_data); /* override new oob content to last codeword */ mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob, 0, mtd->oobavail); -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 06/15] mtd: rawnand: qcom: wait for desc completion in all BAM channels
The BAM has 3 channels - tx, rx and command. command channel is used for register read/writes, tx channel for data writes and rx channel for data reads. Currently, the driver assumes the transfer completion once it gets all the command descriptors completed. Sometimes, there is race condition between data channel (tx/rx) and command channel completion. In these cases, the data present in buffer is not valid during small window between command descriptor completion and data descriptor completion. This patch generates NAND transfer completion when both (Data and Command) DMA channels have completed all its DMA descriptors. It assigns completion callback in last DMA descriptors of that channel and wait for completion. Fixes: 8d6b6d7e135e ("mtd: nand: qcom: support for command descriptor formation") Cc: sta...@vger.kernel.org Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. NONE * Changes from v2: 1. Changed commit message and comments slightly 2. Renamed wait_second_completion from first_chan_done and set it before submit desc 3. Mark for stable tree * Changes from v1: NONE drivers/mtd/nand/raw/qcom_nandc.c | 53 ++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 2375780..fc20149 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -213,6 +213,8 @@ #define QPIC_PER_CW_CMD_SGL32 #define QPIC_PER_CW_DATA_SGL 8 +#define QPIC_NAND_COMPLETION_TIMEOUT msecs_to_jiffies(2000) + /* * Flags used in DMA descriptor preparation helper functions * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma) @@ -245,6 +247,11 @@ * @tx_sgl_start - start index in data sgl for tx. * @rx_sgl_pos - current index in data sgl for rx. * @rx_sgl_start - start index in data sgl for rx. + * @wait_second_completion - wait for second DMA desc completion before making + * the NAND transfer completion. + * @txn_done - completion for NAND transfer. + * @last_data_desc - last DMA desc in data channel (tx/rx). + * @last_cmd_desc - last DMA desc in command channel. */ struct bam_transaction { struct bam_cmd_element *bam_ce; @@ -258,6 +265,10 @@ struct bam_transaction { u32 tx_sgl_start; u32 rx_sgl_pos; u32 rx_sgl_start; + bool wait_second_completion; + struct completion txn_done; + struct dma_async_tx_descriptor *last_data_desc; + struct dma_async_tx_descriptor *last_cmd_desc; }; /* @@ -504,6 +515,8 @@ static void free_bam_transaction(struct qcom_nand_controller *nandc) bam_txn->data_sgl = bam_txn_buf; + init_completion(_txn->txn_done); + return bam_txn; } @@ -523,11 +536,33 @@ static void clear_bam_transaction(struct qcom_nand_controller *nandc) bam_txn->tx_sgl_start = 0; bam_txn->rx_sgl_pos = 0; bam_txn->rx_sgl_start = 0; + bam_txn->last_data_desc = NULL; + bam_txn->wait_second_completion = false; sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * QPIC_PER_CW_CMD_SGL); sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage * QPIC_PER_CW_DATA_SGL); + + reinit_completion(_txn->txn_done); +} + +/* Callback for DMA descriptor completion */ +static void qpic_bam_dma_done(void *data) +{ + struct bam_transaction *bam_txn = data; + + /* +* In case of data transfer with NAND, 2 callbacks will be generated. +* One for command channel and another one for data channel. +* If current transaction has data descriptors +* (i.e. wait_second_completion is true), then set this to false +* and wait for second DMA descriptor completion. +*/ + if (bam_txn->wait_second_completion) + bam_txn->wait_second_completion = false; + else + complete(_txn->txn_done); } static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) @@ -756,6 +791,12 @@ static int prepare_bam_async_desc(struct qcom_nand_controller *nandc, desc->dma_desc = dma_desc; + /* update last data/command descriptor */ + if (chan == nandc->cmd_chan) + bam_txn->last_cmd_desc = dma_desc; + else + bam_txn->last_data_desc = dma_desc; + list_add_tail(>node, >desc_list); return 0; @@ -1273,10 +1314,20 @@ static int submit_descs(struct qcom_nand_controller *nandc) cookie = dmaengine_submit(desc->dma_desc); if (nandc->props->is_bam) { + bam_txn->last_cmd_desc->callback = qpic_bam_dma_done; + bam_txn->last_cmd_desc->callback_param = bam_txn; + if (bam_txn-&g
[PATCH v4 10/15] mtd: rawnand: qcom: modify write_oob to remove read codeword part
QCOM NAND controller layout protects available OOB data bytes with ECC also so when ecc->write_oob() is being called then it can't update just OOB bytes. Currently, it first reads the last codeword which includes old OOB bytes. Then it updates the old OOB bytes with new ones and then again writes the codeword back. The reading codeword is unnecessary since user is responsible to have these bytes cleared to 0xFF. This patch removes the read part and updates the OOB bytes with data area padded with OxFF. Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: NONE * Changes from v2: 1. Changed commit message and comments slightly 2. Changed host->use_ecc assignment place * Changes from v1: NEW CHANGE drivers/mtd/nand/raw/qcom_nandc.c | 16 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 285b2ad3..28361b5 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2064,11 +2064,9 @@ static int qcom_nandc_write_page_raw(struct mtd_info *mtd, /* * implements ecc->write_oob() * - * the NAND controller cannot write only data or only oob within a codeword, - * since ecc is calculated for the combined codeword. we first copy the - * entire contents for the last codeword(data + oob), replace the old oob - * with the new one in chip->oob_poi, and then write the entire codeword. - * this read-copy-write operation results in a slight performance loss. + * the NAND controller cannot write only data or only OOB within a codeword + * since ECC is calculated for the combined codeword. So update the OOB from + * chip->oob_poi, and pad the data area with OxFF before writing. */ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) @@ -2081,19 +2079,13 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int ret; host->use_ecc = true; - - clear_bam_transaction(nandc); - ret = copy_last_cw(host, page); - if (ret) - return ret; - - clear_read_regs(nandc); clear_bam_transaction(nandc); /* calculate the data and oob size for the last codeword/step */ data_size = ecc->size - ((ecc->steps - 1) << 2); oob_size = mtd->oobavail; + memset(nandc->data_buffer, 0xff, host->cw_data); /* override new oob content to last codeword */ mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob, 0, mtd->oobavail); -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 13/15] mtd: rawnand: qcom: code reorganization for raw read
Make separate function to perform raw read for one codeword and call this function multiple times for each codeword in case of raw page read. This separate function will help in subsequent patches related with erased codeword bitflip detection. It will decrease throughput for raw page read. Raw page read is used for debug purpose so it won't affect normal flash operations. Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. Major code changes for making separate function for raw_read 2. Changed commit message * Changes from v2: NONE * Changes from v1: 1. Included more detail in function comment drivers/mtd/nand/raw/qcom_nandc.c | 146 -- 1 file changed, 78 insertions(+), 68 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 5999c39..160acdf 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1587,6 +1587,74 @@ static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) return 0; } +/* performs raw read for one codeword */ +static int +qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, + u8 *data_buf, u8 *oob_buf, int page, int cw) +{ + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = >ecc; + int data_size1, data_size2, oob_size1, oob_size2; + int ret, reg_off = FLASH_BUF_ACC, read_loc = 0; + + nand_read_page_op(chip, page, 0, NULL, 0); + host->use_ecc = false; + + clear_bam_transaction(nandc); + set_address(host, host->cw_size * cw, page); + update_rw_regs(host, 1, true); + config_nand_page_read(nandc); + + data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); + oob_size1 = host->bbm_size; + + if (cw == (ecc->steps - 1)) { + data_size2 = ecc->size - data_size1 - +((ecc->steps - 1) * 4); + oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw + + host->spare_bytes; + } else { + data_size2 = host->cw_data - data_size1; + oob_size2 = host->ecc_bytes_hw + host->spare_bytes; + } + + if (nandc->props->is_bam) { + nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0); + read_loc += data_size1; + + nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0); + read_loc += oob_size1; + + nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0); + read_loc += data_size2; + + nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1); + } + + config_nand_cw_read(nandc, false); + + read_data_dma(nandc, reg_off, data_buf, data_size1, 0); + reg_off += data_size1; + + read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0); + reg_off += oob_size1; + + read_data_dma(nandc, reg_off, data_buf + data_size1, data_size2, 0); + reg_off += data_size2; + + read_data_dma(nandc, reg_off, oob_buf + oob_size1, oob_size2, 0); + + ret = submit_descs(nandc); + free_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to read raw cw %d\n", cw); + return ret; + } + + return check_flash_errors(host, 1); +} + /* * reads back status registers set by the controller to notify page read * errors. this is equivalent to what 'ecc->correct()' would do. @@ -1851,79 +1919,21 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, int oob_required, int page) { struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - u8 *data_buf, *oob_buf; struct nand_ecc_ctrl *ecc = >ecc; - int i, ret; - int read_loc; + int cw, ret; + u8 *data_buf = buf, *oob_buf = chip->oob_poi; - nand_read_page_op(chip, page, 0, NULL, 0); - data_buf = buf; - oob_buf = chip->oob_poi; + for (cw = 0; cw < ecc->steps; cw++) { + ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf, +page, cw); + if (ret) + return ret; - host->use_ecc = false; - - clear_bam_transaction(nandc); - update_rw_regs(host, ecc->steps, true); - config_nand_page_read(nandc); - - for (i = 0; i < ecc->steps; i++) { - int data_size1, data_size2, oob_size1, oob_size2; - int reg_off = FLASH_BUF_ACC; - - data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); - oob_size1 = host->bbm_siz
[PATCH v4 13/15] mtd: rawnand: qcom: code reorganization for raw read
Make separate function to perform raw read for one codeword and call this function multiple times for each codeword in case of raw page read. This separate function will help in subsequent patches related with erased codeword bitflip detection. It will decrease throughput for raw page read. Raw page read is used for debug purpose so it won't affect normal flash operations. Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. Major code changes for making separate function for raw_read 2. Changed commit message * Changes from v2: NONE * Changes from v1: 1. Included more detail in function comment drivers/mtd/nand/raw/qcom_nandc.c | 146 -- 1 file changed, 78 insertions(+), 68 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 5999c39..160acdf 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1587,6 +1587,74 @@ static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) return 0; } +/* performs raw read for one codeword */ +static int +qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, + u8 *data_buf, u8 *oob_buf, int page, int cw) +{ + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = >ecc; + int data_size1, data_size2, oob_size1, oob_size2; + int ret, reg_off = FLASH_BUF_ACC, read_loc = 0; + + nand_read_page_op(chip, page, 0, NULL, 0); + host->use_ecc = false; + + clear_bam_transaction(nandc); + set_address(host, host->cw_size * cw, page); + update_rw_regs(host, 1, true); + config_nand_page_read(nandc); + + data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); + oob_size1 = host->bbm_size; + + if (cw == (ecc->steps - 1)) { + data_size2 = ecc->size - data_size1 - +((ecc->steps - 1) * 4); + oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw + + host->spare_bytes; + } else { + data_size2 = host->cw_data - data_size1; + oob_size2 = host->ecc_bytes_hw + host->spare_bytes; + } + + if (nandc->props->is_bam) { + nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0); + read_loc += data_size1; + + nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0); + read_loc += oob_size1; + + nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0); + read_loc += data_size2; + + nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1); + } + + config_nand_cw_read(nandc, false); + + read_data_dma(nandc, reg_off, data_buf, data_size1, 0); + reg_off += data_size1; + + read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0); + reg_off += oob_size1; + + read_data_dma(nandc, reg_off, data_buf + data_size1, data_size2, 0); + reg_off += data_size2; + + read_data_dma(nandc, reg_off, oob_buf + oob_size1, oob_size2, 0); + + ret = submit_descs(nandc); + free_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to read raw cw %d\n", cw); + return ret; + } + + return check_flash_errors(host, 1); +} + /* * reads back status registers set by the controller to notify page read * errors. this is equivalent to what 'ecc->correct()' would do. @@ -1851,79 +1919,21 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, int oob_required, int page) { struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - u8 *data_buf, *oob_buf; struct nand_ecc_ctrl *ecc = >ecc; - int i, ret; - int read_loc; + int cw, ret; + u8 *data_buf = buf, *oob_buf = chip->oob_poi; - nand_read_page_op(chip, page, 0, NULL, 0); - data_buf = buf; - oob_buf = chip->oob_poi; + for (cw = 0; cw < ecc->steps; cw++) { + ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf, +page, cw); + if (ret) + return ret; - host->use_ecc = false; - - clear_bam_transaction(nandc); - update_rw_regs(host, ecc->steps, true); - config_nand_page_read(nandc); - - for (i = 0; i < ecc->steps; i++) { - int data_size1, data_size2, oob_size1, oob_size2; - int reg_off = FLASH_BUF_ACC; - - data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); - oob_size1 = host->bbm_siz
[PATCH v4 05/15] mtd: rawnand: qcom: use the ecc strength from device parameter
Currently the driver uses the ECC strength specified in DT. The QPIC/EBI2 NAND supports 4 or 8-bit ECC correction. The same kind of board can have different NAND parts so use the ECC strength from device parameters if it is not specified in DT. Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. Added parenthesis around (cwperpage * 4) * Changes from v2: 1. s/<< 2/* 4/ 2. Updated the cwperpage location 3. The block handling the ecc-step-size property has been removed in a previous patch * Changes from v1: 1. Removed the custom logic and used the helper fuction. drivers/mtd/nand/raw/qcom_nandc.c | 29 + 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index bf80a61..2375780 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2315,19 +2315,39 @@ static int qcom_nand_ooblayout_free(struct mtd_info *mtd, int section, .free = qcom_nand_ooblayout_free, }; +static int +qcom_nandc_calc_ecc_bytes(int step_size, int strength) +{ + return strength == 4 ? 12 : 16; +} +NAND_ECC_CAPS_SINGLE(qcom_nandc_ecc_caps, qcom_nandc_calc_ecc_bytes, +NANDC_STEP_SIZE, 4, 8); + static int qcom_nand_host_setup(struct qcom_nand_host *host) { struct nand_chip *chip = >chip; struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_ctrl *ecc = >ecc; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - int cwperpage, bad_block_byte; + int cwperpage, bad_block_byte, ret; bool wide_bus; int ecc_mode = 1; /* controller only supports 512 bytes data steps */ ecc->size = NANDC_STEP_SIZE; wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; + cwperpage = mtd->writesize / NANDC_STEP_SIZE; + + /* +* Each CW has 4 available OOB bytes which will be protected with ECC +* so remaining bytes can be used for ECC. +*/ + ret = nand_ecc_choose_conf(chip, _nandc_ecc_caps, + mtd->oobsize - (cwperpage * 4)); + if (ret) { + dev_err(nandc->dev, "No valid ECC settings possible\n"); + return ret; + } if (ecc->strength >= 8) { /* 8 bit ECC defaults to BCH ECC on all platforms */ @@ -2396,7 +2416,6 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) mtd_set_ooblayout(mtd, _nand_ooblayout_ops); - cwperpage = mtd->writesize / ecc->size; nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage, cwperpage); @@ -2412,12 +2431,6 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) * for 8 bit ECC */ host->cw_size = host->cw_data + ecc->bytes; - - if (ecc->bytes * (mtd->writesize / ecc->size) > mtd->oobsize) { - dev_err(nandc->dev, "ecc data doesn't fit in OOB area\n"); - return -EINVAL; - } - bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1; host->cfg0 = (cwperpage - 1) << CW_PER_PAGE -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 05/15] mtd: rawnand: qcom: use the ecc strength from device parameter
Currently the driver uses the ECC strength specified in DT. The QPIC/EBI2 NAND supports 4 or 8-bit ECC correction. The same kind of board can have different NAND parts so use the ECC strength from device parameters if it is not specified in DT. Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. Added parenthesis around (cwperpage * 4) * Changes from v2: 1. s/<< 2/* 4/ 2. Updated the cwperpage location 3. The block handling the ecc-step-size property has been removed in a previous patch * Changes from v1: 1. Removed the custom logic and used the helper fuction. drivers/mtd/nand/raw/qcom_nandc.c | 29 + 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index bf80a61..2375780 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2315,19 +2315,39 @@ static int qcom_nand_ooblayout_free(struct mtd_info *mtd, int section, .free = qcom_nand_ooblayout_free, }; +static int +qcom_nandc_calc_ecc_bytes(int step_size, int strength) +{ + return strength == 4 ? 12 : 16; +} +NAND_ECC_CAPS_SINGLE(qcom_nandc_ecc_caps, qcom_nandc_calc_ecc_bytes, +NANDC_STEP_SIZE, 4, 8); + static int qcom_nand_host_setup(struct qcom_nand_host *host) { struct nand_chip *chip = >chip; struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_ctrl *ecc = >ecc; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - int cwperpage, bad_block_byte; + int cwperpage, bad_block_byte, ret; bool wide_bus; int ecc_mode = 1; /* controller only supports 512 bytes data steps */ ecc->size = NANDC_STEP_SIZE; wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; + cwperpage = mtd->writesize / NANDC_STEP_SIZE; + + /* +* Each CW has 4 available OOB bytes which will be protected with ECC +* so remaining bytes can be used for ECC. +*/ + ret = nand_ecc_choose_conf(chip, _nandc_ecc_caps, + mtd->oobsize - (cwperpage * 4)); + if (ret) { + dev_err(nandc->dev, "No valid ECC settings possible\n"); + return ret; + } if (ecc->strength >= 8) { /* 8 bit ECC defaults to BCH ECC on all platforms */ @@ -2396,7 +2416,6 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) mtd_set_ooblayout(mtd, _nand_ooblayout_ops); - cwperpage = mtd->writesize / ecc->size; nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage, cwperpage); @@ -2412,12 +2431,6 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) * for 8 bit ECC */ host->cw_size = host->cw_data + ecc->bytes; - - if (ecc->bytes * (mtd->writesize / ecc->size) > mtd->oobsize) { - dev_err(nandc->dev, "ecc data doesn't fit in OOB area\n"); - return -EINVAL; - } - bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1; host->cfg0 = (cwperpage - 1) << CW_PER_PAGE -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 03/15] dt-bindings: qcom_nandc: update for ECC strength and step size
1. If nand-ecc-strength specified in DT, then controller will use this ECC strength otherwise ECC strength will be calculated according to chip requirement and available OOB size. 2. QCOM NAND controller supports only one step size (512 bytes) but nand-ecc-step-size is required property in DT. This DT property can be removed and ecc step size can be assigned in driver with 512 bytes value. Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. Clubbed following 2 patches into one https://patchwork.ozlabs.org/patch/920465/ https://patchwork.ozlabs.org/patch/920467/ * Changes from v2: NONE * Changes from v1: NEW PATCH Documentation/devicetree/bindings/mtd/qcom_nandc.txt | 7 +++ 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt index 73d336be..1123cc6 100644 --- a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt +++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt @@ -45,11 +45,12 @@ Required properties: number (e.g., 0, 1, 2, etc.) - #address-cells: see partition.txt - #size-cells: see partition.txt -- nand-ecc-strength: see nand.txt -- nand-ecc-step-size: must be 512. see nand.txt for more details. Optional properties: - nand-bus-width: see nand.txt +- nand-ecc-strength: see nand.txt. If not specified, then ECC strength will + be used according to chip requirement and available + OOB size. Each nandcs device node may optionally contain a 'partitions' sub-node, which further contains sub-nodes describing the flash partition mapping. See @@ -77,7 +78,6 @@ nand-controller@1ac0 { reg = <0>; nand-ecc-strength = <4>; - nand-ecc-step-size = <512>; nand-bus-width = <8>; partitions { @@ -117,7 +117,6 @@ nand-controller@79b { nand@0 { reg = <0>; nand-ecc-strength = <4>; - nand-ecc-step-size = <512>; nand-bus-width = <8>; partitions { -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 03/15] dt-bindings: qcom_nandc: update for ECC strength and step size
1. If nand-ecc-strength specified in DT, then controller will use this ECC strength otherwise ECC strength will be calculated according to chip requirement and available OOB size. 2. QCOM NAND controller supports only one step size (512 bytes) but nand-ecc-step-size is required property in DT. This DT property can be removed and ecc step size can be assigned in driver with 512 bytes value. Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. Clubbed following 2 patches into one https://patchwork.ozlabs.org/patch/920465/ https://patchwork.ozlabs.org/patch/920467/ * Changes from v2: NONE * Changes from v1: NEW PATCH Documentation/devicetree/bindings/mtd/qcom_nandc.txt | 7 +++ 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt index 73d336be..1123cc6 100644 --- a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt +++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt @@ -45,11 +45,12 @@ Required properties: number (e.g., 0, 1, 2, etc.) - #address-cells: see partition.txt - #size-cells: see partition.txt -- nand-ecc-strength: see nand.txt -- nand-ecc-step-size: must be 512. see nand.txt for more details. Optional properties: - nand-bus-width: see nand.txt +- nand-ecc-strength: see nand.txt. If not specified, then ECC strength will + be used according to chip requirement and available + OOB size. Each nandcs device node may optionally contain a 'partitions' sub-node, which further contains sub-nodes describing the flash partition mapping. See @@ -77,7 +78,6 @@ nand-controller@1ac0 { reg = <0>; nand-ecc-strength = <4>; - nand-ecc-step-size = <512>; nand-bus-width = <8>; partitions { @@ -117,7 +117,6 @@ nand-controller@79b { nand@0 { reg = <0>; nand-ecc-strength = <4>; - nand-ecc-step-size = <512>; nand-bus-width = <8>; partitions { -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 15/15] mtd: rawnand: provide only single helper function for ECC conf
Function nand_ecc_choose_conf() will be help for all the cases, so other helper functions can be made static. nand_check_ecc_caps(): Invoke nand_ecc_choose_conf() with both chip->ecc.size and chip->ecc.strength value set. nand_maximize_ecc(): Invoke nand_ecc_choose_conf() with NAND_ECC_MAXIMIZE flag. nand_match_ecc_req(): Invoke nand_ecc_choose_conf() with either chip->ecc.size or chip->ecc.strength value set and without NAND_ECC_MAXIMIZE flag. CC: Masahiro Yamada Signed-off-by: Abhishek Sahu --- Changes from v3: NEW PATCH drivers/mtd/nand/raw/nand_base.c | 39 +++ include/linux/mtd/rawnand.h | 9 - 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index c64e3fc..f620236 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -6077,24 +6077,17 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd) * by the controller and the calculated ECC bytes fit within the chip's OOB. * On success, the calculated ECC bytes is set. */ -int nand_check_ecc_caps(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail) +static int +nand_check_ecc_caps(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) { struct mtd_info *mtd = nand_to_mtd(chip); const struct nand_ecc_step_info *stepinfo; int preset_step = chip->ecc.size; int preset_strength = chip->ecc.strength; - int nsteps, ecc_bytes; + int ecc_bytes, nsteps = mtd->writesize / preset_step; int i, j; - if (WARN_ON(oobavail < 0)) - return -EINVAL; - - if (!preset_step || !preset_strength) - return -ENODATA; - - nsteps = mtd->writesize / preset_step; - for (i = 0; i < caps->nstepinfos; i++) { stepinfo = >stepinfos[i]; @@ -6127,7 +6120,6 @@ int nand_check_ecc_caps(struct nand_chip *chip, return -ENOTSUPP; } -EXPORT_SYMBOL_GPL(nand_check_ecc_caps); /** * nand_match_ecc_req - meet the chip's requirement with least ECC bytes @@ -6139,8 +6131,9 @@ int nand_check_ecc_caps(struct nand_chip *chip, * number of ECC bytes (i.e. with the largest number of OOB-free bytes). * On success, the chosen ECC settings are set. */ -int nand_match_ecc_req(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail) +static int +nand_match_ecc_req(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) { struct mtd_info *mtd = nand_to_mtd(chip); const struct nand_ecc_step_info *stepinfo; @@ -6151,9 +6144,6 @@ int nand_match_ecc_req(struct nand_chip *chip, int best_ecc_bytes_total = INT_MAX; int i, j; - if (WARN_ON(oobavail < 0)) - return -EINVAL; - /* No information provided by the NAND chip */ if (!req_step || !req_strength) return -ENOTSUPP; @@ -6212,7 +6202,6 @@ int nand_match_ecc_req(struct nand_chip *chip, return 0; } -EXPORT_SYMBOL_GPL(nand_match_ecc_req); /** * nand_maximize_ecc - choose the max ECC strength available @@ -6223,8 +6212,9 @@ int nand_match_ecc_req(struct nand_chip *chip, * Choose the max ECC strength that is supported on the controller, and can fit * within the chip's OOB. On success, the chosen ECC settings are set. */ -int nand_maximize_ecc(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail) +static int +nand_maximize_ecc(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) { struct mtd_info *mtd = nand_to_mtd(chip); const struct nand_ecc_step_info *stepinfo; @@ -6234,9 +6224,6 @@ int nand_maximize_ecc(struct nand_chip *chip, int best_strength, best_ecc_bytes; int i, j; - if (WARN_ON(oobavail < 0)) - return -EINVAL; - for (i = 0; i < caps->nstepinfos; i++) { stepinfo = >stepinfos[i]; step_size = stepinfo->stepsize; @@ -6285,7 +6272,6 @@ int nand_maximize_ecc(struct nand_chip *chip, return 0; } -EXPORT_SYMBOL_GPL(nand_maximize_ecc); /** * nand_ecc_choose_conf - Set the ECC strength and ECC step size @@ -6307,6 +6293,11 @@ int nand_maximize_ecc(struct nand_chip *chip, int nand_ecc_choose_conf(struct nand_chip *chip, const struct nand_ecc_caps *caps, int oobavail) { + struct mtd_info *mtd = nand_to_mtd(chip); + + if (WARN_ON(oobavail < 0 || oobavail > mtd->oobsize)) + return -EINVAL; + if (chip->ecc.size && chip->ecc.strength) return nand_check_ecc_cap
[PATCH v4 15/15] mtd: rawnand: provide only single helper function for ECC conf
Function nand_ecc_choose_conf() will be help for all the cases, so other helper functions can be made static. nand_check_ecc_caps(): Invoke nand_ecc_choose_conf() with both chip->ecc.size and chip->ecc.strength value set. nand_maximize_ecc(): Invoke nand_ecc_choose_conf() with NAND_ECC_MAXIMIZE flag. nand_match_ecc_req(): Invoke nand_ecc_choose_conf() with either chip->ecc.size or chip->ecc.strength value set and without NAND_ECC_MAXIMIZE flag. CC: Masahiro Yamada Signed-off-by: Abhishek Sahu --- Changes from v3: NEW PATCH drivers/mtd/nand/raw/nand_base.c | 39 +++ include/linux/mtd/rawnand.h | 9 - 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index c64e3fc..f620236 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -6077,24 +6077,17 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd) * by the controller and the calculated ECC bytes fit within the chip's OOB. * On success, the calculated ECC bytes is set. */ -int nand_check_ecc_caps(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail) +static int +nand_check_ecc_caps(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) { struct mtd_info *mtd = nand_to_mtd(chip); const struct nand_ecc_step_info *stepinfo; int preset_step = chip->ecc.size; int preset_strength = chip->ecc.strength; - int nsteps, ecc_bytes; + int ecc_bytes, nsteps = mtd->writesize / preset_step; int i, j; - if (WARN_ON(oobavail < 0)) - return -EINVAL; - - if (!preset_step || !preset_strength) - return -ENODATA; - - nsteps = mtd->writesize / preset_step; - for (i = 0; i < caps->nstepinfos; i++) { stepinfo = >stepinfos[i]; @@ -6127,7 +6120,6 @@ int nand_check_ecc_caps(struct nand_chip *chip, return -ENOTSUPP; } -EXPORT_SYMBOL_GPL(nand_check_ecc_caps); /** * nand_match_ecc_req - meet the chip's requirement with least ECC bytes @@ -6139,8 +6131,9 @@ int nand_check_ecc_caps(struct nand_chip *chip, * number of ECC bytes (i.e. with the largest number of OOB-free bytes). * On success, the chosen ECC settings are set. */ -int nand_match_ecc_req(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail) +static int +nand_match_ecc_req(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) { struct mtd_info *mtd = nand_to_mtd(chip); const struct nand_ecc_step_info *stepinfo; @@ -6151,9 +6144,6 @@ int nand_match_ecc_req(struct nand_chip *chip, int best_ecc_bytes_total = INT_MAX; int i, j; - if (WARN_ON(oobavail < 0)) - return -EINVAL; - /* No information provided by the NAND chip */ if (!req_step || !req_strength) return -ENOTSUPP; @@ -6212,7 +6202,6 @@ int nand_match_ecc_req(struct nand_chip *chip, return 0; } -EXPORT_SYMBOL_GPL(nand_match_ecc_req); /** * nand_maximize_ecc - choose the max ECC strength available @@ -6223,8 +6212,9 @@ int nand_match_ecc_req(struct nand_chip *chip, * Choose the max ECC strength that is supported on the controller, and can fit * within the chip's OOB. On success, the chosen ECC settings are set. */ -int nand_maximize_ecc(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail) +static int +nand_maximize_ecc(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) { struct mtd_info *mtd = nand_to_mtd(chip); const struct nand_ecc_step_info *stepinfo; @@ -6234,9 +6224,6 @@ int nand_maximize_ecc(struct nand_chip *chip, int best_strength, best_ecc_bytes; int i, j; - if (WARN_ON(oobavail < 0)) - return -EINVAL; - for (i = 0; i < caps->nstepinfos; i++) { stepinfo = >stepinfos[i]; step_size = stepinfo->stepsize; @@ -6285,7 +6272,6 @@ int nand_maximize_ecc(struct nand_chip *chip, return 0; } -EXPORT_SYMBOL_GPL(nand_maximize_ecc); /** * nand_ecc_choose_conf - Set the ECC strength and ECC step size @@ -6307,6 +6293,11 @@ int nand_maximize_ecc(struct nand_chip *chip, int nand_ecc_choose_conf(struct nand_chip *chip, const struct nand_ecc_caps *caps, int oobavail) { + struct mtd_info *mtd = nand_to_mtd(chip); + + if (WARN_ON(oobavail < 0 || oobavail > mtd->oobsize)) + return -EINVAL; + if (chip->ecc.size && chip->ecc.strength) return nand_check_ecc_cap
[PATCH v4 02/15] mtd: rawnand: denali: use helper function for ecc setup
Use the NAND core helper function nand_ecc_choose_conf to tune the ECC parameters instead of the function locally defined. Acked-by: Miquel Raynal Acked-by: Masahiro Yamada Signed-off-by: Abhishek Sahu --- * Changes from v3: NONE * Changes from v2: 1. Changed commit message * Changes from v1: NEW PATCH drivers/mtd/nand/raw/denali.c | 30 ++ 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c index 2a302a1..a586a1d 100644 --- a/drivers/mtd/nand/raw/denali.c +++ b/drivers/mtd/nand/raw/denali.c @@ -1120,33 +1120,6 @@ int denali_calc_ecc_bytes(int step_size, int strength) } EXPORT_SYMBOL(denali_calc_ecc_bytes); -static int denali_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip, - struct denali_nand_info *denali) -{ - int oobavail = mtd->oobsize - denali->oob_skip_bytes; - int ret; - - /* -* If .size and .strength are already set (usually by DT), -* check if they are supported by this controller. -*/ - if (chip->ecc.size && chip->ecc.strength) - return nand_check_ecc_caps(chip, denali->ecc_caps, oobavail); - - /* -* We want .size and .strength closest to the chip's requirement -* unless NAND_ECC_MAXIMIZE is requested. -*/ - if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) { - ret = nand_match_ecc_req(chip, denali->ecc_caps, oobavail); - if (!ret) - return 0; - } - - /* Max ECC strength is the last thing we can do */ - return nand_maximize_ecc(chip, denali->ecc_caps, oobavail); -} - static int denali_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion) { @@ -1317,7 +1290,8 @@ int denali_init(struct denali_nand_info *denali) chip->ecc.mode = NAND_ECC_HW_SYNDROME; chip->options |= NAND_NO_SUBPAGE_WRITE; - ret = denali_ecc_setup(mtd, chip, denali); + ret = nand_ecc_choose_conf(chip, denali->ecc_caps, + mtd->oobsize - denali->oob_skip_bytes); if (ret) { dev_err(denali->dev, "Failed to setup ECC settings.\n"); goto disable_irq; -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 02/15] mtd: rawnand: denali: use helper function for ecc setup
Use the NAND core helper function nand_ecc_choose_conf to tune the ECC parameters instead of the function locally defined. Acked-by: Miquel Raynal Acked-by: Masahiro Yamada Signed-off-by: Abhishek Sahu --- * Changes from v3: NONE * Changes from v2: 1. Changed commit message * Changes from v1: NEW PATCH drivers/mtd/nand/raw/denali.c | 30 ++ 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c index 2a302a1..a586a1d 100644 --- a/drivers/mtd/nand/raw/denali.c +++ b/drivers/mtd/nand/raw/denali.c @@ -1120,33 +1120,6 @@ int denali_calc_ecc_bytes(int step_size, int strength) } EXPORT_SYMBOL(denali_calc_ecc_bytes); -static int denali_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip, - struct denali_nand_info *denali) -{ - int oobavail = mtd->oobsize - denali->oob_skip_bytes; - int ret; - - /* -* If .size and .strength are already set (usually by DT), -* check if they are supported by this controller. -*/ - if (chip->ecc.size && chip->ecc.strength) - return nand_check_ecc_caps(chip, denali->ecc_caps, oobavail); - - /* -* We want .size and .strength closest to the chip's requirement -* unless NAND_ECC_MAXIMIZE is requested. -*/ - if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) { - ret = nand_match_ecc_req(chip, denali->ecc_caps, oobavail); - if (!ret) - return 0; - } - - /* Max ECC strength is the last thing we can do */ - return nand_maximize_ecc(chip, denali->ecc_caps, oobavail); -} - static int denali_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion) { @@ -1317,7 +1290,8 @@ int denali_init(struct denali_nand_info *denali) chip->ecc.mode = NAND_ECC_HW_SYNDROME; chip->options |= NAND_NO_SUBPAGE_WRITE; - ret = denali_ecc_setup(mtd, chip, denali); + ret = nand_ecc_choose_conf(chip, denali->ecc_caps, + mtd->oobsize - denali->oob_skip_bytes); if (ret) { dev_err(denali->dev, "Failed to setup ECC settings.\n"); goto disable_irq; -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 08/15] mtd: rawnand: qcom: fix null pointer access for erased page detection
parse_read_errors can be called with only oob_buf in which case data_buf will be NULL. If data_buf is NULL, then don’t treat this page as completely erased in case of ECC uncorrectable error for RS ECC. For BCH ECC, the controller itself tells regarding erased page in status register. Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: NONE * Changes from v2: NONE * Changes from v1: 1. Added more detail in commit message 2. Added comment before each if/else drivers/mtd/nand/raw/qcom_nandc.c | 18 +++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 0d931d5..a831f9c 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1611,13 +1611,24 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, int ret, ecclen, extraooblen; void *eccbuf; - /* ignore erased codeword errors */ + /* +* For BCH ECC, ignore erased codeword errors, if +* ERASED_CW bits are set. +*/ if (host->bch_enabled) { erased = (erased_cw & ERASED_CW) == ERASED_CW ? true : false; - } else { + /* +* For RS ECC, HW reports the erased CW by placing +* special characters at certain offsets in the buffer. +* These special characters will be valid only if +* complete page is read i.e. data_buf is not NULL. +*/ + } else if (data_buf) { erased = erased_chunk_check_and_fixup(data_buf, data_len); + } else { + erased = false; } if (erased) { @@ -1665,7 +1676,8 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, max_bitflips = max(max_bitflips, stat); } - data_buf += data_len; + if (data_buf) + data_buf += data_len; if (oob_buf) oob_buf += oob_len + ecc->bytes; } -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 08/15] mtd: rawnand: qcom: fix null pointer access for erased page detection
parse_read_errors can be called with only oob_buf in which case data_buf will be NULL. If data_buf is NULL, then don’t treat this page as completely erased in case of ECC uncorrectable error for RS ECC. For BCH ECC, the controller itself tells regarding erased page in status register. Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: NONE * Changes from v2: NONE * Changes from v1: 1. Added more detail in commit message 2. Added comment before each if/else drivers/mtd/nand/raw/qcom_nandc.c | 18 +++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 0d931d5..a831f9c 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1611,13 +1611,24 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, int ret, ecclen, extraooblen; void *eccbuf; - /* ignore erased codeword errors */ + /* +* For BCH ECC, ignore erased codeword errors, if +* ERASED_CW bits are set. +*/ if (host->bch_enabled) { erased = (erased_cw & ERASED_CW) == ERASED_CW ? true : false; - } else { + /* +* For RS ECC, HW reports the erased CW by placing +* special characters at certain offsets in the buffer. +* These special characters will be valid only if +* complete page is read i.e. data_buf is not NULL. +*/ + } else if (data_buf) { erased = erased_chunk_check_and_fixup(data_buf, data_len); + } else { + erased = false; } if (erased) { @@ -1665,7 +1676,8 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, max_bitflips = max(max_bitflips, stat); } - data_buf += data_len; + if (data_buf) + data_buf += data_len; if (oob_buf) oob_buf += oob_len + ecc->bytes; } -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 07/15] mtd: rawnand: qcom: erased page detection for uncorrectable errors only
Following is the flow in the HW if controller tries to read erased page: 1. First ECC uncorrectable error will be generated from ECC engine since ECC engine first calculates the ECC with all 0xff and match the calculated ECC with ECC code in OOB (which is again all 0xff). 2. After getting ECC error, erased CW detection logic will be applied which is different for BCH and RS ECC a. For BCH, HW checks if all the bytes in page are 0xff and then it updates the status in separate register NAND_ERASED_CW_DETECT_STATUS. b. For RS ECC, the HW reports the same error when reading an erased CW, but it notifies that it is an erased CW by placing special characters at certain offsets in the buffer. So the erased CW detect status should be checked only if ECC engine generated the uncorrectable error. Currently for all other operational errors also (like TIMEOUT, MPU errors, etc.), the erased CW detect logic is being applied so fix this and return EIO for other operational errors. Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: NONE * Changes from v2: 1. Changed commit message slightly * Changes from v1: 1. Added more detail in commit message 2. Added comment before each if/else 3. Removed redundant check for BS_UNCORRECTABLE_BIT drivers/mtd/nand/raw/qcom_nandc.c | 65 ++- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index fc20149..0d931d5 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1576,6 +1576,7 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, struct nand_ecc_ctrl *ecc = >ecc; unsigned int max_bitflips = 0; struct read_stats *buf; + bool flash_op_err = false; int i; buf = (struct read_stats *)nandc->reg_read_buf; @@ -1597,8 +1598,18 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, buffer = le32_to_cpu(buf->buffer); erased_cw = le32_to_cpu(buf->erased_cw); - if (flash & (FS_OP_ERR | FS_MPU_ERR)) { + /* +* Check ECC failure for each codeword. ECC failure can +* happen in either of the following conditions +* 1. If number of bitflips are greater than ECC engine +*capability. +* 2. If this codeword contains all 0xff for which erased +*codeword detection check will be done. +*/ + if ((flash & FS_OP_ERR) && (buffer & BS_UNCORRECTABLE_BIT)) { bool erased; + int ret, ecclen, extraooblen; + void *eccbuf; /* ignore erased codeword errors */ if (host->bch_enabled) { @@ -1616,29 +1627,36 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, continue; } - if (buffer & BS_UNCORRECTABLE_BIT) { - int ret, ecclen, extraooblen; - void *eccbuf; + eccbuf = oob_buf ? oob_buf + oob_len : NULL; + ecclen = oob_buf ? host->ecc_bytes_hw : 0; + extraooblen = oob_buf ? oob_len : 0; - eccbuf = oob_buf ? oob_buf + oob_len : NULL; - ecclen = oob_buf ? host->ecc_bytes_hw : 0; - extraooblen = oob_buf ? oob_len : 0; - - /* -* make sure it isn't an erased page reported -* as not-erased by HW because of a few bitflips -*/ - ret = nand_check_erased_ecc_chunk(data_buf, - data_len, eccbuf, ecclen, oob_buf, - extraooblen, ecc->strength); - if (ret < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += ret; - max_bitflips = - max_t(unsigned int, max_bitflips, ret); - } + /* +* make sure it isn't an erased page reported +* as not-erased by HW because of a few bitflips +*/ + ret = nand_check_erased_ecc_chunk(data_buf, + data_len, eccbuf, ecclen, oob_buf, +
[PATCH v4 07/15] mtd: rawnand: qcom: erased page detection for uncorrectable errors only
Following is the flow in the HW if controller tries to read erased page: 1. First ECC uncorrectable error will be generated from ECC engine since ECC engine first calculates the ECC with all 0xff and match the calculated ECC with ECC code in OOB (which is again all 0xff). 2. After getting ECC error, erased CW detection logic will be applied which is different for BCH and RS ECC a. For BCH, HW checks if all the bytes in page are 0xff and then it updates the status in separate register NAND_ERASED_CW_DETECT_STATUS. b. For RS ECC, the HW reports the same error when reading an erased CW, but it notifies that it is an erased CW by placing special characters at certain offsets in the buffer. So the erased CW detect status should be checked only if ECC engine generated the uncorrectable error. Currently for all other operational errors also (like TIMEOUT, MPU errors, etc.), the erased CW detect logic is being applied so fix this and return EIO for other operational errors. Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: NONE * Changes from v2: 1. Changed commit message slightly * Changes from v1: 1. Added more detail in commit message 2. Added comment before each if/else 3. Removed redundant check for BS_UNCORRECTABLE_BIT drivers/mtd/nand/raw/qcom_nandc.c | 65 ++- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index fc20149..0d931d5 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1576,6 +1576,7 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, struct nand_ecc_ctrl *ecc = >ecc; unsigned int max_bitflips = 0; struct read_stats *buf; + bool flash_op_err = false; int i; buf = (struct read_stats *)nandc->reg_read_buf; @@ -1597,8 +1598,18 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, buffer = le32_to_cpu(buf->buffer); erased_cw = le32_to_cpu(buf->erased_cw); - if (flash & (FS_OP_ERR | FS_MPU_ERR)) { + /* +* Check ECC failure for each codeword. ECC failure can +* happen in either of the following conditions +* 1. If number of bitflips are greater than ECC engine +*capability. +* 2. If this codeword contains all 0xff for which erased +*codeword detection check will be done. +*/ + if ((flash & FS_OP_ERR) && (buffer & BS_UNCORRECTABLE_BIT)) { bool erased; + int ret, ecclen, extraooblen; + void *eccbuf; /* ignore erased codeword errors */ if (host->bch_enabled) { @@ -1616,29 +1627,36 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, continue; } - if (buffer & BS_UNCORRECTABLE_BIT) { - int ret, ecclen, extraooblen; - void *eccbuf; + eccbuf = oob_buf ? oob_buf + oob_len : NULL; + ecclen = oob_buf ? host->ecc_bytes_hw : 0; + extraooblen = oob_buf ? oob_len : 0; - eccbuf = oob_buf ? oob_buf + oob_len : NULL; - ecclen = oob_buf ? host->ecc_bytes_hw : 0; - extraooblen = oob_buf ? oob_len : 0; - - /* -* make sure it isn't an erased page reported -* as not-erased by HW because of a few bitflips -*/ - ret = nand_check_erased_ecc_chunk(data_buf, - data_len, eccbuf, ecclen, oob_buf, - extraooblen, ecc->strength); - if (ret < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += ret; - max_bitflips = - max_t(unsigned int, max_bitflips, ret); - } + /* +* make sure it isn't an erased page reported +* as not-erased by HW because of a few bitflips +*/ + ret = nand_check_erased_ecc_chunk(data_buf, + data_len, eccbuf, ecclen, oob_buf, +
[PATCH v4 01/15] mtd: rawnand: helper function for setting up ECC configuration
commit 2c8f8afa7f92 ("mtd: nand: add generic helpers to check, match, maximize ECC settings") provides generic helpers which drivers can use for setting up ECC parameters. Since same board can have different ECC strength nand chips so following is the logic for setting up ECC strength and ECC step size, which can be used by most of the drivers. 1. If both ECC step size and ECC strength are already set (usually by DT) then just check whether this setting is supported by NAND controller. 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength supported by NAND controller. 3. Otherwise, try to match the ECC step size and ECC strength closest to the chip's requirement. If available OOB size can't fit the chip requirement then select maximum ECC strength which can be fit with available OOB size. This patch introduces nand_ecc_choose_conf function which calls the required helper functions for the above logic. The drivers can use this single function instead of calling the 3 helper functions individually. CC: Masahiro Yamada Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. call nand_maximize_ecc() 2 times to make code more clear. * Changes from v2: 1. Renamed function to nand_ecc_choose_conf. 2. Minor code reorganization to remove warning and 2 function calls for nand_maximize_ecc. * Changes from v1: NEW PATCH drivers/mtd/nand/raw/nand_base.c | 33 + include/linux/mtd/rawnand.h | 3 +++ 2 files changed, 36 insertions(+) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 10c4f991..c64e3fc 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -6287,6 +6287,39 @@ int nand_maximize_ecc(struct nand_chip *chip, } EXPORT_SYMBOL_GPL(nand_maximize_ecc); +/** + * nand_ecc_choose_conf - Set the ECC strength and ECC step size + * @chip: nand chip info structure + * @caps: ECC engine caps info structure + * @oobavail: OOB size that the ECC engine can use + * + * Choose the ECC configuration according to following logic + * + * 1. If both ECC step size and ECC strength are already set (usually by DT) + *then check if it is supported by this controller. + * 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength. + * 3. Otherwise, try to match the ECC step size and ECC strength closest + *to the chip's requirement. If available OOB size can't fit the chip + *requirement then fallback to the maximum ECC step size and ECC strength. + * + * On success, the chosen ECC settings are set. + */ +int nand_ecc_choose_conf(struct nand_chip *chip, +const struct nand_ecc_caps *caps, int oobavail) +{ + if (chip->ecc.size && chip->ecc.strength) + return nand_check_ecc_caps(chip, caps, oobavail); + + if (chip->ecc.options & NAND_ECC_MAXIMIZE) + return nand_maximize_ecc(chip, caps, oobavail); + + if (!nand_match_ecc_req(chip, caps, oobavail)) + return 0; + + return nand_maximize_ecc(chip, caps, oobavail); +} +EXPORT_SYMBOL_GPL(nand_ecc_choose_conf); + /* * Check if the chip configuration meet the datasheet requirements. diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 3e8ec3b..03a0061 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1650,6 +1650,9 @@ int nand_match_ecc_req(struct nand_chip *chip, int nand_maximize_ecc(struct nand_chip *chip, const struct nand_ecc_caps *caps, int oobavail); +int nand_ecc_choose_conf(struct nand_chip *chip, +const struct nand_ecc_caps *caps, int oobavail); + /* Default write_oob implementation */ int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page); -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 01/15] mtd: rawnand: helper function for setting up ECC configuration
commit 2c8f8afa7f92 ("mtd: nand: add generic helpers to check, match, maximize ECC settings") provides generic helpers which drivers can use for setting up ECC parameters. Since same board can have different ECC strength nand chips so following is the logic for setting up ECC strength and ECC step size, which can be used by most of the drivers. 1. If both ECC step size and ECC strength are already set (usually by DT) then just check whether this setting is supported by NAND controller. 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength supported by NAND controller. 3. Otherwise, try to match the ECC step size and ECC strength closest to the chip's requirement. If available OOB size can't fit the chip requirement then select maximum ECC strength which can be fit with available OOB size. This patch introduces nand_ecc_choose_conf function which calls the required helper functions for the above logic. The drivers can use this single function instead of calling the 3 helper functions individually. CC: Masahiro Yamada Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. call nand_maximize_ecc() 2 times to make code more clear. * Changes from v2: 1. Renamed function to nand_ecc_choose_conf. 2. Minor code reorganization to remove warning and 2 function calls for nand_maximize_ecc. * Changes from v1: NEW PATCH drivers/mtd/nand/raw/nand_base.c | 33 + include/linux/mtd/rawnand.h | 3 +++ 2 files changed, 36 insertions(+) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 10c4f991..c64e3fc 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -6287,6 +6287,39 @@ int nand_maximize_ecc(struct nand_chip *chip, } EXPORT_SYMBOL_GPL(nand_maximize_ecc); +/** + * nand_ecc_choose_conf - Set the ECC strength and ECC step size + * @chip: nand chip info structure + * @caps: ECC engine caps info structure + * @oobavail: OOB size that the ECC engine can use + * + * Choose the ECC configuration according to following logic + * + * 1. If both ECC step size and ECC strength are already set (usually by DT) + *then check if it is supported by this controller. + * 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength. + * 3. Otherwise, try to match the ECC step size and ECC strength closest + *to the chip's requirement. If available OOB size can't fit the chip + *requirement then fallback to the maximum ECC step size and ECC strength. + * + * On success, the chosen ECC settings are set. + */ +int nand_ecc_choose_conf(struct nand_chip *chip, +const struct nand_ecc_caps *caps, int oobavail) +{ + if (chip->ecc.size && chip->ecc.strength) + return nand_check_ecc_caps(chip, caps, oobavail); + + if (chip->ecc.options & NAND_ECC_MAXIMIZE) + return nand_maximize_ecc(chip, caps, oobavail); + + if (!nand_match_ecc_req(chip, caps, oobavail)) + return 0; + + return nand_maximize_ecc(chip, caps, oobavail); +} +EXPORT_SYMBOL_GPL(nand_ecc_choose_conf); + /* * Check if the chip configuration meet the datasheet requirements. diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 3e8ec3b..03a0061 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1650,6 +1650,9 @@ int nand_match_ecc_req(struct nand_chip *chip, int nand_maximize_ecc(struct nand_chip *chip, const struct nand_ecc_caps *caps, int oobavail); +int nand_ecc_choose_conf(struct nand_chip *chip, +const struct nand_ecc_caps *caps, int oobavail); + /* Default write_oob implementation */ int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page); -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 09/15] mtd: rawnand: qcom: parse read errors for read oob also
read_page and read_oob both calls the read_page_ecc function. The QCOM NAND controller protect the OOB available bytes with ECC so read errors should be checked for read_oob also. This patch moves the error checking code inside read_page_ecc so caller does not have to check explicitly for read errors. Reviewed-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: NONE * Changes from v2: NONE * Changes from v1: 1. Minor code change for return early in case of error drivers/mtd/nand/raw/qcom_nandc.c | 26 +- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index a831f9c..285b2ad3 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1698,6 +1698,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, struct nand_chip *chip = >chip; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct nand_ecc_ctrl *ecc = >ecc; + u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; int i, ret; config_nand_page_read(nandc); @@ -1758,12 +1759,14 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, } ret = submit_descs(nandc); - if (ret) + free_descs(nandc); + + if (ret) { dev_err(nandc->dev, "failure to read page/oob\n"); + return ret; + } - free_descs(nandc); - - return ret; + return parse_read_errors(host, data_buf_start, oob_buf_start); } /* @@ -1808,20 +1811,14 @@ static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, struct qcom_nand_host *host = to_qcom_nand_host(chip); struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); u8 *data_buf, *oob_buf = NULL; - int ret; nand_read_page_op(chip, page, 0, NULL, 0); data_buf = buf; oob_buf = oob_required ? chip->oob_poi : NULL; clear_bam_transaction(nandc); - ret = read_page_ecc(host, data_buf, oob_buf); - if (ret) { - dev_err(nandc->dev, "failure to read page\n"); - return ret; - } - return parse_read_errors(host, data_buf, oob_buf); + return read_page_ecc(host, data_buf, oob_buf); } /* implements ecc->read_page_raw() */ @@ -1911,7 +1908,6 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, struct qcom_nand_host *host = to_qcom_nand_host(chip); struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct nand_ecc_ctrl *ecc = >ecc; - int ret; clear_read_regs(nandc); clear_bam_transaction(nandc); @@ -1920,11 +1916,7 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, set_address(host, 0, page); update_rw_regs(host, ecc->steps, true); - ret = read_page_ecc(host, NULL, chip->oob_poi); - if (ret) - dev_err(nandc->dev, "failure to read oob\n"); - - return ret; + return read_page_ecc(host, NULL, chip->oob_poi); } /* implements ecc->write_page() */ -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 09/15] mtd: rawnand: qcom: parse read errors for read oob also
read_page and read_oob both calls the read_page_ecc function. The QCOM NAND controller protect the OOB available bytes with ECC so read errors should be checked for read_oob also. This patch moves the error checking code inside read_page_ecc so caller does not have to check explicitly for read errors. Reviewed-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: NONE * Changes from v2: NONE * Changes from v1: 1. Minor code change for return early in case of error drivers/mtd/nand/raw/qcom_nandc.c | 26 +- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index a831f9c..285b2ad3 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1698,6 +1698,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, struct nand_chip *chip = >chip; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct nand_ecc_ctrl *ecc = >ecc; + u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; int i, ret; config_nand_page_read(nandc); @@ -1758,12 +1759,14 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, } ret = submit_descs(nandc); - if (ret) + free_descs(nandc); + + if (ret) { dev_err(nandc->dev, "failure to read page/oob\n"); + return ret; + } - free_descs(nandc); - - return ret; + return parse_read_errors(host, data_buf_start, oob_buf_start); } /* @@ -1808,20 +1811,14 @@ static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, struct qcom_nand_host *host = to_qcom_nand_host(chip); struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); u8 *data_buf, *oob_buf = NULL; - int ret; nand_read_page_op(chip, page, 0, NULL, 0); data_buf = buf; oob_buf = oob_required ? chip->oob_poi : NULL; clear_bam_transaction(nandc); - ret = read_page_ecc(host, data_buf, oob_buf); - if (ret) { - dev_err(nandc->dev, "failure to read page\n"); - return ret; - } - return parse_read_errors(host, data_buf, oob_buf); + return read_page_ecc(host, data_buf, oob_buf); } /* implements ecc->read_page_raw() */ @@ -1911,7 +1908,6 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, struct qcom_nand_host *host = to_qcom_nand_host(chip); struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct nand_ecc_ctrl *ecc = >ecc; - int ret; clear_read_regs(nandc); clear_bam_transaction(nandc); @@ -1920,11 +1916,7 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, set_address(host, 0, page); update_rw_regs(host, ecc->steps, true); - ret = read_page_ecc(host, NULL, chip->oob_poi); - if (ret) - dev_err(nandc->dev, "failure to read oob\n"); - - return ret; + return read_page_ecc(host, NULL, chip->oob_poi); } /* implements ecc->write_page() */ -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 12/15] mtd: rawnand: qcom: check for operation errors in case of raw read
Currently there is no error checking for raw read. For raw reads, there won’t be any ECC failure but the operational failures are possible, so schedule the NAND_FLASH_STATUS read after each codeword. Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. Since bad block reorganization patch has removed from this patch series so following change is required in copy_last_cw) config_nand_single_cw_page_read(nandc); -> config_nand_single_cw_page_read(nandc, host->use_ecc); * Changes from v2: NONE * Changes from v1: 1. Removed the code for copy_last_cw. drivers/mtd/nand/raw/qcom_nandc.c | 58 +++ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 887b1f6..5999c39 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1096,7 +1096,8 @@ static void config_nand_page_read(struct qcom_nand_controller *nandc) * Helper to prepare DMA descriptors for configuring registers * before reading each codeword in NAND page. */ -static void config_nand_cw_read(struct qcom_nand_controller *nandc) +static void +config_nand_cw_read(struct qcom_nand_controller *nandc, bool use_ecc) { if (nandc->props->is_bam) write_reg_dma(nandc, NAND_READ_LOCATION_0, 4, @@ -1105,19 +1106,25 @@ static void config_nand_cw_read(struct qcom_nand_controller *nandc) write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); - read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, -NAND_BAM_NEXT_SGL); + if (use_ecc) { + read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); + read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, +NAND_BAM_NEXT_SGL); + } else { + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + } } /* * Helper to prepare dma descriptors to configure registers needed for reading a * single codeword in page */ -static void config_nand_single_cw_page_read(struct qcom_nand_controller *nandc) +static void +config_nand_single_cw_page_read(struct qcom_nand_controller *nandc, + bool use_ecc) { config_nand_page_read(nandc); - config_nand_cw_read(nandc); + config_nand_cw_read(nandc, use_ecc); } /* @@ -1198,7 +1205,7 @@ static int nandc_param(struct qcom_nand_host *host) nandc->buf_count = 512; memset(nandc->data_buffer, 0xff, nandc->buf_count); - config_nand_single_cw_page_read(nandc); + config_nand_single_cw_page_read(nandc, false); read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, nandc->buf_count, 0); @@ -1563,6 +1570,23 @@ struct read_stats { __le32 erased_cw; }; +/* reads back FLASH_STATUS register set by the controller */ +static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) +{ + struct nand_chip *chip = >chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + int i; + + for (i = 0; i < cw_cnt; i++) { + u32 flash = le32_to_cpu(nandc->reg_read_buf[i]); + + if (flash & (FS_OP_ERR | FS_MPU_ERR)) + return -EIO; + } + + return 0; +} + /* * reads back status registers set by the controller to notify page read * errors. this is equivalent to what 'ecc->correct()' would do. @@ -1729,7 +1753,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, } } - config_nand_cw_read(nandc); + config_nand_cw_read(nandc, true); if (data_buf) read_data_dma(nandc, FLASH_BUF_ACC, data_buf, @@ -1791,7 +1815,7 @@ static int copy_last_cw(struct qcom_nand_host *host, int page) set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, true); - config_nand_single_cw_page_read(nandc); + config_nand_single_cw_page_read(nandc, host->use_ecc); read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); @@ -1874,7 +1898,7 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1); } - config_nand_cw_read(nandc); + config_nand_cw_read(nandc, false); read_data_dma(nandc, reg_off, data_buf, data_size1, 0); reg_off += data_size1; @@ -1893,12 +1917,13 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, } ret = submit_descs(nandc); - if (ret) + free_descs(nandc); + if (ret)
[PATCH v4 12/15] mtd: rawnand: qcom: check for operation errors in case of raw read
Currently there is no error checking for raw read. For raw reads, there won’t be any ECC failure but the operational failures are possible, so schedule the NAND_FLASH_STATUS read after each codeword. Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. Since bad block reorganization patch has removed from this patch series so following change is required in copy_last_cw) config_nand_single_cw_page_read(nandc); -> config_nand_single_cw_page_read(nandc, host->use_ecc); * Changes from v2: NONE * Changes from v1: 1. Removed the code for copy_last_cw. drivers/mtd/nand/raw/qcom_nandc.c | 58 +++ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 887b1f6..5999c39 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1096,7 +1096,8 @@ static void config_nand_page_read(struct qcom_nand_controller *nandc) * Helper to prepare DMA descriptors for configuring registers * before reading each codeword in NAND page. */ -static void config_nand_cw_read(struct qcom_nand_controller *nandc) +static void +config_nand_cw_read(struct qcom_nand_controller *nandc, bool use_ecc) { if (nandc->props->is_bam) write_reg_dma(nandc, NAND_READ_LOCATION_0, 4, @@ -1105,19 +1106,25 @@ static void config_nand_cw_read(struct qcom_nand_controller *nandc) write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); - read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, -NAND_BAM_NEXT_SGL); + if (use_ecc) { + read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); + read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, +NAND_BAM_NEXT_SGL); + } else { + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + } } /* * Helper to prepare dma descriptors to configure registers needed for reading a * single codeword in page */ -static void config_nand_single_cw_page_read(struct qcom_nand_controller *nandc) +static void +config_nand_single_cw_page_read(struct qcom_nand_controller *nandc, + bool use_ecc) { config_nand_page_read(nandc); - config_nand_cw_read(nandc); + config_nand_cw_read(nandc, use_ecc); } /* @@ -1198,7 +1205,7 @@ static int nandc_param(struct qcom_nand_host *host) nandc->buf_count = 512; memset(nandc->data_buffer, 0xff, nandc->buf_count); - config_nand_single_cw_page_read(nandc); + config_nand_single_cw_page_read(nandc, false); read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, nandc->buf_count, 0); @@ -1563,6 +1570,23 @@ struct read_stats { __le32 erased_cw; }; +/* reads back FLASH_STATUS register set by the controller */ +static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) +{ + struct nand_chip *chip = >chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + int i; + + for (i = 0; i < cw_cnt; i++) { + u32 flash = le32_to_cpu(nandc->reg_read_buf[i]); + + if (flash & (FS_OP_ERR | FS_MPU_ERR)) + return -EIO; + } + + return 0; +} + /* * reads back status registers set by the controller to notify page read * errors. this is equivalent to what 'ecc->correct()' would do. @@ -1729,7 +1753,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, } } - config_nand_cw_read(nandc); + config_nand_cw_read(nandc, true); if (data_buf) read_data_dma(nandc, FLASH_BUF_ACC, data_buf, @@ -1791,7 +1815,7 @@ static int copy_last_cw(struct qcom_nand_host *host, int page) set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, true); - config_nand_single_cw_page_read(nandc); + config_nand_single_cw_page_read(nandc, host->use_ecc); read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); @@ -1874,7 +1898,7 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1); } - config_nand_cw_read(nandc); + config_nand_cw_read(nandc, false); read_data_dma(nandc, reg_off, data_buf, data_size1, 0); reg_off += data_size1; @@ -1893,12 +1917,13 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, } ret = submit_descs(nandc); - if (ret) + free_descs(nandc); + if (ret)
[PATCH v4 11/15] mtd: rawnand: qcom: fix return value for raw page read
Fix value returned by ->read_page_raw() to be the actual operation status, instead of always 0. Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: NONE * Changes from v2: 1. Changed commit message * Changes from v1: NEW CHANGE drivers/mtd/nand/raw/qcom_nandc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 28361b5..887b1f6 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1898,7 +1898,7 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, free_descs(nandc); - return 0; + return ret; } /* implements ecc->read_oob() */ -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 11/15] mtd: rawnand: qcom: fix return value for raw page read
Fix value returned by ->read_page_raw() to be the actual operation status, instead of always 0. Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: NONE * Changes from v2: 1. Changed commit message * Changes from v1: NEW CHANGE drivers/mtd/nand/raw/qcom_nandc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 28361b5..887b1f6 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1898,7 +1898,7 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, free_descs(nandc); - return 0; + return ret; } /* implements ecc->read_oob() */ -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 04/15] mtd: rawnand: qcom: remove dt property nand-ecc-step-size
QCOM NAND controller supports only one step size (512) so nand-ecc-step-size DT property is redundant. This property can be removed and ecc step size can be assigned with 512 value. Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. Minor change in comment (s/512 bytes of data in each step/512 bytes data steps) * Changes from v2: NEW CHANGE 1. Removed the custom logic and used the helper fuction. drivers/mtd/nand/raw/qcom_nandc.c | 11 ++- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 6a5519f..bf80a61 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2325,15 +2325,8 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) bool wide_bus; int ecc_mode = 1; - /* -* the controller requires each step consists of 512 bytes of data. -* bail out if DT has populated a wrong step size. -*/ - if (ecc->size != NANDC_STEP_SIZE) { - dev_err(nandc->dev, "invalid ecc size\n"); - return -EINVAL; - } - + /* controller only supports 512 bytes data steps */ + ecc->size = NANDC_STEP_SIZE; wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; if (ecc->strength >= 8) { -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 04/15] mtd: rawnand: qcom: remove dt property nand-ecc-step-size
QCOM NAND controller supports only one step size (512) so nand-ecc-step-size DT property is redundant. This property can be removed and ecc step size can be assigned with 512 value. Acked-by: Miquel Raynal Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. Minor change in comment (s/512 bytes of data in each step/512 bytes data steps) * Changes from v2: NEW CHANGE 1. Removed the custom logic and used the helper fuction. drivers/mtd/nand/raw/qcom_nandc.c | 11 ++- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 6a5519f..bf80a61 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2325,15 +2325,8 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) bool wide_bus; int ecc_mode = 1; - /* -* the controller requires each step consists of 512 bytes of data. -* bail out if DT has populated a wrong step size. -*/ - if (ecc->size != NANDC_STEP_SIZE) { - dev_err(nandc->dev, "invalid ecc size\n"); - return -EINVAL; - } - + /* controller only supports 512 bytes data steps */ + ecc->size = NANDC_STEP_SIZE; wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; if (ecc->strength >= 8) { -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH v4 14/15] mtd: rawnand: qcom: erased page bitflips detection
NAND parts can have bitflips in an erased page due to the process technology used. In this case, QCOM NAND controller is not able to identify that page as an erased page. Currently the driver calls nand_check_erased_ecc_chunk() for identifying the erased pages but this won’t work always since the checking is being with ECC engine returned data. In case of bitflips, the ECC engine tries to correct the data and then it generates the uncorrectable error. Now, this data is not equal to original raw data. For erased CW identification, the raw data should be read again from NAND device and this nand_check_erased_ecc_chunk function() should be called for raw data only. Now following logic is being added to identify the erased codeword bitflips. 1. In most of the cases, not all the codewords will have bitflips and only single CW will have bitflips. So, there is no need to read the complete raw page data. The NAND raw read can be scheduled for any CW in page. The NAND controller works on CW basis and it will update the status register after each CW read. Maintain the bitmask for the CW which generated the uncorrectable error. 2. Do raw read for all the CW's which generated the uncorrectable error. 3. Both DATA and OOB need to be checked for number of 0. The top-level API can be called with only data buf or OOB buf so use chip->databuf if data buf is null and chip->oob_poi if OOB buf is null for copying the raw bytes temporarily. 4. For each CW, check the number of 0 in cw_data and usable oob bytes, The bbm and spare (unused) bytes bit flip won’t affect the ECC so don’t check the number of bitflips in this area. Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. Major changes in erased codeword detection for raw read function * Changes from v2: NONE * Changes from v1: 1. Minor change in commit message 2. invalidate pagebuf if databuf or oob_poi is used drivers/mtd/nand/raw/qcom_nandc.c | 127 +++--- 1 file changed, 90 insertions(+), 37 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 160acdf..e34edf1 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1656,20 +1656,95 @@ static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) } /* + * Bitflips can happen in erased codewords also so this function counts the + * number of 0 in each CW for which ECC engine returns the uncorrectable + * error. The page will be assumed as erased if this count is less than or + * equal to the ecc->strength for each CW. + * + * 1. Both DATA and OOB need to be checked for number of 0. The + *top-level API can be called with only data buf or OOB buf so use + *chip->data_buf if data buf is null and chip->oob_poi if oob buf + *is null for copying the raw bytes. + * 2. Perform raw read for all the CW which has uncorrectable errors. + * 3. For each CW, check the number of 0 in cw_data and usable OOB bytes. + *The BBM and spare bytes bit flip won’t affect the ECC so don’t check + *the number of bitflips in this area. + */ +static int +check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf, + u8 *oob_buf, unsigned long uncorrectable_cws, + int page, unsigned int max_bitflips) +{ + struct nand_chip *chip = >chip; + struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_ecc_ctrl *ecc = >ecc; + int cw, data_size, oob_size, ret = 0; + + if (!data_buf) { + data_buf = chip->data_buf; + chip->pagebuf = -1; + } + + if (!oob_buf) { + oob_buf = chip->oob_poi; + chip->pagebuf = -1; + } + + for (cw = 0; cw < ecc->steps && uncorrectable_cws; cw++) { + if (cw == (ecc->steps - 1)) { + data_size = ecc->size - ((ecc->steps - 1) * 4); + oob_size = (ecc->steps * 4) + host->ecc_bytes_hw; + } else { + data_size = host->cw_data; + oob_size = host->ecc_bytes_hw; + } + + if (uncorrectable_cws & BIT(0)) { + ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, +oob_buf, page, cw); + if (ret) + return ret; + /* +* make sure it isn't an erased page reported +* as not-erased by HW because of a few bitflips +*/ + ret = nand_check_erased_ecc_chunk(data_buf, + data_size, oob_buf + host->bbm_size, + oob_size, NULL, + 0, ecc->strength); +
[PATCH v4 14/15] mtd: rawnand: qcom: erased page bitflips detection
NAND parts can have bitflips in an erased page due to the process technology used. In this case, QCOM NAND controller is not able to identify that page as an erased page. Currently the driver calls nand_check_erased_ecc_chunk() for identifying the erased pages but this won’t work always since the checking is being with ECC engine returned data. In case of bitflips, the ECC engine tries to correct the data and then it generates the uncorrectable error. Now, this data is not equal to original raw data. For erased CW identification, the raw data should be read again from NAND device and this nand_check_erased_ecc_chunk function() should be called for raw data only. Now following logic is being added to identify the erased codeword bitflips. 1. In most of the cases, not all the codewords will have bitflips and only single CW will have bitflips. So, there is no need to read the complete raw page data. The NAND raw read can be scheduled for any CW in page. The NAND controller works on CW basis and it will update the status register after each CW read. Maintain the bitmask for the CW which generated the uncorrectable error. 2. Do raw read for all the CW's which generated the uncorrectable error. 3. Both DATA and OOB need to be checked for number of 0. The top-level API can be called with only data buf or OOB buf so use chip->databuf if data buf is null and chip->oob_poi if OOB buf is null for copying the raw bytes temporarily. 4. For each CW, check the number of 0 in cw_data and usable oob bytes, The bbm and spare (unused) bytes bit flip won’t affect the ECC so don’t check the number of bitflips in this area. Signed-off-by: Abhishek Sahu --- * Changes from v3: 1. Major changes in erased codeword detection for raw read function * Changes from v2: NONE * Changes from v1: 1. Minor change in commit message 2. invalidate pagebuf if databuf or oob_poi is used drivers/mtd/nand/raw/qcom_nandc.c | 127 +++--- 1 file changed, 90 insertions(+), 37 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 160acdf..e34edf1 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1656,20 +1656,95 @@ static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) } /* + * Bitflips can happen in erased codewords also so this function counts the + * number of 0 in each CW for which ECC engine returns the uncorrectable + * error. The page will be assumed as erased if this count is less than or + * equal to the ecc->strength for each CW. + * + * 1. Both DATA and OOB need to be checked for number of 0. The + *top-level API can be called with only data buf or OOB buf so use + *chip->data_buf if data buf is null and chip->oob_poi if oob buf + *is null for copying the raw bytes. + * 2. Perform raw read for all the CW which has uncorrectable errors. + * 3. For each CW, check the number of 0 in cw_data and usable OOB bytes. + *The BBM and spare bytes bit flip won’t affect the ECC so don’t check + *the number of bitflips in this area. + */ +static int +check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf, + u8 *oob_buf, unsigned long uncorrectable_cws, + int page, unsigned int max_bitflips) +{ + struct nand_chip *chip = >chip; + struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_ecc_ctrl *ecc = >ecc; + int cw, data_size, oob_size, ret = 0; + + if (!data_buf) { + data_buf = chip->data_buf; + chip->pagebuf = -1; + } + + if (!oob_buf) { + oob_buf = chip->oob_poi; + chip->pagebuf = -1; + } + + for (cw = 0; cw < ecc->steps && uncorrectable_cws; cw++) { + if (cw == (ecc->steps - 1)) { + data_size = ecc->size - ((ecc->steps - 1) * 4); + oob_size = (ecc->steps * 4) + host->ecc_bytes_hw; + } else { + data_size = host->cw_data; + oob_size = host->ecc_bytes_hw; + } + + if (uncorrectable_cws & BIT(0)) { + ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, +oob_buf, page, cw); + if (ret) + return ret; + /* +* make sure it isn't an erased page reported +* as not-erased by HW because of a few bitflips +*/ + ret = nand_check_erased_ecc_chunk(data_buf, + data_size, oob_buf + host->bbm_size, + oob_size, NULL, + 0, ecc->strength); +
[PATCH] mtd: rawnand: fix return value check for bad block status
Positive return value from read_oob() is making false BAD blocks. For some of the NAND controllers, OOB bytes will be protected with ECC and read_oob() will return number of bitflips. If there is any bitflip in ECC protected OOB bytes for BAD block status page, then that block is getting treated as BAD. Fixes: c120e75e0e7d ("mtd: nand: use read_oob() instead of cmdfunc() for bad block check") Cc: Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/raw/nand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index f28c3a5..4a73f73 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -440,7 +440,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs) for (; page < page_end; page++) { res = chip->ecc.read_oob(mtd, chip, page); - if (res) + if (res < 0) return res; bad = chip->oob_poi[chip->badblockpos]; -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
[PATCH] mtd: rawnand: fix return value check for bad block status
Positive return value from read_oob() is making false BAD blocks. For some of the NAND controllers, OOB bytes will be protected with ECC and read_oob() will return number of bitflips. If there is any bitflip in ECC protected OOB bytes for BAD block status page, then that block is getting treated as BAD. Fixes: c120e75e0e7d ("mtd: nand: use read_oob() instead of cmdfunc() for bad block check") Cc: Signed-off-by: Abhishek Sahu --- drivers/mtd/nand/raw/nand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index f28c3a5..4a73f73 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -440,7 +440,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs) for (; page < page_end; page++) { res = chip->ecc.read_oob(mtd, chip, page); - if (res) + if (res < 0) return res; bad = chip->oob_poi[chip->badblockpos]; -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH v3 13/16] mtd: rawnand: qcom: minor code reorganization for bad block check
On 2018-06-07 18:23, Miquel Raynal wrote: Hi Abhishek, On Mon, 28 May 2018 15:40:52 +0530, Abhishek Sahu wrote: On 2018-05-28 12:33, Miquel Raynal wrote: > Hi Abhishek, > >> >> /* implements ecc->read_page() */ >> >> static int qcom_nandc_read_page(struct mtd_info *mtd, struct >> nand_chip *chip, >> >> uint8_t *buf, int oob_required, int page) >> >> @@ -2118,6 +2083,7 @@ static int qcom_nandc_block_bad(struct mtd_info >> *mtd, loff_t ofs) >> >> struct nand_ecc_ctrl *ecc = >ecc; >> >> int page, ret, bbpos, bad = 0; >> >> u32 flash_status; >> >> + u8 *bbm_bytes_buf = chip->data_buf; >> >> >> page = (int)(ofs >> chip->page_shift) & chip->pagemask; >> >> >> @@ -2128,11 +2094,31 @@ static int qcom_nandc_block_bad(struct >> mtd_info *mtd, loff_t ofs) >> >>* that contains the BBM >> >>*/ >> >> host->use_ecc = false; >> >> + bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); >> > > Are we sure there is no layout with only 1 step? >> >> All the layouts are such that, the BBM will come in >> first byte of spare area. >> >> For 4 bit ECC, the cw_size is 528 so for 2K page >> >>2048 - 528 * 3 = 464 > > My question was more about small page NANDs. But I suppose it works > too if ecc->steps == 1. > Correct Miquel. >> >> So for last CW, the 464 is BBM (i.e 2048th byte) in >> full page. >> >> > >> >>clear_bam_transaction(nandc); >> >> - ret = copy_last_cw(host, page); >> >> - if (ret) >> >> + clear_read_regs(nandc); >> >> + >> >> + set_address(host, host->cw_size * (ecc->steps - 1), page); >> >> + update_rw_regs(host, 1, true); >> >> + >> >> + /* >> >> + * The last codeword data will be copied from NAND device to NAND >> >> + * controller internal HW buffer. Copy only required BBM size bytes >> >> + * from this HW buffer to bbm_bytes_buf which is present at >> >> + * bbpos offset. >> >> + */ >> >> + nandc_set_read_loc(nandc, 0, bbpos, host->bbm_size, 1); >> >> + config_nand_single_cw_page_read(nandc); >> >> + read_data_dma(nandc, FLASH_BUF_ACC + bbpos, bbm_bytes_buf, >> >> + host->bbm_size, 0); >> >> + >> >> + ret = submit_descs(nandc); >> >> + free_descs(nandc); >> >> + if (ret) { >> >> + dev_err(nandc->dev, "failed to copy bad block bytes\n"); >> >> goto err; >> >> + } >> >> >> flash_status = le32_to_cpu(nandc->reg_read_buf[0]); >> >> >> @@ -2141,12 +2127,10 @@ static int qcom_nandc_block_bad(struct >> mtd_info *mtd, loff_t ofs) >> >> goto err; >> >> } >> >> >> -bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); >> >> - >> >> - bad = nandc->data_buffer[bbpos] != 0xff; >> >> + bad = bbm_bytes_buf[0] != 0xff; >> > > This is suspect as it still points to the beginning of the data buffer. >> > Can you please check you did not meant bbm_bytes_buf[bbpos]? >> > >> The main thing here is >> nandc_set_read_loc(nandc, 0, bbpos, host->bbm_size, 1); >> >> After reading one complete CW from NAND, the data will be still >> in NAND HW buffer. >> >> The above register tells that we need to read data from >> bbpos of size host->bbm_size (which is 1 byte for 8 bus witdh >> and 2 byte for 16 bus width) in bbm_bytes_buf. > > I see: idx 0 in bbm_bytes_buf is the data at offset bbpos. Then > it's ok. > >> >> So bbm_bytes_buf[0] will contain the BBM first byte. >> and bbm_bytes_buf[1] will contain the BBM second byte. >> >> Regards, >> Abhishek >> >> >> >> if (chip->options & NAND_BUSWIDTH_16) >> >> - bad = bad || (nandc->data_buffer[bbpos + 1] != 0xff); >> >> + bad = bad || (bbm_bytes_buf[1] != 0xff); > > Sorry, my mistake, I did not see the above line. > > However, technically, the BBM could be located in the first, second or > last page of the block. You should check the three of them are 0xFF > bef
Re: [PATCH v3 13/16] mtd: rawnand: qcom: minor code reorganization for bad block check
On 2018-06-07 18:23, Miquel Raynal wrote: Hi Abhishek, On Mon, 28 May 2018 15:40:52 +0530, Abhishek Sahu wrote: On 2018-05-28 12:33, Miquel Raynal wrote: > Hi Abhishek, > >> >> /* implements ecc->read_page() */ >> >> static int qcom_nandc_read_page(struct mtd_info *mtd, struct >> nand_chip *chip, >> >> uint8_t *buf, int oob_required, int page) >> >> @@ -2118,6 +2083,7 @@ static int qcom_nandc_block_bad(struct mtd_info >> *mtd, loff_t ofs) >> >> struct nand_ecc_ctrl *ecc = >ecc; >> >> int page, ret, bbpos, bad = 0; >> >> u32 flash_status; >> >> + u8 *bbm_bytes_buf = chip->data_buf; >> >> >> page = (int)(ofs >> chip->page_shift) & chip->pagemask; >> >> >> @@ -2128,11 +2094,31 @@ static int qcom_nandc_block_bad(struct >> mtd_info *mtd, loff_t ofs) >> >>* that contains the BBM >> >>*/ >> >> host->use_ecc = false; >> >> + bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); >> > > Are we sure there is no layout with only 1 step? >> >> All the layouts are such that, the BBM will come in >> first byte of spare area. >> >> For 4 bit ECC, the cw_size is 528 so for 2K page >> >>2048 - 528 * 3 = 464 > > My question was more about small page NANDs. But I suppose it works > too if ecc->steps == 1. > Correct Miquel. >> >> So for last CW, the 464 is BBM (i.e 2048th byte) in >> full page. >> >> > >> >>clear_bam_transaction(nandc); >> >> - ret = copy_last_cw(host, page); >> >> - if (ret) >> >> + clear_read_regs(nandc); >> >> + >> >> + set_address(host, host->cw_size * (ecc->steps - 1), page); >> >> + update_rw_regs(host, 1, true); >> >> + >> >> + /* >> >> + * The last codeword data will be copied from NAND device to NAND >> >> + * controller internal HW buffer. Copy only required BBM size bytes >> >> + * from this HW buffer to bbm_bytes_buf which is present at >> >> + * bbpos offset. >> >> + */ >> >> + nandc_set_read_loc(nandc, 0, bbpos, host->bbm_size, 1); >> >> + config_nand_single_cw_page_read(nandc); >> >> + read_data_dma(nandc, FLASH_BUF_ACC + bbpos, bbm_bytes_buf, >> >> + host->bbm_size, 0); >> >> + >> >> + ret = submit_descs(nandc); >> >> + free_descs(nandc); >> >> + if (ret) { >> >> + dev_err(nandc->dev, "failed to copy bad block bytes\n"); >> >> goto err; >> >> + } >> >> >> flash_status = le32_to_cpu(nandc->reg_read_buf[0]); >> >> >> @@ -2141,12 +2127,10 @@ static int qcom_nandc_block_bad(struct >> mtd_info *mtd, loff_t ofs) >> >> goto err; >> >> } >> >> >> -bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); >> >> - >> >> - bad = nandc->data_buffer[bbpos] != 0xff; >> >> + bad = bbm_bytes_buf[0] != 0xff; >> > > This is suspect as it still points to the beginning of the data buffer. >> > Can you please check you did not meant bbm_bytes_buf[bbpos]? >> > >> The main thing here is >> nandc_set_read_loc(nandc, 0, bbpos, host->bbm_size, 1); >> >> After reading one complete CW from NAND, the data will be still >> in NAND HW buffer. >> >> The above register tells that we need to read data from >> bbpos of size host->bbm_size (which is 1 byte for 8 bus witdh >> and 2 byte for 16 bus width) in bbm_bytes_buf. > > I see: idx 0 in bbm_bytes_buf is the data at offset bbpos. Then > it's ok. > >> >> So bbm_bytes_buf[0] will contain the BBM first byte. >> and bbm_bytes_buf[1] will contain the BBM second byte. >> >> Regards, >> Abhishek >> >> >> >> if (chip->options & NAND_BUSWIDTH_16) >> >> - bad = bad || (nandc->data_buffer[bbpos + 1] != 0xff); >> >> + bad = bad || (bbm_bytes_buf[1] != 0xff); > > Sorry, my mistake, I did not see the above line. > > However, technically, the BBM could be located in the first, second or > last page of the block. You should check the three of them are 0xFF > bef
Re: [PATCH v3 15/16] mtd: rawnand: qcom: helper function for raw read
On 2018-06-07 18:13, Miquel Raynal wrote: Hi Abhishek, On Mon, 28 May 2018 13:04:45 +0530, Abhishek Sahu wrote: On 2018-05-27 19:23, Miquel Raynal wrote: > Hi Abhishek, > > On Fri, 25 May 2018 17:51:43 +0530, Abhishek Sahu > wrote: > >> This patch does minor code reorganization for raw reads. >> Currently the raw read is required for complete page but for >> subsequent patches related with erased codeword bit flips >> detection, only few CW should be read. So, this patch adds >> helper function and introduces the read CW bitmask which >> specifies which CW reads are required in complete page. >> >> Signed-off-by: Abhishek Sahu >> --- >> + for (i = start_step; i < last_step; i++) { > > This comment applies for both patches 15 and 16: > > I would really prefer having a qcom_nandc_read_cw_raw() that reads only > one CW. From qcom_nandc_read_page_raw() you would loop over all the CW > calling qcom_nandc_read_cw_raw() helper (it's raw reads, we don't care > about performances) Doing that way will degrade performances hugely. Currently once we formed the descriptor, the DMA will take care of complete page data transfer from NAND device to buffer and will generate single interrupt. Now it will form one CW descriptor and wait for it to be finished. In background, the data transfer from NAND device will be also split and for every CW, it will give the PAGE_READ command again, which is again time consuming. Data transfer degradation is ok but it will increase CPU time and number of interrupts which will impact other peripherals performance that time. Most of the NAND parts has 4K page size i.e 8 CWs. > and from ->read_page_raw() you would check > CW with uncorrectable errors for being blank with that helper. You > would avoid the not-so-nice logic where you read all the CW between the > first bad one and the last bad one. > The reading b/w first CW and last CW is only from NAND device to NAND HW buffers. The NAND controller has 2 HW buffers which is used to optimize the traffic throughput between the NAND device and system memory,in both directions. Each buffer is 544B in size: 512B for data + 32B spare bytes. Throughput optimization is achieved by executing internal data transfers (i.e. between NANDc buffers and system memory) simultaneously with NAND device operations. Making separate function won't help in improving performance for this case either since once every thing is set for reading page (descriptor formation, issue the PAGE_READ, Data transfer from Flash array to data register in NAND device), the read time from device to NAND HW buffer is very less. Again, we did optimization in which the copying from NAND HW buffer to actual buffer is being done only for those CW's only. Again, in this case CPU time will be more. I understand the point and thanks for detailing it. But raw access happen either during debug (we don't care about CPU time) or when there is an uncorrectable error, which is very unlikely to happen very often when using eg. UBI/UBIFS. So I'm still convinced it is better to have a _simple_ and straightforward code for this path than something way harder to understand and much faster. You can add a comment to explain what would be the fastest way and why though. Thanks Miquel. I will do the changes to make function for single codeword raw read. Regards, Abhishek
Re: [PATCH v3 15/16] mtd: rawnand: qcom: helper function for raw read
On 2018-06-07 18:13, Miquel Raynal wrote: Hi Abhishek, On Mon, 28 May 2018 13:04:45 +0530, Abhishek Sahu wrote: On 2018-05-27 19:23, Miquel Raynal wrote: > Hi Abhishek, > > On Fri, 25 May 2018 17:51:43 +0530, Abhishek Sahu > wrote: > >> This patch does minor code reorganization for raw reads. >> Currently the raw read is required for complete page but for >> subsequent patches related with erased codeword bit flips >> detection, only few CW should be read. So, this patch adds >> helper function and introduces the read CW bitmask which >> specifies which CW reads are required in complete page. >> >> Signed-off-by: Abhishek Sahu >> --- >> + for (i = start_step; i < last_step; i++) { > > This comment applies for both patches 15 and 16: > > I would really prefer having a qcom_nandc_read_cw_raw() that reads only > one CW. From qcom_nandc_read_page_raw() you would loop over all the CW > calling qcom_nandc_read_cw_raw() helper (it's raw reads, we don't care > about performances) Doing that way will degrade performances hugely. Currently once we formed the descriptor, the DMA will take care of complete page data transfer from NAND device to buffer and will generate single interrupt. Now it will form one CW descriptor and wait for it to be finished. In background, the data transfer from NAND device will be also split and for every CW, it will give the PAGE_READ command again, which is again time consuming. Data transfer degradation is ok but it will increase CPU time and number of interrupts which will impact other peripherals performance that time. Most of the NAND parts has 4K page size i.e 8 CWs. > and from ->read_page_raw() you would check > CW with uncorrectable errors for being blank with that helper. You > would avoid the not-so-nice logic where you read all the CW between the > first bad one and the last bad one. > The reading b/w first CW and last CW is only from NAND device to NAND HW buffers. The NAND controller has 2 HW buffers which is used to optimize the traffic throughput between the NAND device and system memory,in both directions. Each buffer is 544B in size: 512B for data + 32B spare bytes. Throughput optimization is achieved by executing internal data transfers (i.e. between NANDc buffers and system memory) simultaneously with NAND device operations. Making separate function won't help in improving performance for this case either since once every thing is set for reading page (descriptor formation, issue the PAGE_READ, Data transfer from Flash array to data register in NAND device), the read time from device to NAND HW buffer is very less. Again, we did optimization in which the copying from NAND HW buffer to actual buffer is being done only for those CW's only. Again, in this case CPU time will be more. I understand the point and thanks for detailing it. But raw access happen either during debug (we don't care about CPU time) or when there is an uncorrectable error, which is very unlikely to happen very often when using eg. UBI/UBIFS. So I'm still convinced it is better to have a _simple_ and straightforward code for this path than something way harder to understand and much faster. You can add a comment to explain what would be the fastest way and why though. Thanks Miquel. I will do the changes to make function for single codeword raw read. Regards, Abhishek
Re: [PATCH v3 01/16] mtd: rawnand: helper function for setting up ECC configuration
On 2018-06-07 18:07, Miquel Raynal wrote: Hi Abhishek, On Mon, 28 May 2018 11:16:29 +0530, Abhishek Sahu wrote: On 2018-05-26 14:12, Miquel Raynal wrote: > Hi Abhishek, > > On Fri, 25 May 2018 17:51:29 +0530, Abhishek Sahu > wrote: > >> commit 2c8f8afa7f92 ("mtd: nand: add generic helpers to check, >> match, maximize ECC settings") provides generic helpers which >> drivers can use for setting up ECC parameters. >> >> Since same board can have different ECC strength nand chips so >> following is the logic for setting up ECC strength and ECC step >> size, which can be used by most of the drivers. >> >> 1. If both ECC step size and ECC strength are already set >>(usually by DT) then just check whether this setting >>is supported by NAND controller. >> 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength >>supported by NAND controller. >> 3. Otherwise, try to match the ECC step size and ECC strength closest >>to the chip's requirement. If available OOB size can't fit the chip >>requirement then select maximum ECC strength which can be fit with >>available OOB size. >> >> This patch introduces nand_ecc_choose_conf function which calls the >> required helper functions for the above logic. The drivers can use >> this single function instead of calling the 3 helper functions >> individually. >> >> CC: Masahiro Yamada >> Signed-off-by: Abhishek Sahu >> --- >> * Changes from v2: >> >> 1. Renamed function to nand_ecc_choose_conf. >> 2. Minor code reorganization to remove warning and 2 function calls >> for nand_maximize_ecc. >> >> * Changes from v1: >> NEW PATCH >> >> drivers/mtd/nand/raw/nand_base.c | 42 >> >> drivers/mtd/nand/raw/nand_base.c | 31 +++ >> include/linux/mtd/rawnand.h | 3 +++ >> 2 files changed, 34 insertions(+) >> >> diff --git a/drivers/mtd/nand/raw/nand_base.c >> b/drivers/mtd/nand/raw/nand_base.c >> index 72f3a89..e52019d 100644 >> --- a/drivers/mtd/nand/raw/nand_base.c >> +++ b/drivers/mtd/nand/raw/nand_base.c >> @@ -6249,6 +6249,37 @@ int nand_maximize_ecc(struct nand_chip *chip, >> } >> EXPORT_SYMBOL_GPL(nand_maximize_ecc); >> >> +/** >> + * nand_ecc_choose_conf - Set the ECC strength and ECC step size >> + * @chip: nand chip info structure >> + * @caps: ECC engine caps info structure >> + * @oobavail: OOB size that the ECC engine can use >> + * >> + * Choose the ECC configuration according to following logic >> + * >> + * 1. If both ECC step size and ECC strength are already set (usually >> by DT) >> + *then check if it is supported by this controller. >> + * 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength. >> + * 3. Otherwise, try to match the ECC step size and ECC strength >> closest >> + *to the chip's requirement. If available OOB size can't fit the >> chip >> + *requirement then fallback to the maximum ECC step size and ECC >> strength. >> + * >> + * On success, the chosen ECC settings are set. >> + */ >> +int nand_ecc_choose_conf(struct nand_chip *chip, >> + const struct nand_ecc_caps *caps, int oobavail) >> +{ >> + if (chip->ecc.size && chip->ecc.strength) >> + return nand_check_ecc_caps(chip, caps, oobavail); >> + >> + if (!(chip->ecc.options & NAND_ECC_MAXIMIZE) && >> + !nand_match_ecc_req(chip, caps, oobavail)) >> + return 0; >> + >> + return nand_maximize_ecc(chip, caps, oobavail); > > I personally don't mind if nand_maximize_ecc() is called twice in > the function if it clarifies the logic. Maybe the following will be > more clear for the user? Thanks Miquel. Both the implementations are fine. The above implementation (which was in Denali NAND driver) code was also clear. We can go for any of these implementation. Shall I update this ? Yes, please :) Thanks Miquel for confirming. I will update accordingly. Also, one more question. Shall I make other functions (nand_check_ecc_caps, nand_maximize_ecc and nand_match_ecc_req) static. Since currently, Denali NAND driver was only using these functions. And Now, this nand_ecc_choose_conf will be help in all the cases. For nand_check_ecc_caps: call nand_ecc_choose_conf with chip->ecc.size && chip->ecc.strength For nand_maximize_ecc: call nand_ecc_choose_conf with
Re: [PATCH v3 01/16] mtd: rawnand: helper function for setting up ECC configuration
On 2018-06-07 18:07, Miquel Raynal wrote: Hi Abhishek, On Mon, 28 May 2018 11:16:29 +0530, Abhishek Sahu wrote: On 2018-05-26 14:12, Miquel Raynal wrote: > Hi Abhishek, > > On Fri, 25 May 2018 17:51:29 +0530, Abhishek Sahu > wrote: > >> commit 2c8f8afa7f92 ("mtd: nand: add generic helpers to check, >> match, maximize ECC settings") provides generic helpers which >> drivers can use for setting up ECC parameters. >> >> Since same board can have different ECC strength nand chips so >> following is the logic for setting up ECC strength and ECC step >> size, which can be used by most of the drivers. >> >> 1. If both ECC step size and ECC strength are already set >>(usually by DT) then just check whether this setting >>is supported by NAND controller. >> 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength >>supported by NAND controller. >> 3. Otherwise, try to match the ECC step size and ECC strength closest >>to the chip's requirement. If available OOB size can't fit the chip >>requirement then select maximum ECC strength which can be fit with >>available OOB size. >> >> This patch introduces nand_ecc_choose_conf function which calls the >> required helper functions for the above logic. The drivers can use >> this single function instead of calling the 3 helper functions >> individually. >> >> CC: Masahiro Yamada >> Signed-off-by: Abhishek Sahu >> --- >> * Changes from v2: >> >> 1. Renamed function to nand_ecc_choose_conf. >> 2. Minor code reorganization to remove warning and 2 function calls >> for nand_maximize_ecc. >> >> * Changes from v1: >> NEW PATCH >> >> drivers/mtd/nand/raw/nand_base.c | 42 >> >> drivers/mtd/nand/raw/nand_base.c | 31 +++ >> include/linux/mtd/rawnand.h | 3 +++ >> 2 files changed, 34 insertions(+) >> >> diff --git a/drivers/mtd/nand/raw/nand_base.c >> b/drivers/mtd/nand/raw/nand_base.c >> index 72f3a89..e52019d 100644 >> --- a/drivers/mtd/nand/raw/nand_base.c >> +++ b/drivers/mtd/nand/raw/nand_base.c >> @@ -6249,6 +6249,37 @@ int nand_maximize_ecc(struct nand_chip *chip, >> } >> EXPORT_SYMBOL_GPL(nand_maximize_ecc); >> >> +/** >> + * nand_ecc_choose_conf - Set the ECC strength and ECC step size >> + * @chip: nand chip info structure >> + * @caps: ECC engine caps info structure >> + * @oobavail: OOB size that the ECC engine can use >> + * >> + * Choose the ECC configuration according to following logic >> + * >> + * 1. If both ECC step size and ECC strength are already set (usually >> by DT) >> + *then check if it is supported by this controller. >> + * 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength. >> + * 3. Otherwise, try to match the ECC step size and ECC strength >> closest >> + *to the chip's requirement. If available OOB size can't fit the >> chip >> + *requirement then fallback to the maximum ECC step size and ECC >> strength. >> + * >> + * On success, the chosen ECC settings are set. >> + */ >> +int nand_ecc_choose_conf(struct nand_chip *chip, >> + const struct nand_ecc_caps *caps, int oobavail) >> +{ >> + if (chip->ecc.size && chip->ecc.strength) >> + return nand_check_ecc_caps(chip, caps, oobavail); >> + >> + if (!(chip->ecc.options & NAND_ECC_MAXIMIZE) && >> + !nand_match_ecc_req(chip, caps, oobavail)) >> + return 0; >> + >> + return nand_maximize_ecc(chip, caps, oobavail); > > I personally don't mind if nand_maximize_ecc() is called twice in > the function if it clarifies the logic. Maybe the following will be > more clear for the user? Thanks Miquel. Both the implementations are fine. The above implementation (which was in Denali NAND driver) code was also clear. We can go for any of these implementation. Shall I update this ? Yes, please :) Thanks Miquel for confirming. I will update accordingly. Also, one more question. Shall I make other functions (nand_check_ecc_caps, nand_maximize_ecc and nand_match_ecc_req) static. Since currently, Denali NAND driver was only using these functions. And Now, this nand_ecc_choose_conf will be help in all the cases. For nand_check_ecc_caps: call nand_ecc_choose_conf with chip->ecc.size && chip->ecc.strength For nand_maximize_ecc: call nand_ecc_choose_conf with
Re: [PATCH v3 01/16] mtd: rawnand: helper function for setting up ECC configuration
On 2018-05-30 13:08, Masahiro Yamada wrote: 2018-05-30 15:21 GMT+09:00 Abhishek Sahu : On 2018-05-30 05:58, Masahiro Yamada wrote: Hi. 2018-05-30 4:30 GMT+09:00 Boris Brezillon : On Sat, 26 May 2018 10:42:47 +0200 Miquel Raynal wrote: Hi Abhishek, On Fri, 25 May 2018 17:51:29 +0530, Abhishek Sahu wrote: > commit 2c8f8afa7f92 ("mtd: nand: add generic helpers to check, > match, maximize ECC settings") provides generic helpers which > drivers can use for setting up ECC parameters. > > Since same board can have different ECC strength nand chips so > following is the logic for setting up ECC strength and ECC step > size, which can be used by most of the drivers. > > 1. If both ECC step size and ECC strength are already set >(usually by DT) then just check whether this setting >is supported by NAND controller. > 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength >supported by NAND controller. > 3. Otherwise, try to match the ECC step size and ECC strength closest >to the chip's requirement. If available OOB size can't fit the chip >requirement then select maximum ECC strength which can be fit with >available OOB size. > > This patch introduces nand_ecc_choose_conf function which calls the > required helper functions for the above logic. The drivers can use > this single function instead of calling the 3 helper functions > individually. > > CC: Masahiro Yamada > Signed-off-by: Abhishek Sahu > --- > * Changes from v2: > > 1. Renamed function to nand_ecc_choose_conf. > 2. Minor code reorganization to remove warning and 2 function calls > for nand_maximize_ecc. > > * Changes from v1: > NEW PATCH > > drivers/mtd/nand/raw/nand_base.c | 42 > > drivers/mtd/nand/raw/nand_base.c | 31 +++ > include/linux/mtd/rawnand.h | 3 +++ > 2 files changed, 34 insertions(+) > > diff --git a/drivers/mtd/nand/raw/nand_base.c > b/drivers/mtd/nand/raw/nand_base.c > index 72f3a89..e52019d 100644 > --- a/drivers/mtd/nand/raw/nand_base.c > +++ b/drivers/mtd/nand/raw/nand_base.c > @@ -6249,6 +6249,37 @@ int nand_maximize_ecc(struct nand_chip *chip, > } > EXPORT_SYMBOL_GPL(nand_maximize_ecc); > > +/** > + * nand_ecc_choose_conf - Set the ECC strength and ECC step size > + * @chip: nand chip info structure > + * @caps: ECC engine caps info structure > + * @oobavail: OOB size that the ECC engine can use > + * > + * Choose the ECC configuration according to following logic > + * > + * 1. If both ECC step size and ECC strength are already set (usually > by DT) > + *then check if it is supported by this controller. > + * 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength. > + * 3. Otherwise, try to match the ECC step size and ECC strength > closest > + *to the chip's requirement. If available OOB size can't fit the > chip > + *requirement then fallback to the maximum ECC step size and ECC > strength. > + * > + * On success, the chosen ECC settings are set. > + */ > +int nand_ecc_choose_conf(struct nand_chip *chip, > +const struct nand_ecc_caps *caps, int oobavail) > +{ > + if (chip->ecc.size && chip->ecc.strength) > + return nand_check_ecc_caps(chip, caps, oobavail); > + > + if (!(chip->ecc.options & NAND_ECC_MAXIMIZE) && > + !nand_match_ecc_req(chip, caps, oobavail)) > + return 0; > + > + return nand_maximize_ecc(chip, caps, oobavail); I personally don't mind if nand_maximize_ecc() is called twice in the function if it clarifies the logic. Maybe the following will be more clear for the user? if (chip->ecc.size && chip->ecc.strength) return nand_check_ecc_caps(chip, caps, oobavail); if (chip->ecc.options & NAND_ECC_MAXIMIZE) return nand_maximize_ecc(chip, caps, oobavail); if (!nand_match_ecc_req(chip, caps, oobavail)) return 0; return nand_maximize_ecc(chip, caps, oobavail); I personally don't mind, and it seems Masahiro wanted to keep the logic he had used in the denali driver. Also, I'm not sure we should just error out when nand_check_ecc_caps() fails. What about something more robust, like: int ret; if (chip->ecc.size && chip->ecc.strength) { ret = nand_check_ecc_caps(chip, caps, oobavail); if (ret) goto maximize_ecc; Nope. When someone asked for a specific ECC config by passing the nand-ecc-xxx props we should apply it or return an erro if it's not supported. People passing those props should now what the ECC engine supports and pick on
Re: [PATCH v3 01/16] mtd: rawnand: helper function for setting up ECC configuration
On 2018-05-30 13:08, Masahiro Yamada wrote: 2018-05-30 15:21 GMT+09:00 Abhishek Sahu : On 2018-05-30 05:58, Masahiro Yamada wrote: Hi. 2018-05-30 4:30 GMT+09:00 Boris Brezillon : On Sat, 26 May 2018 10:42:47 +0200 Miquel Raynal wrote: Hi Abhishek, On Fri, 25 May 2018 17:51:29 +0530, Abhishek Sahu wrote: > commit 2c8f8afa7f92 ("mtd: nand: add generic helpers to check, > match, maximize ECC settings") provides generic helpers which > drivers can use for setting up ECC parameters. > > Since same board can have different ECC strength nand chips so > following is the logic for setting up ECC strength and ECC step > size, which can be used by most of the drivers. > > 1. If both ECC step size and ECC strength are already set >(usually by DT) then just check whether this setting >is supported by NAND controller. > 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength >supported by NAND controller. > 3. Otherwise, try to match the ECC step size and ECC strength closest >to the chip's requirement. If available OOB size can't fit the chip >requirement then select maximum ECC strength which can be fit with >available OOB size. > > This patch introduces nand_ecc_choose_conf function which calls the > required helper functions for the above logic. The drivers can use > this single function instead of calling the 3 helper functions > individually. > > CC: Masahiro Yamada > Signed-off-by: Abhishek Sahu > --- > * Changes from v2: > > 1. Renamed function to nand_ecc_choose_conf. > 2. Minor code reorganization to remove warning and 2 function calls > for nand_maximize_ecc. > > * Changes from v1: > NEW PATCH > > drivers/mtd/nand/raw/nand_base.c | 42 > > drivers/mtd/nand/raw/nand_base.c | 31 +++ > include/linux/mtd/rawnand.h | 3 +++ > 2 files changed, 34 insertions(+) > > diff --git a/drivers/mtd/nand/raw/nand_base.c > b/drivers/mtd/nand/raw/nand_base.c > index 72f3a89..e52019d 100644 > --- a/drivers/mtd/nand/raw/nand_base.c > +++ b/drivers/mtd/nand/raw/nand_base.c > @@ -6249,6 +6249,37 @@ int nand_maximize_ecc(struct nand_chip *chip, > } > EXPORT_SYMBOL_GPL(nand_maximize_ecc); > > +/** > + * nand_ecc_choose_conf - Set the ECC strength and ECC step size > + * @chip: nand chip info structure > + * @caps: ECC engine caps info structure > + * @oobavail: OOB size that the ECC engine can use > + * > + * Choose the ECC configuration according to following logic > + * > + * 1. If both ECC step size and ECC strength are already set (usually > by DT) > + *then check if it is supported by this controller. > + * 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength. > + * 3. Otherwise, try to match the ECC step size and ECC strength > closest > + *to the chip's requirement. If available OOB size can't fit the > chip > + *requirement then fallback to the maximum ECC step size and ECC > strength. > + * > + * On success, the chosen ECC settings are set. > + */ > +int nand_ecc_choose_conf(struct nand_chip *chip, > +const struct nand_ecc_caps *caps, int oobavail) > +{ > + if (chip->ecc.size && chip->ecc.strength) > + return nand_check_ecc_caps(chip, caps, oobavail); > + > + if (!(chip->ecc.options & NAND_ECC_MAXIMIZE) && > + !nand_match_ecc_req(chip, caps, oobavail)) > + return 0; > + > + return nand_maximize_ecc(chip, caps, oobavail); I personally don't mind if nand_maximize_ecc() is called twice in the function if it clarifies the logic. Maybe the following will be more clear for the user? if (chip->ecc.size && chip->ecc.strength) return nand_check_ecc_caps(chip, caps, oobavail); if (chip->ecc.options & NAND_ECC_MAXIMIZE) return nand_maximize_ecc(chip, caps, oobavail); if (!nand_match_ecc_req(chip, caps, oobavail)) return 0; return nand_maximize_ecc(chip, caps, oobavail); I personally don't mind, and it seems Masahiro wanted to keep the logic he had used in the denali driver. Also, I'm not sure we should just error out when nand_check_ecc_caps() fails. What about something more robust, like: int ret; if (chip->ecc.size && chip->ecc.strength) { ret = nand_check_ecc_caps(chip, caps, oobavail); if (ret) goto maximize_ecc; Nope. When someone asked for a specific ECC config by passing the nand-ecc-xxx props we should apply it or return an erro if it's not supported. People passing those props should now what the ECC engine supports and pick on
Re: [PATCH v3 01/16] mtd: rawnand: helper function for setting up ECC configuration
On 2018-05-30 05:58, Masahiro Yamada wrote: Hi. 2018-05-30 4:30 GMT+09:00 Boris Brezillon : On Sat, 26 May 2018 10:42:47 +0200 Miquel Raynal wrote: Hi Abhishek, On Fri, 25 May 2018 17:51:29 +0530, Abhishek Sahu wrote: > commit 2c8f8afa7f92 ("mtd: nand: add generic helpers to check, > match, maximize ECC settings") provides generic helpers which > drivers can use for setting up ECC parameters. > > Since same board can have different ECC strength nand chips so > following is the logic for setting up ECC strength and ECC step > size, which can be used by most of the drivers. > > 1. If both ECC step size and ECC strength are already set >(usually by DT) then just check whether this setting >is supported by NAND controller. > 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength >supported by NAND controller. > 3. Otherwise, try to match the ECC step size and ECC strength closest >to the chip's requirement. If available OOB size can't fit the chip >requirement then select maximum ECC strength which can be fit with >available OOB size. > > This patch introduces nand_ecc_choose_conf function which calls the > required helper functions for the above logic. The drivers can use > this single function instead of calling the 3 helper functions > individually. > > CC: Masahiro Yamada > Signed-off-by: Abhishek Sahu > --- > * Changes from v2: > > 1. Renamed function to nand_ecc_choose_conf. > 2. Minor code reorganization to remove warning and 2 function calls > for nand_maximize_ecc. > > * Changes from v1: > NEW PATCH > > drivers/mtd/nand/raw/nand_base.c | 42 > drivers/mtd/nand/raw/nand_base.c | 31 +++ > include/linux/mtd/rawnand.h | 3 +++ > 2 files changed, 34 insertions(+) > > diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c > index 72f3a89..e52019d 100644 > --- a/drivers/mtd/nand/raw/nand_base.c > +++ b/drivers/mtd/nand/raw/nand_base.c > @@ -6249,6 +6249,37 @@ int nand_maximize_ecc(struct nand_chip *chip, > } > EXPORT_SYMBOL_GPL(nand_maximize_ecc); > > +/** > + * nand_ecc_choose_conf - Set the ECC strength and ECC step size > + * @chip: nand chip info structure > + * @caps: ECC engine caps info structure > + * @oobavail: OOB size that the ECC engine can use > + * > + * Choose the ECC configuration according to following logic > + * > + * 1. If both ECC step size and ECC strength are already set (usually by DT) > + *then check if it is supported by this controller. > + * 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength. > + * 3. Otherwise, try to match the ECC step size and ECC strength closest > + *to the chip's requirement. If available OOB size can't fit the chip > + *requirement then fallback to the maximum ECC step size and ECC strength. > + * > + * On success, the chosen ECC settings are set. > + */ > +int nand_ecc_choose_conf(struct nand_chip *chip, > +const struct nand_ecc_caps *caps, int oobavail) > +{ > + if (chip->ecc.size && chip->ecc.strength) > + return nand_check_ecc_caps(chip, caps, oobavail); > + > + if (!(chip->ecc.options & NAND_ECC_MAXIMIZE) && > + !nand_match_ecc_req(chip, caps, oobavail)) > + return 0; > + > + return nand_maximize_ecc(chip, caps, oobavail); I personally don't mind if nand_maximize_ecc() is called twice in the function if it clarifies the logic. Maybe the following will be more clear for the user? if (chip->ecc.size && chip->ecc.strength) return nand_check_ecc_caps(chip, caps, oobavail); if (chip->ecc.options & NAND_ECC_MAXIMIZE) return nand_maximize_ecc(chip, caps, oobavail); if (!nand_match_ecc_req(chip, caps, oobavail)) return 0; return nand_maximize_ecc(chip, caps, oobavail); I personally don't mind, and it seems Masahiro wanted to keep the logic he had used in the denali driver. Also, I'm not sure we should just error out when nand_check_ecc_caps() fails. What about something more robust, like: int ret; if (chip->ecc.size && chip->ecc.strength) { ret = nand_check_ecc_caps(chip, caps, oobavail); if (ret) goto maximize_ecc; Nope. When someone asked for a specific ECC config by passing the nand-ecc-xxx props we should apply it or return an erro if it's not supported. People passing those props should now what the ECC engine supports and pick one valid values. return 0; } if (chip->ecc.options & NAND_ECC_MAXIMIZE) goto maximize_ecc;
Re: [PATCH v3 01/16] mtd: rawnand: helper function for setting up ECC configuration
On 2018-05-30 05:58, Masahiro Yamada wrote: Hi. 2018-05-30 4:30 GMT+09:00 Boris Brezillon : On Sat, 26 May 2018 10:42:47 +0200 Miquel Raynal wrote: Hi Abhishek, On Fri, 25 May 2018 17:51:29 +0530, Abhishek Sahu wrote: > commit 2c8f8afa7f92 ("mtd: nand: add generic helpers to check, > match, maximize ECC settings") provides generic helpers which > drivers can use for setting up ECC parameters. > > Since same board can have different ECC strength nand chips so > following is the logic for setting up ECC strength and ECC step > size, which can be used by most of the drivers. > > 1. If both ECC step size and ECC strength are already set >(usually by DT) then just check whether this setting >is supported by NAND controller. > 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength >supported by NAND controller. > 3. Otherwise, try to match the ECC step size and ECC strength closest >to the chip's requirement. If available OOB size can't fit the chip >requirement then select maximum ECC strength which can be fit with >available OOB size. > > This patch introduces nand_ecc_choose_conf function which calls the > required helper functions for the above logic. The drivers can use > this single function instead of calling the 3 helper functions > individually. > > CC: Masahiro Yamada > Signed-off-by: Abhishek Sahu > --- > * Changes from v2: > > 1. Renamed function to nand_ecc_choose_conf. > 2. Minor code reorganization to remove warning and 2 function calls > for nand_maximize_ecc. > > * Changes from v1: > NEW PATCH > > drivers/mtd/nand/raw/nand_base.c | 42 > drivers/mtd/nand/raw/nand_base.c | 31 +++ > include/linux/mtd/rawnand.h | 3 +++ > 2 files changed, 34 insertions(+) > > diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c > index 72f3a89..e52019d 100644 > --- a/drivers/mtd/nand/raw/nand_base.c > +++ b/drivers/mtd/nand/raw/nand_base.c > @@ -6249,6 +6249,37 @@ int nand_maximize_ecc(struct nand_chip *chip, > } > EXPORT_SYMBOL_GPL(nand_maximize_ecc); > > +/** > + * nand_ecc_choose_conf - Set the ECC strength and ECC step size > + * @chip: nand chip info structure > + * @caps: ECC engine caps info structure > + * @oobavail: OOB size that the ECC engine can use > + * > + * Choose the ECC configuration according to following logic > + * > + * 1. If both ECC step size and ECC strength are already set (usually by DT) > + *then check if it is supported by this controller. > + * 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength. > + * 3. Otherwise, try to match the ECC step size and ECC strength closest > + *to the chip's requirement. If available OOB size can't fit the chip > + *requirement then fallback to the maximum ECC step size and ECC strength. > + * > + * On success, the chosen ECC settings are set. > + */ > +int nand_ecc_choose_conf(struct nand_chip *chip, > +const struct nand_ecc_caps *caps, int oobavail) > +{ > + if (chip->ecc.size && chip->ecc.strength) > + return nand_check_ecc_caps(chip, caps, oobavail); > + > + if (!(chip->ecc.options & NAND_ECC_MAXIMIZE) && > + !nand_match_ecc_req(chip, caps, oobavail)) > + return 0; > + > + return nand_maximize_ecc(chip, caps, oobavail); I personally don't mind if nand_maximize_ecc() is called twice in the function if it clarifies the logic. Maybe the following will be more clear for the user? if (chip->ecc.size && chip->ecc.strength) return nand_check_ecc_caps(chip, caps, oobavail); if (chip->ecc.options & NAND_ECC_MAXIMIZE) return nand_maximize_ecc(chip, caps, oobavail); if (!nand_match_ecc_req(chip, caps, oobavail)) return 0; return nand_maximize_ecc(chip, caps, oobavail); I personally don't mind, and it seems Masahiro wanted to keep the logic he had used in the denali driver. Also, I'm not sure we should just error out when nand_check_ecc_caps() fails. What about something more robust, like: int ret; if (chip->ecc.size && chip->ecc.strength) { ret = nand_check_ecc_caps(chip, caps, oobavail); if (ret) goto maximize_ecc; Nope. When someone asked for a specific ECC config by passing the nand-ecc-xxx props we should apply it or return an erro if it's not supported. People passing those props should now what the ECC engine supports and pick one valid values. return 0; } if (chip->ecc.options & NAND_ECC_MAXIMIZE) goto maximize_ecc;
Re: [PATCH v3 13/16] mtd: rawnand: qcom: minor code reorganization for bad block check
On 2018-05-28 12:33, Miquel Raynal wrote: Hi Abhishek, >> /* implements ecc->read_page() */ >> static int qcom_nandc_read_page(struct mtd_info *mtd, struct >> nand_chip *chip, >>uint8_t *buf, int oob_required, int page) >> @@ -2118,6 +2083,7 @@ static int qcom_nandc_block_bad(struct mtd_info >> *mtd, loff_t ofs) >>struct nand_ecc_ctrl *ecc = >ecc; >>int page, ret, bbpos, bad = 0; >>u32 flash_status; >> + u8 *bbm_bytes_buf = chip->data_buf; >> >> page = (int)(ofs >> chip->page_shift) & chip->pagemask; >> >> @@ -2128,11 +2094,31 @@ static int qcom_nandc_block_bad(struct >> mtd_info *mtd, loff_t ofs) >> * that contains the BBM >> */ >>host->use_ecc = false; >> + bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); > > Are we sure there is no layout with only 1 step? All the layouts are such that, the BBM will come in first byte of spare area. For 4 bit ECC, the cw_size is 528 so for 2K page 2048 - 528 * 3 = 464 My question was more about small page NANDs. But I suppose it works too if ecc->steps == 1. Correct Miquel. So for last CW, the 464 is BBM (i.e 2048th byte) in full page. > >> >> clear_bam_transaction(nandc); >> - ret = copy_last_cw(host, page); >> - if (ret) >> + clear_read_regs(nandc); >> + >> + set_address(host, host->cw_size * (ecc->steps - 1), page); >> + update_rw_regs(host, 1, true); >> + >> + /* >> + * The last codeword data will be copied from NAND device to NAND >> + * controller internal HW buffer. Copy only required BBM size bytes >> + * from this HW buffer to bbm_bytes_buf which is present at >> + * bbpos offset. >> + */ >> + nandc_set_read_loc(nandc, 0, bbpos, host->bbm_size, 1); >> + config_nand_single_cw_page_read(nandc); >> + read_data_dma(nandc, FLASH_BUF_ACC + bbpos, bbm_bytes_buf, >> +host->bbm_size, 0); >> + >> + ret = submit_descs(nandc); >> + free_descs(nandc); >> + if (ret) { >> + dev_err(nandc->dev, "failed to copy bad block bytes\n"); >>goto err; >> + } >> >> flash_status = le32_to_cpu(nandc->reg_read_buf[0]); >> >> @@ -2141,12 +2127,10 @@ static int qcom_nandc_block_bad(struct >> mtd_info *mtd, loff_t ofs) >>goto err; >>} >> >> - bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); >> - >> - bad = nandc->data_buffer[bbpos] != 0xff; >> + bad = bbm_bytes_buf[0] != 0xff; > > This is suspect as it still points to the beginning of the data buffer. > Can you please check you did not meant bbm_bytes_buf[bbpos]? > The main thing here is nandc_set_read_loc(nandc, 0, bbpos, host->bbm_size, 1); After reading one complete CW from NAND, the data will be still in NAND HW buffer. The above register tells that we need to read data from bbpos of size host->bbm_size (which is 1 byte for 8 bus witdh and 2 byte for 16 bus width) in bbm_bytes_buf. I see: idx 0 in bbm_bytes_buf is the data at offset bbpos. Then it's ok. So bbm_bytes_buf[0] will contain the BBM first byte. and bbm_bytes_buf[1] will contain the BBM second byte. Regards, Abhishek >> >> if (chip->options & NAND_BUSWIDTH_16) >> - bad = bad || (nandc->data_buffer[bbpos + 1] != 0xff); >> + bad = bad || (bbm_bytes_buf[1] != 0xff); Sorry, my mistake, I did not see the above line. However, technically, the BBM could be located in the first, second or last page of the block. You should check the three of them are 0xFF before declaring the block is not bad. The more I look at the function, the more I wonder if you actually need it. Why does the generic nand_block_bad() implementation in the core do not fit? The BBM bytes can be accessed in raw mode only for QCOM NAND Contoller. We started with following patch for initial patches http://patchwork.ozlabs.org/patch/508565/ I am also not very much sure, how can we go ahead now. Ideally we need to use generic function only which requires raw_read. Thanks, Abhishek
Re: [PATCH v3 13/16] mtd: rawnand: qcom: minor code reorganization for bad block check
On 2018-05-28 12:33, Miquel Raynal wrote: Hi Abhishek, >> /* implements ecc->read_page() */ >> static int qcom_nandc_read_page(struct mtd_info *mtd, struct >> nand_chip *chip, >>uint8_t *buf, int oob_required, int page) >> @@ -2118,6 +2083,7 @@ static int qcom_nandc_block_bad(struct mtd_info >> *mtd, loff_t ofs) >>struct nand_ecc_ctrl *ecc = >ecc; >>int page, ret, bbpos, bad = 0; >>u32 flash_status; >> + u8 *bbm_bytes_buf = chip->data_buf; >> >> page = (int)(ofs >> chip->page_shift) & chip->pagemask; >> >> @@ -2128,11 +2094,31 @@ static int qcom_nandc_block_bad(struct >> mtd_info *mtd, loff_t ofs) >> * that contains the BBM >> */ >>host->use_ecc = false; >> + bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); > > Are we sure there is no layout with only 1 step? All the layouts are such that, the BBM will come in first byte of spare area. For 4 bit ECC, the cw_size is 528 so for 2K page 2048 - 528 * 3 = 464 My question was more about small page NANDs. But I suppose it works too if ecc->steps == 1. Correct Miquel. So for last CW, the 464 is BBM (i.e 2048th byte) in full page. > >> >> clear_bam_transaction(nandc); >> - ret = copy_last_cw(host, page); >> - if (ret) >> + clear_read_regs(nandc); >> + >> + set_address(host, host->cw_size * (ecc->steps - 1), page); >> + update_rw_regs(host, 1, true); >> + >> + /* >> + * The last codeword data will be copied from NAND device to NAND >> + * controller internal HW buffer. Copy only required BBM size bytes >> + * from this HW buffer to bbm_bytes_buf which is present at >> + * bbpos offset. >> + */ >> + nandc_set_read_loc(nandc, 0, bbpos, host->bbm_size, 1); >> + config_nand_single_cw_page_read(nandc); >> + read_data_dma(nandc, FLASH_BUF_ACC + bbpos, bbm_bytes_buf, >> +host->bbm_size, 0); >> + >> + ret = submit_descs(nandc); >> + free_descs(nandc); >> + if (ret) { >> + dev_err(nandc->dev, "failed to copy bad block bytes\n"); >>goto err; >> + } >> >> flash_status = le32_to_cpu(nandc->reg_read_buf[0]); >> >> @@ -2141,12 +2127,10 @@ static int qcom_nandc_block_bad(struct >> mtd_info *mtd, loff_t ofs) >>goto err; >>} >> >> - bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); >> - >> - bad = nandc->data_buffer[bbpos] != 0xff; >> + bad = bbm_bytes_buf[0] != 0xff; > > This is suspect as it still points to the beginning of the data buffer. > Can you please check you did not meant bbm_bytes_buf[bbpos]? > The main thing here is nandc_set_read_loc(nandc, 0, bbpos, host->bbm_size, 1); After reading one complete CW from NAND, the data will be still in NAND HW buffer. The above register tells that we need to read data from bbpos of size host->bbm_size (which is 1 byte for 8 bus witdh and 2 byte for 16 bus width) in bbm_bytes_buf. I see: idx 0 in bbm_bytes_buf is the data at offset bbpos. Then it's ok. So bbm_bytes_buf[0] will contain the BBM first byte. and bbm_bytes_buf[1] will contain the BBM second byte. Regards, Abhishek >> >> if (chip->options & NAND_BUSWIDTH_16) >> - bad = bad || (nandc->data_buffer[bbpos + 1] != 0xff); >> + bad = bad || (bbm_bytes_buf[1] != 0xff); Sorry, my mistake, I did not see the above line. However, technically, the BBM could be located in the first, second or last page of the block. You should check the three of them are 0xFF before declaring the block is not bad. The more I look at the function, the more I wonder if you actually need it. Why does the generic nand_block_bad() implementation in the core do not fit? The BBM bytes can be accessed in raw mode only for QCOM NAND Contoller. We started with following patch for initial patches http://patchwork.ozlabs.org/patch/508565/ I am also not very much sure, how can we go ahead now. Ideally we need to use generic function only which requires raw_read. Thanks, Abhishek
Re: [PATCH v3 15/16] mtd: rawnand: qcom: helper function for raw read
On 2018-05-27 19:23, Miquel Raynal wrote: Hi Abhishek, On Fri, 25 May 2018 17:51:43 +0530, Abhishek Sahu <abs...@codeaurora.org> wrote: This patch does minor code reorganization for raw reads. Currently the raw read is required for complete page but for subsequent patches related with erased codeword bit flips detection, only few CW should be read. So, this patch adds helper function and introduces the read CW bitmask which specifies which CW reads are required in complete page. Signed-off-by: Abhishek Sahu <abs...@codeaurora.org> --- * Changes from v2: NONE * Changes from v1: 1. Included more detail in function comment drivers/mtd/nand/raw/qcom_nandc.c | 197 -- 1 file changed, 123 insertions(+), 74 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 87f900e..34143a4 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1588,6 +1588,127 @@ static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) } /* + * Helper to perform the page raw read operation. The read_cw_mask will be + * used to specify the codewords (CW) for which the data should be read. The + * single page contains multiple CW. + * + * Normally other NAND controllers store the data in main area and + * ecc bytes in OOB area. So, if page size is 2048+64 then 2048 + * data bytes will go in main area followed by ECC bytes. The QCOM NAND + * controller follows different layout in which the data+OOB is internally + * divided in 528/532 bytes CW and each CW contains 516 bytes followed by + * ECC parity bytes for that CW. By this, 4 available OOB bytes per CW + * will also be protected with ECC. + * + * For each CW read, following are the 2 steps: + * 1. Read the codeword bytes from NAND chip to NAND controller internal HW + *buffer. + * 2. Copy all these bytes from this HW buffer to actual buffer. + * + * Sometime, only few CW data is required in complete page. The read_cw_mask + * specifies which CW in a page needs to be read. Start address will be + * determined with this CW mask to skip unnecessary data copy from NAND + * flash device. Then, actual data copy from NAND controller HW internal buffer + * to data buffer will be done only for the CWs, which have the mask set. + */ +static int +nandc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + u8 *data_buf, u8 *oob_buf, + int page, unsigned long read_cw_mask) Please prefix the helper with "qcom_nandc" Sure Miquel. I will update that. +{ + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = >ecc; + int i, ret; + int read_loc, start_step, last_step; + + nand_read_page_op(chip, page, 0, NULL, 0); + + host->use_ecc = false; + start_step = ffs(read_cw_mask) - 1; + last_step = fls(read_cw_mask); + + clear_bam_transaction(nandc); + set_address(host, host->cw_size * start_step, page); + update_rw_regs(host, last_step - start_step, true); + config_nand_page_read(nandc); + + for (i = start_step; i < last_step; i++) { This comment applies for both patches 15 and 16: I would really prefer having a qcom_nandc_read_cw_raw() that reads only one CW. From qcom_nandc_read_page_raw() you would loop over all the CW calling qcom_nandc_read_cw_raw() helper (it's raw reads, we don't care about performances) Doing that way will degrade performances hugely. Currently once we formed the descriptor, the DMA will take care of complete page data transfer from NAND device to buffer and will generate single interrupt. Now it will form one CW descriptor and wait for it to be finished. In background, the data transfer from NAND device will be also split and for every CW, it will give the PAGE_READ command again, which is again time consuming. Data transfer degradation is ok but it will increase CPU time and number of interrupts which will impact other peripherals performance that time. Most of the NAND parts has 4K page size i.e 8 CWs. and from ->read_page_raw() you would check CW with uncorrectable errors for being blank with that helper. You would avoid the not-so-nice logic where you read all the CW between the first bad one and the last bad one. The reading b/w first CW and last CW is only from NAND device to NAND HW buffers. The NAND controller has 2 HW buffers which is used to optimize the traffic throughput between the NAND device and system memory,in both directions. Each buffer is 544B in size: 512B for data + 32B spare bytes. Throughput optimization is achieved by executing internal data transfers (i.e. between NANDc buffers and system memory) simultaneously with NAND device operations. Making separate function won't help in improv
Re: [PATCH v3 15/16] mtd: rawnand: qcom: helper function for raw read
On 2018-05-27 19:23, Miquel Raynal wrote: Hi Abhishek, On Fri, 25 May 2018 17:51:43 +0530, Abhishek Sahu wrote: This patch does minor code reorganization for raw reads. Currently the raw read is required for complete page but for subsequent patches related with erased codeword bit flips detection, only few CW should be read. So, this patch adds helper function and introduces the read CW bitmask which specifies which CW reads are required in complete page. Signed-off-by: Abhishek Sahu --- * Changes from v2: NONE * Changes from v1: 1. Included more detail in function comment drivers/mtd/nand/raw/qcom_nandc.c | 197 -- 1 file changed, 123 insertions(+), 74 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 87f900e..34143a4 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1588,6 +1588,127 @@ static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) } /* + * Helper to perform the page raw read operation. The read_cw_mask will be + * used to specify the codewords (CW) for which the data should be read. The + * single page contains multiple CW. + * + * Normally other NAND controllers store the data in main area and + * ecc bytes in OOB area. So, if page size is 2048+64 then 2048 + * data bytes will go in main area followed by ECC bytes. The QCOM NAND + * controller follows different layout in which the data+OOB is internally + * divided in 528/532 bytes CW and each CW contains 516 bytes followed by + * ECC parity bytes for that CW. By this, 4 available OOB bytes per CW + * will also be protected with ECC. + * + * For each CW read, following are the 2 steps: + * 1. Read the codeword bytes from NAND chip to NAND controller internal HW + *buffer. + * 2. Copy all these bytes from this HW buffer to actual buffer. + * + * Sometime, only few CW data is required in complete page. The read_cw_mask + * specifies which CW in a page needs to be read. Start address will be + * determined with this CW mask to skip unnecessary data copy from NAND + * flash device. Then, actual data copy from NAND controller HW internal buffer + * to data buffer will be done only for the CWs, which have the mask set. + */ +static int +nandc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + u8 *data_buf, u8 *oob_buf, + int page, unsigned long read_cw_mask) Please prefix the helper with "qcom_nandc" Sure Miquel. I will update that. +{ + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = >ecc; + int i, ret; + int read_loc, start_step, last_step; + + nand_read_page_op(chip, page, 0, NULL, 0); + + host->use_ecc = false; + start_step = ffs(read_cw_mask) - 1; + last_step = fls(read_cw_mask); + + clear_bam_transaction(nandc); + set_address(host, host->cw_size * start_step, page); + update_rw_regs(host, last_step - start_step, true); + config_nand_page_read(nandc); + + for (i = start_step; i < last_step; i++) { This comment applies for both patches 15 and 16: I would really prefer having a qcom_nandc_read_cw_raw() that reads only one CW. From qcom_nandc_read_page_raw() you would loop over all the CW calling qcom_nandc_read_cw_raw() helper (it's raw reads, we don't care about performances) Doing that way will degrade performances hugely. Currently once we formed the descriptor, the DMA will take care of complete page data transfer from NAND device to buffer and will generate single interrupt. Now it will form one CW descriptor and wait for it to be finished. In background, the data transfer from NAND device will be also split and for every CW, it will give the PAGE_READ command again, which is again time consuming. Data transfer degradation is ok but it will increase CPU time and number of interrupts which will impact other peripherals performance that time. Most of the NAND parts has 4K page size i.e 8 CWs. and from ->read_page_raw() you would check CW with uncorrectable errors for being blank with that helper. You would avoid the not-so-nice logic where you read all the CW between the first bad one and the last bad one. The reading b/w first CW and last CW is only from NAND device to NAND HW buffers. The NAND controller has 2 HW buffers which is used to optimize the traffic throughput between the NAND device and system memory,in both directions. Each buffer is 544B in size: 512B for data + 32B spare bytes. Throughput optimization is achieved by executing internal data transfers (i.e. between NANDc buffers and system memory) simultaneously with NAND device operations. Making separate function won't help in improving performance for this case either since once every thin
Re: [PATCH v3 13/16] mtd: rawnand: qcom: minor code reorganization for bad block check
On 2018-05-26 14:28, Miquel Raynal wrote: Hi Abhishek, @@ -2141,12 +2127,10 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) goto err; } - bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); - - bad = nandc->data_buffer[bbpos] != 0xff; + bad = bbm_bytes_buf[0] != 0xff; BTW, as there are host->bbm_size bytes that can inform on the block state, don't we need to check all of them? We are checking all of them. host->bbm_size will be either 1 (for NAND_BUSWIDTH_8) or 2 (for NAND_BUSWIDTH_16). https://elixir.bootlin.com/linux/v4.17-rc7/source/drivers/mtd/nand/raw/qcom_nandc.c#L2347 Thanks, Abhishek if (chip->options & NAND_BUSWIDTH_16) - bad = bad || (nandc->data_buffer[bbpos + 1] != 0xff); + bad = bad || (bbm_bytes_buf[1] != 0xff); err: return bad; } Thanks, Miquèl
Re: [PATCH v3 13/16] mtd: rawnand: qcom: minor code reorganization for bad block check
On 2018-05-26 14:28, Miquel Raynal wrote: Hi Abhishek, @@ -2141,12 +2127,10 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) goto err; } - bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); - - bad = nandc->data_buffer[bbpos] != 0xff; + bad = bbm_bytes_buf[0] != 0xff; BTW, as there are host->bbm_size bytes that can inform on the block state, don't we need to check all of them? We are checking all of them. host->bbm_size will be either 1 (for NAND_BUSWIDTH_8) or 2 (for NAND_BUSWIDTH_16). https://elixir.bootlin.com/linux/v4.17-rc7/source/drivers/mtd/nand/raw/qcom_nandc.c#L2347 Thanks, Abhishek if (chip->options & NAND_BUSWIDTH_16) - bad = bad || (nandc->data_buffer[bbpos + 1] != 0xff); + bad = bad || (bbm_bytes_buf[1] != 0xff); err: return bad; } Thanks, Miquèl
Re: [PATCH v3 13/16] mtd: rawnand: qcom: minor code reorganization for bad block check
On 2018-05-26 14:16, Miquel Raynal wrote: Hi Abhishek, On Fri, 25 May 2018 17:51:41 +0530, Abhishek Sahu <abs...@codeaurora.org> wrote: The QCOM NAND controller layout is such that, the bad block byte offset for last codeword will come to first byte in spare area. "is the first spare byte"? Currently, the raw read for last codeword is being done with copy_last_cw function. It does following 2 things: "It does the following:" 1. Read the last codeword bytes from NAND chip to NAND controller internal HW buffer. 2. Copy all these bytes from HW buffer to actual buffer. For bad block check, maximum two bytes are required so instead of copying the complete bytes in step 2, only those bbm_size bytes can be copied. This patch does minor code reorganization for the same. After this, copy_last_cw function won’t be required. "This patch does minor code reorganization to just retrieve these two bytes when checking for bad blocks, allowing to remove copy_last_cw() now useless." Thanks Miquel. I will update all these. Signed-off-by: Abhishek Sahu <abs...@codeaurora.org> --- * Changes from v2: 1. Changed commit message and comments slightly * Changes from v1: NEW CHANGE drivers/mtd/nand/raw/qcom_nandc.c | 66 +++ 1 file changed, 25 insertions(+), 41 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index d693b5f..f72bc8a 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1769,41 +1769,6 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, return parse_read_errors(host, data_buf_start, oob_buf_start); } -/* - * a helper that copies the last step/codeword of a page (containing free oob) - * into our local buffer - */ -static int copy_last_cw(struct qcom_nand_host *host, int page) -{ - struct nand_chip *chip = >chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = >ecc; - int size; - int ret; - - clear_read_regs(nandc); - - size = host->use_ecc ? host->cw_data : host->cw_size; - - /* prepare a clean read buffer */ - memset(nandc->data_buffer, 0xff, size); - - set_address(host, host->cw_size * (ecc->steps - 1), page); - update_rw_regs(host, 1, true); - - config_nand_single_cw_page_read(nandc); - - read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); - - ret = submit_descs(nandc); - if (ret) - dev_err(nandc->dev, "failed to copy last codeword\n"); - - free_descs(nandc); - - return ret; -} - /* implements ecc->read_page() */ static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) @@ -2118,6 +2083,7 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) struct nand_ecc_ctrl *ecc = >ecc; int page, ret, bbpos, bad = 0; u32 flash_status; + u8 *bbm_bytes_buf = chip->data_buf; page = (int)(ofs >> chip->page_shift) & chip->pagemask; @@ -2128,11 +2094,31 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) * that contains the BBM */ host->use_ecc = false; + bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); Are we sure there is no layout with only 1 step? All the layouts are such that, the BBM will come in first byte of spare area. For 4 bit ECC, the cw_size is 528 so for 2K page 2048 - 528 * 3 = 464 So for last CW, the 464 is BBM (i.e 2048th byte) in full page. clear_bam_transaction(nandc); - ret = copy_last_cw(host, page); - if (ret) + clear_read_regs(nandc); + + set_address(host, host->cw_size * (ecc->steps - 1), page); + update_rw_regs(host, 1, true); + + /* +* The last codeword data will be copied from NAND device to NAND +* controller internal HW buffer. Copy only required BBM size bytes +* from this HW buffer to bbm_bytes_buf which is present at +* bbpos offset. +*/ + nandc_set_read_loc(nandc, 0, bbpos, host->bbm_size, 1); + config_nand_single_cw_page_read(nandc); + read_data_dma(nandc, FLASH_BUF_ACC + bbpos, bbm_bytes_buf, + host->bbm_size, 0); + + ret = submit_descs(nandc); + free_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failed to copy bad block bytes\n"); goto err; + } flash_status = le32_to_cpu(nandc->reg_read_buf[0]); @@ -2141,12 +2127,10 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) goto err; } - bbpos = mtd->writesize - host
Re: [PATCH v3 13/16] mtd: rawnand: qcom: minor code reorganization for bad block check
On 2018-05-26 14:16, Miquel Raynal wrote: Hi Abhishek, On Fri, 25 May 2018 17:51:41 +0530, Abhishek Sahu wrote: The QCOM NAND controller layout is such that, the bad block byte offset for last codeword will come to first byte in spare area. "is the first spare byte"? Currently, the raw read for last codeword is being done with copy_last_cw function. It does following 2 things: "It does the following:" 1. Read the last codeword bytes from NAND chip to NAND controller internal HW buffer. 2. Copy all these bytes from HW buffer to actual buffer. For bad block check, maximum two bytes are required so instead of copying the complete bytes in step 2, only those bbm_size bytes can be copied. This patch does minor code reorganization for the same. After this, copy_last_cw function won’t be required. "This patch does minor code reorganization to just retrieve these two bytes when checking for bad blocks, allowing to remove copy_last_cw() now useless." Thanks Miquel. I will update all these. Signed-off-by: Abhishek Sahu --- * Changes from v2: 1. Changed commit message and comments slightly * Changes from v1: NEW CHANGE drivers/mtd/nand/raw/qcom_nandc.c | 66 +++ 1 file changed, 25 insertions(+), 41 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index d693b5f..f72bc8a 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1769,41 +1769,6 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, return parse_read_errors(host, data_buf_start, oob_buf_start); } -/* - * a helper that copies the last step/codeword of a page (containing free oob) - * into our local buffer - */ -static int copy_last_cw(struct qcom_nand_host *host, int page) -{ - struct nand_chip *chip = >chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = >ecc; - int size; - int ret; - - clear_read_regs(nandc); - - size = host->use_ecc ? host->cw_data : host->cw_size; - - /* prepare a clean read buffer */ - memset(nandc->data_buffer, 0xff, size); - - set_address(host, host->cw_size * (ecc->steps - 1), page); - update_rw_regs(host, 1, true); - - config_nand_single_cw_page_read(nandc); - - read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); - - ret = submit_descs(nandc); - if (ret) - dev_err(nandc->dev, "failed to copy last codeword\n"); - - free_descs(nandc); - - return ret; -} - /* implements ecc->read_page() */ static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) @@ -2118,6 +2083,7 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) struct nand_ecc_ctrl *ecc = >ecc; int page, ret, bbpos, bad = 0; u32 flash_status; + u8 *bbm_bytes_buf = chip->data_buf; page = (int)(ofs >> chip->page_shift) & chip->pagemask; @@ -2128,11 +2094,31 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) * that contains the BBM */ host->use_ecc = false; + bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); Are we sure there is no layout with only 1 step? All the layouts are such that, the BBM will come in first byte of spare area. For 4 bit ECC, the cw_size is 528 so for 2K page 2048 - 528 * 3 = 464 So for last CW, the 464 is BBM (i.e 2048th byte) in full page. clear_bam_transaction(nandc); - ret = copy_last_cw(host, page); - if (ret) + clear_read_regs(nandc); + + set_address(host, host->cw_size * (ecc->steps - 1), page); + update_rw_regs(host, 1, true); + + /* +* The last codeword data will be copied from NAND device to NAND +* controller internal HW buffer. Copy only required BBM size bytes +* from this HW buffer to bbm_bytes_buf which is present at +* bbpos offset. +*/ + nandc_set_read_loc(nandc, 0, bbpos, host->bbm_size, 1); + config_nand_single_cw_page_read(nandc); + read_data_dma(nandc, FLASH_BUF_ACC + bbpos, bbm_bytes_buf, + host->bbm_size, 0); + + ret = submit_descs(nandc); + free_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failed to copy bad block bytes\n"); goto err; + } flash_status = le32_to_cpu(nandc->reg_read_buf[0]); @@ -2141,12 +2127,10 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) goto err; } - bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); - - bad = nandc-&g
Re: [PATCH v3 06/16] mtd: rawnand: qcom: use the ecc strength from device parameter
On 2018-05-26 14:13, Miquel Raynal wrote: Hi Abhishek, On Fri, 25 May 2018 17:51:34 +0530, Abhishek Sahu <abs...@codeaurora.org> wrote: Currently the driver uses the ECC strength specified in DT. The QPIC/EBI2 NAND supports 4 or 8-bit ECC correction. The same kind of board can have different NAND parts so use the ECC strength from device parameters if it is not specified in DT. Signed-off-by: Abhishek Sahu <abs...@codeaurora.org> --- * Changes from v2: NONE Yes you did change things: - s/<< 2/* 4/ - updated the cwperpage location - the block handling the ecc-step-size property has been removed in a previous patch Please be careful with that, it is time consuming to review the patches all over again. Sorry Miquel for that. I Will pay more attention to this. I can understand the effort require in reviewing and how this can help in making review quicker. * Changes from v1: 1. Removed the custom logic and used the helper fuction. drivers/mtd/nand/raw/qcom_nandc.c | 29 + 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index b538390..7377923 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2315,19 +2315,39 @@ static int qcom_nand_ooblayout_free(struct mtd_info *mtd, int section, .free = qcom_nand_ooblayout_free, }; +static int +qcom_nandc_calc_ecc_bytes(int step_size, int strength) +{ + return strength == 4 ? 12 : 16; +} +NAND_ECC_CAPS_SINGLE(qcom_nandc_ecc_caps, qcom_nandc_calc_ecc_bytes, +NANDC_STEP_SIZE, 4, 8); + static int qcom_nand_host_setup(struct qcom_nand_host *host) { struct nand_chip *chip = >chip; struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_ctrl *ecc = >ecc; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - int cwperpage, bad_block_byte; + int cwperpage, bad_block_byte, ret; bool wide_bus; int ecc_mode = 1; /* controller only supports 512 bytes of data in each step */ ecc->size = NANDC_STEP_SIZE; wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; + cwperpage = mtd->writesize / NANDC_STEP_SIZE; + + /* + * Each CW has 4 available OOB bytes which will be protected with ECC +* so remaining bytes can be used for ECC. +*/ + ret = nand_ecc_choose_conf(chip, _nandc_ecc_caps, + mtd->oobsize - cwperpage * 4); Nitpick: could you add parenthesis around (cwperpage * 4) just for clarity. Thanks Miquel. I will update that. Regards, Abhishek + if (ret) { + dev_err(nandc->dev, "No valid ECC settings possible\n"); + return ret; + } if (ecc->strength >= 8) { /* 8 bit ECC defaults to BCH ECC on all platforms */ @@ -2396,7 +2416,6 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) mtd_set_ooblayout(mtd, _nand_ooblayout_ops); - cwperpage = mtd->writesize / ecc->size; nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage, cwperpage); @@ -2412,12 +2431,6 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) * for 8 bit ECC */ host->cw_size = host->cw_data + ecc->bytes; - - if (ecc->bytes * (mtd->writesize / ecc->size) > mtd->oobsize) { - dev_err(nandc->dev, "ecc data doesn't fit in OOB area\n"); - return -EINVAL; - } - bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1; host->cfg0 = (cwperpage - 1) << CW_PER_PAGE Once corrected: Acked-by: Miquel Raynal <miquel.ray...@bootlin.com> Thanks, Miquèl
Re: [PATCH v3 06/16] mtd: rawnand: qcom: use the ecc strength from device parameter
On 2018-05-26 14:13, Miquel Raynal wrote: Hi Abhishek, On Fri, 25 May 2018 17:51:34 +0530, Abhishek Sahu wrote: Currently the driver uses the ECC strength specified in DT. The QPIC/EBI2 NAND supports 4 or 8-bit ECC correction. The same kind of board can have different NAND parts so use the ECC strength from device parameters if it is not specified in DT. Signed-off-by: Abhishek Sahu --- * Changes from v2: NONE Yes you did change things: - s/<< 2/* 4/ - updated the cwperpage location - the block handling the ecc-step-size property has been removed in a previous patch Please be careful with that, it is time consuming to review the patches all over again. Sorry Miquel for that. I Will pay more attention to this. I can understand the effort require in reviewing and how this can help in making review quicker. * Changes from v1: 1. Removed the custom logic and used the helper fuction. drivers/mtd/nand/raw/qcom_nandc.c | 29 + 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index b538390..7377923 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2315,19 +2315,39 @@ static int qcom_nand_ooblayout_free(struct mtd_info *mtd, int section, .free = qcom_nand_ooblayout_free, }; +static int +qcom_nandc_calc_ecc_bytes(int step_size, int strength) +{ + return strength == 4 ? 12 : 16; +} +NAND_ECC_CAPS_SINGLE(qcom_nandc_ecc_caps, qcom_nandc_calc_ecc_bytes, +NANDC_STEP_SIZE, 4, 8); + static int qcom_nand_host_setup(struct qcom_nand_host *host) { struct nand_chip *chip = >chip; struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_ctrl *ecc = >ecc; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - int cwperpage, bad_block_byte; + int cwperpage, bad_block_byte, ret; bool wide_bus; int ecc_mode = 1; /* controller only supports 512 bytes of data in each step */ ecc->size = NANDC_STEP_SIZE; wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; + cwperpage = mtd->writesize / NANDC_STEP_SIZE; + + /* + * Each CW has 4 available OOB bytes which will be protected with ECC +* so remaining bytes can be used for ECC. +*/ + ret = nand_ecc_choose_conf(chip, _nandc_ecc_caps, + mtd->oobsize - cwperpage * 4); Nitpick: could you add parenthesis around (cwperpage * 4) just for clarity. Thanks Miquel. I will update that. Regards, Abhishek + if (ret) { + dev_err(nandc->dev, "No valid ECC settings possible\n"); + return ret; + } if (ecc->strength >= 8) { /* 8 bit ECC defaults to BCH ECC on all platforms */ @@ -2396,7 +2416,6 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) mtd_set_ooblayout(mtd, _nand_ooblayout_ops); - cwperpage = mtd->writesize / ecc->size; nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage, cwperpage); @@ -2412,12 +2431,6 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) * for 8 bit ECC */ host->cw_size = host->cw_data + ecc->bytes; - - if (ecc->bytes * (mtd->writesize / ecc->size) > mtd->oobsize) { - dev_err(nandc->dev, "ecc data doesn't fit in OOB area\n"); - return -EINVAL; - } - bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1; host->cfg0 = (cwperpage - 1) << CW_PER_PAGE Once corrected: Acked-by: Miquel Raynal Thanks, Miquèl
Re: [PATCH v3 05/16] mtd: rawnand: qcom: remove dt property nand-ecc-step-size
On 2018-05-26 14:12, Miquel Raynal wrote: Hi Abhishek, On Fri, 25 May 2018 17:51:33 +0530, Abhishek Sahu <abs...@codeaurora.org> wrote: QCOM NAND controller supports only one step size (512) so nand-ecc-step-size DT property is redundant. This property can be removed and ecc step size can be assigned with 512 value. Signed-off-by: Abhishek Sahu <abs...@codeaurora.org> --- * Changes from v2: NEW CHANGE 1. Removed the custom logic and used the helper fuction. drivers/mtd/nand/raw/qcom_nandc.c | 11 ++- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index b554fb6..b538390 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2325,15 +2325,8 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) bool wide_bus; int ecc_mode = 1; - /* -* the controller requires each step consists of 512 bytes of data. -* bail out if DT has populated a wrong step size. -*/ - if (ecc->size != NANDC_STEP_SIZE) { - dev_err(nandc->dev, "invalid ecc size\n"); - return -EINVAL; - } - + /* controller only supports 512 bytes of data in each step */ "512 bytes data steps" Thanks Miquel. Will update that. Regards, Abhishek + ecc->size = NANDC_STEP_SIZE; wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; if (ecc->strength >= 8) { Once corrected: Acked-by: Miquel Raynal <miquel.ray...@bootlin.com>
Re: [PATCH v3 05/16] mtd: rawnand: qcom: remove dt property nand-ecc-step-size
On 2018-05-26 14:12, Miquel Raynal wrote: Hi Abhishek, On Fri, 25 May 2018 17:51:33 +0530, Abhishek Sahu wrote: QCOM NAND controller supports only one step size (512) so nand-ecc-step-size DT property is redundant. This property can be removed and ecc step size can be assigned with 512 value. Signed-off-by: Abhishek Sahu --- * Changes from v2: NEW CHANGE 1. Removed the custom logic and used the helper fuction. drivers/mtd/nand/raw/qcom_nandc.c | 11 ++- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index b554fb6..b538390 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2325,15 +2325,8 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) bool wide_bus; int ecc_mode = 1; - /* -* the controller requires each step consists of 512 bytes of data. -* bail out if DT has populated a wrong step size. -*/ - if (ecc->size != NANDC_STEP_SIZE) { - dev_err(nandc->dev, "invalid ecc size\n"); - return -EINVAL; - } - + /* controller only supports 512 bytes of data in each step */ "512 bytes data steps" Thanks Miquel. Will update that. Regards, Abhishek + ecc->size = NANDC_STEP_SIZE; wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; if (ecc->strength >= 8) { Once corrected: Acked-by: Miquel Raynal
Re: [PATCH v3 03/16] dt-bindings: qcom_nandc: make nand-ecc-strength optional
On 2018-05-26 14:12, Miquel Raynal wrote: Hi Abhishek, On Fri, 25 May 2018 17:51:31 +0530, Abhishek Sahu <abs...@codeaurora.org> wrote: If nand-ecc-strength specified in DT, then controller will use this ECC strength otherwise ECC strength will be calculated according to chip requirement and available OOB size. Signed-off-by: Abhishek Sahu <abs...@codeaurora.org> --- * Changes from v2: NONE * Changes from v1: NEW PATCH Documentation/devicetree/bindings/mtd/qcom_nandc.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt index 73d336be..f246aa0 100644 --- a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt +++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt @@ -45,11 +45,13 @@ Required properties: number (e.g., 0, 1, 2, etc.) - #address-cells: see partition.txt - #size-cells: see partition.txt -- nand-ecc-strength: see nand.txt - nand-ecc-step-size: must be 512. see nand.txt for more details. I think you can squash the two dt-bindings commits as they are tightly related to each other. Sure Miquel. Earlier made one patch and then split into two. Will squash that and make single patch again :-) Thanks, Abhishek Optional properties: - nand-bus-width: see nand.txt +- nand-ecc-strength: see nand.txt. If not specified, then ECC strength will + be used according to chip requirement and available + OOB size. Each nandcs device node may optionally contain a 'partitions' sub-node, which further contains sub-nodes describing the flash partition mapping. See