* [PATCH v2 0/6] Implement setup_interface() in the DaVinci NAND controller
@ 2024-11-06 8:55 Bastien Curutchet
2024-11-06 8:55 ` [PATCH v2 1/6] memory: ti-aemif: Create aemif_set_cs_timings() Bastien Curutchet
` (5 more replies)
0 siblings, 6 replies; 13+ messages in thread
From: Bastien Curutchet @ 2024-11-06 8:55 UTC (permalink / raw)
To: Santosh Shilimkar, Krzysztof Kozlowski, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra
Cc: linux-kernel, linux-mtd, Thomas Petazzoni, Herve Codina,
Christopher Cordahi, Bastien Curutchet
Hi all,
This patch series aims to implement the setup_interface() operation in
the DaVinci NAND controller to enable the use of all ONFI modes and
improve the NAND access speed.
PATCH 2 depends on PATCH 1
PATCH 6 depends on PATCH 2-4-5
This NAND controller is present in the DaVinci (OMAP L138) and Keystone2
SoCs and functions as a 'child' of the AEMIF controller. So its timings
are set by the AEMIF controller itself from device-tree properties.
Implementing the setup_interface() callback implies being able to update
dynamically these timings, so the first two patches of the series modify
the AEMIF driver to provide its 'children' a way to modify their chip
select timing configuration.
The remaining patches implement the setup_interface() operation.
The computation of the register's contents is directly based on
§20.3.2.3 of the OMAP-L138 TRM [1]
This has been tested on two platforms based upon the DaVinci SoC. One is
interfaced with a Macronix MX30UF4G18AC NAND, the other with a Toshiba
NAND.
[1] : https://www.ti.com/lit/ug/spruh77c/spruh77c.pdf
Change log:
* v2
Cover letter :
- Add dependency details
- Remove the question about ti-aemif.h location
PATCH 1 :
- Fix aemif_cs_timings's description in the comments
PATCH 2 :
- Fix typo in the config_cs_lock's description in the comments
- Move include/memory/ti-aemif.h to include/linux/memory/ti-aemif.h
PATCH 3 [NEW] :
- Fix dependency issue with aemif controller in Kconfig
PATCH 5 :
- Add details about the clock bindings in the commit log
- Replace devm_clk_get() with devm_clk_get_enabled()
- Use dev_err_probe() to return the devm_clk_get_enabled() error
Bastien Curutchet (6):
memory: ti-aemif: Create aemif_set_cs_timings()
memory: ti-aemif: Export aemif_set_cs_timings()
mtd: rawnand: davinci: Always depends on TI_AEMIF
mtd: rawnand: davinci: Order headers alphabetically
mtd: rawnand: davinci: Add clock resource
mtd: rawnand: davinci: Implement setup_interface() operation
drivers/memory/ti-aemif.c | 102 +++++++++++++++++++---------
drivers/mtd/nand/raw/Kconfig | 4 +-
drivers/mtd/nand/raw/davinci_nand.c | 93 +++++++++++++++++++++++--
include/linux/memory/ti-aemif.h | 31 +++++++++
4 files changed, 191 insertions(+), 39 deletions(-)
create mode 100644 include/linux/memory/ti-aemif.h
--
2.47.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 1/6] memory: ti-aemif: Create aemif_set_cs_timings()
2024-11-06 8:55 [PATCH v2 0/6] Implement setup_interface() in the DaVinci NAND controller Bastien Curutchet
@ 2024-11-06 8:55 ` Bastien Curutchet
2024-11-11 19:16 ` Miquel Raynal
2024-11-06 8:55 ` [PATCH v2 2/6] memory: ti-aemif: Export aemif_set_cs_timings() Bastien Curutchet
` (4 subsequent siblings)
5 siblings, 1 reply; 13+ messages in thread
From: Bastien Curutchet @ 2024-11-06 8:55 UTC (permalink / raw)
To: Santosh Shilimkar, Krzysztof Kozlowski, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra
Cc: linux-kernel, linux-mtd, Thomas Petazzoni, Herve Codina,
Christopher Cordahi, Bastien Curutchet
Create an aemif_set_cs_timings() function to isolate the setting of a
chip select timing configuration and ease its exportation.
Move the check of the configuration validity from aemif_calc_rate() to
this new function.
Signed-off-by: Bastien Curutchet <bastien.curutchet@bootlin.com>
---
drivers/memory/ti-aemif.c | 111 ++++++++++++++++++++++++++------------
1 file changed, 78 insertions(+), 33 deletions(-)
diff --git a/drivers/memory/ti-aemif.c b/drivers/memory/ti-aemif.c
index d54dc3cfff73..8d27b2513b2c 100644
--- a/drivers/memory/ti-aemif.c
+++ b/drivers/memory/ti-aemif.c
@@ -69,15 +69,15 @@
#define ACR_SSTROBE_MASK BIT(31)
#define ASIZE_16BIT 1
-#define CONFIG_MASK (TA(TA_MAX) | \
- RHOLD(RHOLD_MAX) | \
- RSTROBE(RSTROBE_MAX) | \
- RSETUP(RSETUP_MAX) | \
- WHOLD(WHOLD_MAX) | \
- WSTROBE(WSTROBE_MAX) | \
- WSETUP(WSETUP_MAX) | \
- EW(EW_MAX) | SSTROBE(SSTROBE_MAX) | \
- ASIZE_MAX)
+#define TIMINGS_MASK (TA(TA_MAX) | \
+ RHOLD(RHOLD_MAX) | \
+ RSTROBE(RSTROBE_MAX) | \
+ RSETUP(RSETUP_MAX) | \
+ WHOLD(WHOLD_MAX) | \
+ WSTROBE(WSTROBE_MAX) | \
+ WSETUP(WSETUP_MAX))
+
+#define CONFIG_MASK (EW(EW_MAX) | SSTROBE(SSTROBE_MAX) | ASIZE_MAX)
/**
* struct aemif_cs_data: structure to hold cs parameters
@@ -107,6 +107,27 @@ struct aemif_cs_data {
u8 asize;
};
+/**
+ * struct aemif_cs_timings: structure to hold cs timing configuration
+ * values are expressed in number of clock cycles - 1
+ * @ta: minimum turn around time
+ * @rhold: read hold width
+ * @rstrobe: read strobe width
+ * @rsetup: read setup width
+ * @whold: write hold width
+ * @wstrobe: write strobe width
+ * @wsetup: write setup width
+ */
+struct aemif_cs_timings {
+ u32 ta;
+ u32 rhold;
+ u32 rstrobe;
+ u32 rsetup;
+ u32 whold;
+ u32 wstrobe;
+ u32 wsetup;
+};
+
/**
* struct aemif_device: structure to hold device data
* @base: base address of AEMIF registers
@@ -125,6 +146,44 @@ struct aemif_device {
struct aemif_cs_data cs_data[NUM_CS];
};
+/**
+ * aemif_set_cs_timings - Set the timing configuration of a given chip select.
+ * @aemif: aemif device to configure
+ * @cs: index of the chip select to configure.
+ * @timings: timings configuration to set
+ *
+ * Returns 0 on success, else negative errno.
+ */
+static int aemif_set_cs_timings(struct aemif_device *aemif, u8 cs, struct aemif_cs_timings *timings)
+{
+ unsigned int offset;
+ u32 val, set;
+
+ if (!timings || !aemif)
+ return -EINVAL;
+
+ if (cs > aemif->num_cs)
+ return -EINVAL;
+
+ if (timings->ta > TA_MAX || timings->rhold > RHOLD_MAX || timings->rstrobe > RSTROBE_MAX ||
+ timings->rsetup > RSETUP_MAX || timings->whold > WHOLD_MAX ||
+ timings->wstrobe > WSTROBE_MAX || timings->wsetup > WSETUP_MAX)
+ return -EINVAL;
+
+ set = TA(timings->ta) | RHOLD(timings->rhold) | RSTROBE(timings->rstrobe) |
+ RSETUP(timings->rsetup) | WHOLD(timings->whold) |
+ WSTROBE(timings->wstrobe) | WSETUP(timings->wsetup);
+
+ offset = A1CR_OFFSET + cs * 4;
+
+ val = readl(aemif->base + offset);
+ val &= ~TIMINGS_MASK;
+ val |= set;
+ writel(val, aemif->base + offset);
+
+ return 0;
+}
+
/**
* aemif_calc_rate - calculate timing data.
* @pdev: platform device to calculate for
@@ -149,10 +208,6 @@ static int aemif_calc_rate(struct platform_device *pdev, int wanted,
if (result < 0)
result = 0;
- /* ... But configuring tighter timings is not an option. */
- else if (result > max)
- result = -EINVAL;
-
return result;
}
@@ -174,32 +229,22 @@ static int aemif_config_abus(struct platform_device *pdev, int csnum)
{
struct aemif_device *aemif = platform_get_drvdata(pdev);
struct aemif_cs_data *data = &aemif->cs_data[csnum];
- int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup;
unsigned long clk_rate = aemif->clk_rate;
+ struct aemif_cs_timings timings;
unsigned offset;
u32 set, val;
offset = A1CR_OFFSET + (data->cs - aemif->cs_offset) * 4;
- ta = aemif_calc_rate(pdev, data->ta, clk_rate, TA_MAX);
- rhold = aemif_calc_rate(pdev, data->rhold, clk_rate, RHOLD_MAX);
- rstrobe = aemif_calc_rate(pdev, data->rstrobe, clk_rate, RSTROBE_MAX);
- rsetup = aemif_calc_rate(pdev, data->rsetup, clk_rate, RSETUP_MAX);
- whold = aemif_calc_rate(pdev, data->whold, clk_rate, WHOLD_MAX);
- wstrobe = aemif_calc_rate(pdev, data->wstrobe, clk_rate, WSTROBE_MAX);
- wsetup = aemif_calc_rate(pdev, data->wsetup, clk_rate, WSETUP_MAX);
-
- if (ta < 0 || rhold < 0 || rstrobe < 0 || rsetup < 0 ||
- whold < 0 || wstrobe < 0 || wsetup < 0) {
- dev_err(&pdev->dev, "%s: cannot get suitable timings\n",
- __func__);
- return -EINVAL;
- }
-
- set = TA(ta) | RHOLD(rhold) | RSTROBE(rstrobe) | RSETUP(rsetup) |
- WHOLD(whold) | WSTROBE(wstrobe) | WSETUP(wsetup);
+ timings.ta = aemif_calc_rate(pdev, data->ta, clk_rate, TA_MAX);
+ timings.rhold = aemif_calc_rate(pdev, data->rhold, clk_rate, RHOLD_MAX);
+ timings.rstrobe = aemif_calc_rate(pdev, data->rstrobe, clk_rate, RSTROBE_MAX);
+ timings.rsetup = aemif_calc_rate(pdev, data->rsetup, clk_rate, RSETUP_MAX);
+ timings.whold = aemif_calc_rate(pdev, data->whold, clk_rate, WHOLD_MAX);
+ timings.wstrobe = aemif_calc_rate(pdev, data->wstrobe, clk_rate, WSTROBE_MAX);
+ timings.wsetup = aemif_calc_rate(pdev, data->wsetup, clk_rate, WSETUP_MAX);
- set |= (data->asize & ACR_ASIZE_MASK);
+ set = (data->asize & ACR_ASIZE_MASK);
if (data->enable_ew)
set |= ACR_EW_MASK;
if (data->enable_ss)
@@ -210,7 +255,7 @@ static int aemif_config_abus(struct platform_device *pdev, int csnum)
val |= set;
writel(val, aemif->base + offset);
- return 0;
+ return aemif_set_cs_timings(aemif, data->cs - aemif->cs_offset, &timings);
}
static inline int aemif_cycles_to_nsec(int val, unsigned long clk_rate)
--
2.47.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 2/6] memory: ti-aemif: Export aemif_set_cs_timings()
2024-11-06 8:55 [PATCH v2 0/6] Implement setup_interface() in the DaVinci NAND controller Bastien Curutchet
2024-11-06 8:55 ` [PATCH v2 1/6] memory: ti-aemif: Create aemif_set_cs_timings() Bastien Curutchet
@ 2024-11-06 8:55 ` Bastien Curutchet
2024-11-11 19:21 ` Miquel Raynal
2024-11-06 8:55 ` [PATCH v2 3/6] mtd: rawnand: davinci: Always depends on TI_AEMIF Bastien Curutchet
` (3 subsequent siblings)
5 siblings, 1 reply; 13+ messages in thread
From: Bastien Curutchet @ 2024-11-06 8:55 UTC (permalink / raw)
To: Santosh Shilimkar, Krzysztof Kozlowski, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra
Cc: linux-kernel, linux-mtd, Thomas Petazzoni, Herve Codina,
Christopher Cordahi, Bastien Curutchet
Export the aemif_set_cs_timing() symbol so it can be used by other
drivers
Add a spinlock to protect the CS configuration register from concurrent
accesses.
Signed-off-by: Bastien Curutchet <bastien.curutchet@bootlin.com>
---
drivers/memory/ti-aemif.c | 35 ++++++++++++---------------------
include/linux/memory/ti-aemif.h | 31 +++++++++++++++++++++++++++++
2 files changed, 44 insertions(+), 22 deletions(-)
create mode 100644 include/linux/memory/ti-aemif.h
diff --git a/drivers/memory/ti-aemif.c b/drivers/memory/ti-aemif.c
index 8d27b2513b2c..4587095aa703 100644
--- a/drivers/memory/ti-aemif.c
+++ b/drivers/memory/ti-aemif.c
@@ -13,10 +13,12 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/memory/ti-aemif.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/spinlock.h>
#define TA_SHIFT 2
#define RHOLD_SHIFT 4
@@ -107,27 +109,6 @@ struct aemif_cs_data {
u8 asize;
};
-/**
- * struct aemif_cs_timings: structure to hold cs timing configuration
- * values are expressed in number of clock cycles - 1
- * @ta: minimum turn around time
- * @rhold: read hold width
- * @rstrobe: read strobe width
- * @rsetup: read setup width
- * @whold: write hold width
- * @wstrobe: write strobe width
- * @wsetup: write setup width
- */
-struct aemif_cs_timings {
- u32 ta;
- u32 rhold;
- u32 rstrobe;
- u32 rsetup;
- u32 whold;
- u32 wstrobe;
- u32 wsetup;
-};
-
/**
* struct aemif_device: structure to hold device data
* @base: base address of AEMIF registers
@@ -136,6 +117,7 @@ struct aemif_cs_timings {
* @num_cs: number of assigned chip-selects
* @cs_offset: start number of cs nodes
* @cs_data: array of chip-select settings
+ * @config_cs_lock: lock used to access CS configuration
*/
struct aemif_device {
void __iomem *base;
@@ -144,6 +126,7 @@ struct aemif_device {
u8 num_cs;
int cs_offset;
struct aemif_cs_data cs_data[NUM_CS];
+ spinlock_t config_cs_lock;
};
/**
@@ -154,8 +137,9 @@ struct aemif_device {
*
* Returns 0 on success, else negative errno.
*/
-static int aemif_set_cs_timings(struct aemif_device *aemif, u8 cs, struct aemif_cs_timings *timings)
+int aemif_set_cs_timings(struct aemif_device *aemif, u8 cs, struct aemif_cs_timings *timings)
{
+ unsigned long flags;
unsigned int offset;
u32 val, set;
@@ -176,13 +160,16 @@ static int aemif_set_cs_timings(struct aemif_device *aemif, u8 cs, struct aemif_
offset = A1CR_OFFSET + cs * 4;
+ spin_lock_irqsave(&aemif->config_cs_lock, flags);
val = readl(aemif->base + offset);
val &= ~TIMINGS_MASK;
val |= set;
writel(val, aemif->base + offset);
+ spin_unlock_irqrestore(&aemif->config_cs_lock, flags);
return 0;
}
+EXPORT_SYMBOL(aemif_set_cs_timings);
/**
* aemif_calc_rate - calculate timing data.
@@ -231,6 +218,7 @@ static int aemif_config_abus(struct platform_device *pdev, int csnum)
struct aemif_cs_data *data = &aemif->cs_data[csnum];
unsigned long clk_rate = aemif->clk_rate;
struct aemif_cs_timings timings;
+ unsigned long flags;
unsigned offset;
u32 set, val;
@@ -250,10 +238,12 @@ static int aemif_config_abus(struct platform_device *pdev, int csnum)
if (data->enable_ss)
set |= ACR_SSTROBE_MASK;
+ spin_lock_irqsave(&aemif->config_cs_lock, flags);
val = readl(aemif->base + offset);
val &= ~CONFIG_MASK;
val |= set;
writel(val, aemif->base + offset);
+ spin_unlock_irqrestore(&aemif->config_cs_lock, flags);
return aemif_set_cs_timings(aemif, data->cs - aemif->cs_offset, &timings);
}
@@ -396,6 +386,7 @@ static int aemif_probe(struct platform_device *pdev)
if (IS_ERR(aemif->base))
return PTR_ERR(aemif->base);
+ spin_lock_init(&aemif->config_cs_lock);
if (np) {
/*
* For every controller device node, there is a cs device node
diff --git a/include/linux/memory/ti-aemif.h b/include/linux/memory/ti-aemif.h
new file mode 100644
index 000000000000..809f0a68605a
--- /dev/null
+++ b/include/linux/memory/ti-aemif.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __TI_AEMIF_H
+#define __TI_AEMIF_H
+
+/**
+ * struct aemif_cs_timings: structure to hold cs timing configuration
+ * values are expressed in number of clock cycles - 1
+ * @ta: minimum turn around time
+ * @rhold: read hold width
+ * @rstrobe: read strobe width
+ * @rsetup: read setup width
+ * @whold: write hold width
+ * @wstrobe: write strobe width
+ * @wsetup: write setup width
+ */
+struct aemif_cs_timings {
+ u32 ta;
+ u32 rhold;
+ u32 rstrobe;
+ u32 rsetup;
+ u32 whold;
+ u32 wstrobe;
+ u32 wsetup;
+};
+
+struct aemif_device;
+
+int aemif_set_cs_timings(struct aemif_device *aemif, u8 cs, struct aemif_cs_timings *timings);
+
+#endif // __TI_AEMIF_H
--
2.47.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 3/6] mtd: rawnand: davinci: Always depends on TI_AEMIF
2024-11-06 8:55 [PATCH v2 0/6] Implement setup_interface() in the DaVinci NAND controller Bastien Curutchet
2024-11-06 8:55 ` [PATCH v2 1/6] memory: ti-aemif: Create aemif_set_cs_timings() Bastien Curutchet
2024-11-06 8:55 ` [PATCH v2 2/6] memory: ti-aemif: Export aemif_set_cs_timings() Bastien Curutchet
@ 2024-11-06 8:55 ` Bastien Curutchet
2024-11-06 8:55 ` [PATCH v2 4/6] mtd: rawnand: davinci: Order headers alphabetically Bastien Curutchet
` (2 subsequent siblings)
5 siblings, 0 replies; 13+ messages in thread
From: Bastien Curutchet @ 2024-11-06 8:55 UTC (permalink / raw)
To: Santosh Shilimkar, Krzysztof Kozlowski, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra
Cc: linux-kernel, linux-mtd, Thomas Petazzoni, Herve Codina,
Christopher Cordahi, Bastien Curutchet, kernel test robot
DAVINCI_NAND depends on TI_AEMIF only when ARCH_KEYSTONE is selected
while the NAND controller is also always a part of the AEMIF controller
on DaVinci SoCs.
Set a dependency on TI_AEMIF regardless of the selected architecture.
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202411020140.3wsKJOSB-lkp@intel.com/
Closes: https://lore.kernel.org/oe-kbuild-all/202411020957.X1T8T9ZR-lkp@intel.com/
Signed-off-by: Bastien Curutchet <bastien.curutchet@bootlin.com>
---
drivers/mtd/nand/raw/Kconfig | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index d0aaccf72d78..bb61434347bd 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -279,8 +279,8 @@ config MTD_NAND_SH_FLCTL
config MTD_NAND_DAVINCI
tristate "DaVinci/Keystone NAND controller"
- depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF) || COMPILE_TEST
- depends on HAS_IOMEM
+ depends on COMPILE_TEST || ARCH_DAVINCI || ARCH_KEYSTONE
+ depends on HAS_IOMEM && TI_AEMIF
help
Enable the driver for NAND flash chips on Texas Instruments
DaVinci/Keystone processors.
--
2.47.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 4/6] mtd: rawnand: davinci: Order headers alphabetically
2024-11-06 8:55 [PATCH v2 0/6] Implement setup_interface() in the DaVinci NAND controller Bastien Curutchet
` (2 preceding siblings ...)
2024-11-06 8:55 ` [PATCH v2 3/6] mtd: rawnand: davinci: Always depends on TI_AEMIF Bastien Curutchet
@ 2024-11-06 8:55 ` Bastien Curutchet
2024-11-06 8:55 ` [PATCH v2 5/6] mtd: rawnand: davinci: Add clock resource Bastien Curutchet
2024-11-06 8:55 ` [PATCH v2 6/6] mtd: rawnand: davinci: Implement setup_interface() operation Bastien Curutchet
5 siblings, 0 replies; 13+ messages in thread
From: Bastien Curutchet @ 2024-11-06 8:55 UTC (permalink / raw)
To: Santosh Shilimkar, Krzysztof Kozlowski, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra
Cc: linux-kernel, linux-mtd, Thomas Petazzoni, Herve Codina,
Christopher Cordahi, Bastien Curutchet
Order headers alphabetically for better readability.
Signed-off-by: Bastien Curutchet <bastien.curutchet@bootlin.com>
---
drivers/mtd/nand/raw/davinci_nand.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c
index 392678143a36..3c0efbdd789e 100644
--- a/drivers/mtd/nand/raw/davinci_nand.c
+++ b/drivers/mtd/nand/raw/davinci_nand.c
@@ -10,15 +10,15 @@
* Dirk Behme <Dirk.Behme@gmail.com>
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
-#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
#define NRCSR_OFFSET 0x00
#define NANDFCR_OFFSET 0x60
--
2.47.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 5/6] mtd: rawnand: davinci: Add clock resource
2024-11-06 8:55 [PATCH v2 0/6] Implement setup_interface() in the DaVinci NAND controller Bastien Curutchet
` (3 preceding siblings ...)
2024-11-06 8:55 ` [PATCH v2 4/6] mtd: rawnand: davinci: Order headers alphabetically Bastien Curutchet
@ 2024-11-06 8:55 ` Bastien Curutchet
2024-11-06 8:55 ` [PATCH v2 6/6] mtd: rawnand: davinci: Implement setup_interface() operation Bastien Curutchet
5 siblings, 0 replies; 13+ messages in thread
From: Bastien Curutchet @ 2024-11-06 8:55 UTC (permalink / raw)
To: Santosh Shilimkar, Krzysztof Kozlowski, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra
Cc: linux-kernel, linux-mtd, Thomas Petazzoni, Herve Codina,
Christopher Cordahi, Bastien Curutchet
NAND controller has a reference clock inherited from the AEMIF
(cf. Documentation/devicetree/bindings/memory-controllers/ti-aemif.txt)
This clock isn't used yet by the driver.
Add a struct clock in the struct davinci_nand_info so it can be used
to compute timings.
Signed-off-by: Bastien Curutchet <bastien.curutchet@bootlin.com>
---
drivers/mtd/nand/raw/davinci_nand.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c
index 3c0efbdd789e..563045c7ce08 100644
--- a/drivers/mtd/nand/raw/davinci_nand.c
+++ b/drivers/mtd/nand/raw/davinci_nand.c
@@ -10,6 +10,7 @@
* Dirk Behme <Dirk.Behme@gmail.com>
*/
+#include <linux/clk.h>
#include <linux/err.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
@@ -117,6 +118,8 @@ struct davinci_nand_info {
uint32_t mask_cle;
uint32_t core_chipsel;
+
+ struct clk *clk;
};
static DEFINE_SPINLOCK(davinci_nand_lock);
@@ -822,6 +825,10 @@ static int nand_davinci_probe(struct platform_device *pdev)
return -EADDRNOTAVAIL;
}
+ info->clk = devm_clk_get_enabled(&pdev->dev, "aemif");
+ if (IS_ERR(info->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(info->clk), "failed to get clock");
+
info->pdev = pdev;
info->base = base;
info->vaddr = vaddr;
--
2.47.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 6/6] mtd: rawnand: davinci: Implement setup_interface() operation
2024-11-06 8:55 [PATCH v2 0/6] Implement setup_interface() in the DaVinci NAND controller Bastien Curutchet
` (4 preceding siblings ...)
2024-11-06 8:55 ` [PATCH v2 5/6] mtd: rawnand: davinci: Add clock resource Bastien Curutchet
@ 2024-11-06 8:55 ` Bastien Curutchet
2024-11-11 19:32 ` Miquel Raynal
5 siblings, 1 reply; 13+ messages in thread
From: Bastien Curutchet @ 2024-11-06 8:55 UTC (permalink / raw)
To: Santosh Shilimkar, Krzysztof Kozlowski, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra
Cc: linux-kernel, linux-mtd, Thomas Petazzoni, Herve Codina,
Christopher Cordahi, Bastien Curutchet
The setup_interface() operation isn't implemented. It forces the driver
to use the ONFI mode 0, though it could use more optimal modes.
Implement the setup_interface() operation. It uses the
aemif_set_cs_timings() function from the AEMIF driver to update the
chip select timings. The calculation of the register's contents is
directly extracted from §20.3.2.3 of the DaVinci TRM [1]
These timings are previously set by the AEMIF driver itself from
device-tree properties. Therefore, IMHO, failing to update them in the
setup_interface() isn't critical, which is why 0 is returned even when
timings aren't updated.
MAX_TH_PS and MAX_TSU_PS are the worst case timings based on the
Keystone2 and DaVinci datasheets.
[1] : https://www.ti.com/lit/ug/spruh77c/spruh77c.pdf
Signed-off-by: Bastien Curutchet <bastien.curutchet@bootlin.com>
---
drivers/mtd/nand/raw/davinci_nand.c | 78 +++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c
index 563045c7ce08..2d0c564c8d17 100644
--- a/drivers/mtd/nand/raw/davinci_nand.c
+++ b/drivers/mtd/nand/raw/davinci_nand.c
@@ -14,6 +14,7 @@
#include <linux/err.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
+#include <linux/memory/ti-aemif.h>
#include <linux/module.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
@@ -44,6 +45,9 @@
#define MASK_ALE 0x08
#define MASK_CLE 0x10
+#define MAX_TSU_PS 3000 /* Input setup time in ps */
+#define MAX_TH_PS 1600 /* Input hold time in ps */
+
struct davinci_nand_pdata {
uint32_t mask_ale;
uint32_t mask_cle;
@@ -120,6 +124,7 @@ struct davinci_nand_info {
uint32_t core_chipsel;
struct clk *clk;
+ struct aemif_device *aemif;
};
static DEFINE_SPINLOCK(davinci_nand_lock);
@@ -767,9 +772,81 @@ static int davinci_nand_exec_op(struct nand_chip *chip,
return 0;
}
+#define TO_CYCLES(ps, period_ns) (DIV_ROUND_UP((ps) / 1000, (period_ns)))
+
+static int davinci_nand_setup_interface(struct nand_chip *chip, int chipnr,
+ const struct nand_interface_config *conf)
+{
+ struct davinci_nand_info *info = to_davinci_nand(nand_to_mtd(chip));
+ const struct nand_sdr_timings *sdr;
+ struct aemif_cs_timings timings;
+ s32 cfg, min, cyc_ns;
+
+ cyc_ns = 1000000000 / clk_get_rate(info->clk);
+
+ sdr = nand_get_sdr_timings(conf);
+ if (IS_ERR(sdr))
+ return PTR_ERR(sdr);
+
+ cfg = TO_CYCLES(sdr->tCLR_min, cyc_ns) - 1;
+ timings.rsetup = cfg > 0 ? cfg : 0;
+
+ cfg = max_t(s32, TO_CYCLES(sdr->tREA_max + MAX_TSU_PS, cyc_ns),
+ TO_CYCLES(sdr->tRP_min, cyc_ns)) - 1;
+ timings.rstrobe = cfg > 0 ? cfg : 0;
+
+ min = TO_CYCLES(sdr->tCEA_max + MAX_TSU_PS, cyc_ns) - 2;
+ while ((s32)(timings.rsetup + timings.rstrobe) < min)
+ timings.rstrobe++;
+
+ cfg = TO_CYCLES((s32)(MAX_TH_PS - sdr->tCHZ_max), cyc_ns) - 1;
+ timings.rhold = cfg > 0 ? cfg : 0;
+
+ min = TO_CYCLES(sdr->tRC_min, cyc_ns) - 3;
+ while ((s32)(timings.rsetup + timings.rstrobe + timings.rhold) < min)
+ timings.rhold++;
+
+ cfg = TO_CYCLES((s32)(sdr->tRHZ_max - (timings.rhold + 1) * cyc_ns * 1000), cyc_ns);
+ cfg = max_t(s32, cfg, TO_CYCLES(sdr->tCHZ_max, cyc_ns)) - 1;
+ timings.ta = cfg > 0 ? cfg : 0;
+
+ cfg = TO_CYCLES(sdr->tWP_min, cyc_ns) - 1;
+ timings.wstrobe = cfg > 0 ? cfg : 0;
+
+ cfg = max_t(s32, TO_CYCLES(sdr->tCLS_min, cyc_ns), TO_CYCLES(sdr->tALS_min, cyc_ns));
+ cfg = max_t(s32, cfg, TO_CYCLES(sdr->tCS_min, cyc_ns)) - 1;
+ timings.wsetup = cfg > 0 ? cfg : 0;
+
+ min = TO_CYCLES(sdr->tDS_min, cyc_ns) - 2;
+ while ((s32)(timings.wsetup + timings.wstrobe) < min)
+ timings.wstrobe++;
+
+ cfg = max_t(s32, TO_CYCLES(sdr->tCLH_min, cyc_ns), TO_CYCLES(sdr->tALH_min, cyc_ns));
+ cfg = max_t(s32, cfg, TO_CYCLES(sdr->tCH_min, cyc_ns));
+ cfg = max_t(s32, cfg, TO_CYCLES(sdr->tDH_min, cyc_ns)) - 1;
+ timings.whold = cfg > 0 ? cfg : 0;
+
+ min = TO_CYCLES(sdr->tWC_min, cyc_ns) - 2;
+ while ((s32)(timings.wsetup + timings.wstrobe + timings.whold) < min)
+ timings.whold++;
+
+ dev_dbg(&info->pdev->dev, "RSETUP %x RSTROBE %x RHOLD %x\n",
+ timings.rsetup, timings.rstrobe, timings.rhold);
+ dev_dbg(&info->pdev->dev, "TA %x\n", timings.ta);
+ dev_dbg(&info->pdev->dev, "WSETUP %x WSTROBE %x WHOLD %x\n",
+ timings.wsetup, timings.wstrobe, timings.whold);
+
+ if (aemif_set_cs_timings(info->aemif, info->core_chipsel, &timings) < 0)
+ dev_info(&info->pdev->dev,
+ "Failed to dynamically update the CS timings, keep them unchanged");
+
+ return 0;
+}
+
static const struct nand_controller_ops davinci_nand_controller_ops = {
.attach_chip = davinci_nand_attach_chip,
.exec_op = davinci_nand_exec_op,
+ .setup_interface = davinci_nand_setup_interface,
};
static int nand_davinci_probe(struct platform_device *pdev)
@@ -832,6 +909,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
info->pdev = pdev;
info->base = base;
info->vaddr = vaddr;
+ info->aemif = dev_get_drvdata(pdev->dev.parent);
mtd = nand_to_mtd(&info->chip);
mtd->dev.parent = &pdev->dev;
--
2.47.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH v2 1/6] memory: ti-aemif: Create aemif_set_cs_timings()
2024-11-06 8:55 ` [PATCH v2 1/6] memory: ti-aemif: Create aemif_set_cs_timings() Bastien Curutchet
@ 2024-11-11 19:16 ` Miquel Raynal
0 siblings, 0 replies; 13+ messages in thread
From: Miquel Raynal @ 2024-11-11 19:16 UTC (permalink / raw)
To: Bastien Curutchet
Cc: Santosh Shilimkar, Krzysztof Kozlowski, Richard Weinberger,
Vignesh Raghavendra, linux-kernel, linux-mtd, Thomas Petazzoni,
Herve Codina, Christopher Cordahi
Hi Bastien,
2 very minor nits
> +/**
> + * struct aemif_cs_timings: structure to hold cs timing configuration
Nitpick: s/cs/CS/ in plain english?
...
> +/**
> + * aemif_set_cs_timings - Set the timing configuration of a given chip select.
> + * @aemif: aemif device to configure
> + * @cs: index of the chip select to configure.
Nit: Spurious dot ^
...
> - /* ... But configuring tighter timings is not an option. */
> - else if (result > max)
> - result = -EINVAL;
> -
Maybe this is too early to remove the check? If someone bisects and
falls in-between the patches of your series, NAND support would be
broken I guess?
With this fixed, feel free to add my:
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
Thanks,
Miquèl
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 2/6] memory: ti-aemif: Export aemif_set_cs_timings()
2024-11-06 8:55 ` [PATCH v2 2/6] memory: ti-aemif: Export aemif_set_cs_timings() Bastien Curutchet
@ 2024-11-11 19:21 ` Miquel Raynal
2024-11-12 9:13 ` Bastien Curutchet
0 siblings, 1 reply; 13+ messages in thread
From: Miquel Raynal @ 2024-11-11 19:21 UTC (permalink / raw)
To: Bastien Curutchet
Cc: Santosh Shilimkar, Krzysztof Kozlowski, Richard Weinberger,
Vignesh Raghavendra, linux-kernel, linux-mtd, Thomas Petazzoni,
Herve Codina, Christopher Cordahi
Hello Bastien,
On 06/11/2024 at 09:55:03 +01, Bastien Curutchet <bastien.curutchet@bootlin.com> wrote:
> Export the aemif_set_cs_timing() symbol so it can be used by other
> drivers
>
> Add a spinlock to protect the CS configuration register from concurrent
> accesses.
What concurrent accesses are you trying to protect yourself against?
I fail to see the use case, but TBH I haven't tried hard enough maybe.
Also, what justifies the use of a spin-lock in this case?
Thanks,
Miquèl
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 6/6] mtd: rawnand: davinci: Implement setup_interface() operation
2024-11-06 8:55 ` [PATCH v2 6/6] mtd: rawnand: davinci: Implement setup_interface() operation Bastien Curutchet
@ 2024-11-11 19:32 ` Miquel Raynal
2024-11-12 12:53 ` Bastien Curutchet
0 siblings, 1 reply; 13+ messages in thread
From: Miquel Raynal @ 2024-11-11 19:32 UTC (permalink / raw)
To: Bastien Curutchet
Cc: Santosh Shilimkar, Krzysztof Kozlowski, Richard Weinberger,
Vignesh Raghavendra, linux-kernel, linux-mtd, Thomas Petazzoni,
Herve Codina, Christopher Cordahi
Hi Bastien,
On 06/11/2024 at 09:55:07 +01, Bastien Curutchet <bastien.curutchet@bootlin.com> wrote:
> The setup_interface() operation isn't implemented. It forces the driver
> to use the ONFI mode 0, though it could use more optimal modes.
>
> Implement the setup_interface() operation. It uses the
> aemif_set_cs_timings() function from the AEMIF driver to update the
> chip select timings. The calculation of the register's contents is
> directly extracted from §20.3.2.3 of the DaVinci TRM [1]
>
> These timings are previously set by the AEMIF driver itself from
> device-tree properties. Therefore, IMHO, failing to update them in the
> setup_interface() isn't critical, which is why 0 is returned even when
> timings aren't updated.
Did you experience failures? Because I'd be more conservative and error
out loudly when something is wrong. In general it is a safest approach
on the long term. Here maybe you have good reasons to do differently, in
this case I am all ears.
> MAX_TH_PS and MAX_TSU_PS are the worst case timings based on the
> Keystone2 and DaVinci datasheets.
>
> [1] : https://www.ti.com/lit/ug/spruh77c/spruh77c.pdf
>
> Signed-off-by: Bastien Curutchet <bastien.curutchet@bootlin.com>
> ---
> drivers/mtd/nand/raw/davinci_nand.c | 78 +++++++++++++++++++++++++++++
> 1 file changed, 78 insertions(+)
>
> diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c
> index 563045c7ce08..2d0c564c8d17 100644
> --- a/drivers/mtd/nand/raw/davinci_nand.c
> +++ b/drivers/mtd/nand/raw/davinci_nand.c
> @@ -14,6 +14,7 @@
> #include <linux/err.h>
> #include <linux/iopoll.h>
> #include <linux/kernel.h>
> +#include <linux/memory/ti-aemif.h>
> #include <linux/module.h>
> #include <linux/mtd/rawnand.h>
> #include <linux/mtd/partitions.h>
> @@ -44,6 +45,9 @@
> #define MASK_ALE 0x08
> #define MASK_CLE 0x10
>
> +#define MAX_TSU_PS 3000 /* Input setup time in ps */
> +#define MAX_TH_PS 1600 /* Input hold time in ps */
> +
> struct davinci_nand_pdata {
> uint32_t mask_ale;
> uint32_t mask_cle;
> @@ -120,6 +124,7 @@ struct davinci_nand_info {
> uint32_t core_chipsel;
>
> struct clk *clk;
> + struct aemif_device *aemif;
> };
>
> static DEFINE_SPINLOCK(davinci_nand_lock);
> @@ -767,9 +772,81 @@ static int davinci_nand_exec_op(struct nand_chip *chip,
> return 0;
> }
>
> +#define TO_CYCLES(ps, period_ns) (DIV_ROUND_UP((ps) / 1000, (period_ns)))
> +
> +static int davinci_nand_setup_interface(struct nand_chip *chip, int chipnr,
> + const struct nand_interface_config *conf)
> +{
> + struct davinci_nand_info *info = to_davinci_nand(nand_to_mtd(chip));
> + const struct nand_sdr_timings *sdr;
> + struct aemif_cs_timings timings;
> + s32 cfg, min, cyc_ns;
> +
> + cyc_ns = 1000000000 / clk_get_rate(info->clk);
> +
> + sdr = nand_get_sdr_timings(conf);
> + if (IS_ERR(sdr))
> + return PTR_ERR(sdr);
> +
> + cfg = TO_CYCLES(sdr->tCLR_min, cyc_ns) - 1;
> + timings.rsetup = cfg > 0 ? cfg : 0;
> +
> + cfg = max_t(s32, TO_CYCLES(sdr->tREA_max + MAX_TSU_PS, cyc_ns),
> + TO_CYCLES(sdr->tRP_min, cyc_ns)) - 1;
> + timings.rstrobe = cfg > 0 ? cfg : 0;
> +
> + min = TO_CYCLES(sdr->tCEA_max + MAX_TSU_PS, cyc_ns) - 2;
> + while ((s32)(timings.rsetup + timings.rstrobe) < min)
> + timings.rstrobe++;
I see a lot of while loops which just stop if you reach a min/max, I
believe a slightly more robust approach should be attempted, and
returning errors when these limits are crossed would be probably
beneficial on the long term?
> +
> + cfg = TO_CYCLES((s32)(MAX_TH_PS - sdr->tCHZ_max), cyc_ns) - 1;
> + timings.rhold = cfg > 0 ? cfg : 0;
> +
> + min = TO_CYCLES(sdr->tRC_min, cyc_ns) - 3;
> + while ((s32)(timings.rsetup + timings.rstrobe + timings.rhold) < min)
> + timings.rhold++;
> +
> + cfg = TO_CYCLES((s32)(sdr->tRHZ_max - (timings.rhold + 1) * cyc_ns * 1000), cyc_ns);
> + cfg = max_t(s32, cfg, TO_CYCLES(sdr->tCHZ_max, cyc_ns)) - 1;
> + timings.ta = cfg > 0 ? cfg : 0;
> +
> + cfg = TO_CYCLES(sdr->tWP_min, cyc_ns) - 1;
> + timings.wstrobe = cfg > 0 ? cfg : 0;
> +
> + cfg = max_t(s32, TO_CYCLES(sdr->tCLS_min, cyc_ns), TO_CYCLES(sdr->tALS_min, cyc_ns));
> + cfg = max_t(s32, cfg, TO_CYCLES(sdr->tCS_min, cyc_ns)) - 1;
> + timings.wsetup = cfg > 0 ? cfg : 0;
> +
> + min = TO_CYCLES(sdr->tDS_min, cyc_ns) - 2;
> + while ((s32)(timings.wsetup + timings.wstrobe) < min)
> + timings.wstrobe++;
> +
> + cfg = max_t(s32, TO_CYCLES(sdr->tCLH_min, cyc_ns), TO_CYCLES(sdr->tALH_min, cyc_ns));
> + cfg = max_t(s32, cfg, TO_CYCLES(sdr->tCH_min, cyc_ns));
> + cfg = max_t(s32, cfg, TO_CYCLES(sdr->tDH_min, cyc_ns)) - 1;
> + timings.whold = cfg > 0 ? cfg : 0;
> +
> + min = TO_CYCLES(sdr->tWC_min, cyc_ns) - 2;
> + while ((s32)(timings.wsetup + timings.wstrobe + timings.whold) < min)
> + timings.whold++;
> +
> + dev_dbg(&info->pdev->dev, "RSETUP %x RSTROBE %x RHOLD %x\n",
> + timings.rsetup, timings.rstrobe, timings.rhold);
> + dev_dbg(&info->pdev->dev, "TA %x\n", timings.ta);
> + dev_dbg(&info->pdev->dev, "WSETUP %x WSTROBE %x WHOLD %x\n",
> + timings.wsetup, timings.wstrobe, timings.whold);
> +
Here you probably want to exit in the NAND_DATA_IFACE_CHECK_ONLY case.
> + if (aemif_set_cs_timings(info->aemif, info->core_chipsel, &timings) < 0)
> + dev_info(&info->pdev->dev,
> + "Failed to dynamically update the CS timings, keep them unchanged");
> +
> + return 0;
> +}
> +
Thanks,
Miquèl
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 2/6] memory: ti-aemif: Export aemif_set_cs_timings()
2024-11-11 19:21 ` Miquel Raynal
@ 2024-11-12 9:13 ` Bastien Curutchet
2024-11-12 11:17 ` Miquel Raynal
0 siblings, 1 reply; 13+ messages in thread
From: Bastien Curutchet @ 2024-11-12 9:13 UTC (permalink / raw)
To: Miquel Raynal
Cc: Santosh Shilimkar, Krzysztof Kozlowski, Richard Weinberger,
Vignesh Raghavendra, linux-kernel, linux-mtd, Thomas Petazzoni,
Herve Codina, Christopher Cordahi
Hi Miquèl,
On 11/11/24 8:21 PM, Miquel Raynal wrote:
>
> Hello Bastien,
>
> On 06/11/2024 at 09:55:03 +01, Bastien Curutchet <bastien.curutchet@bootlin.com> wrote:
>
>> Export the aemif_set_cs_timing() symbol so it can be used by other
>> drivers
>>
>> Add a spinlock to protect the CS configuration register from concurrent
>> accesses.
>
> What concurrent accesses are you trying to protect yourself against?
> I fail to see the use case, but TBH I haven't tried hard enough maybe.
>
The register that handles the CS configuration belongs to the AEMIF
component but it will also be accessed by the AEMIF 'children' with the
aemif_set_cs_timing() function. So far, concurrent accesses shouldn't
occur because the AEMIF configures the CS timings only once and does so
before probing its 'children'; but I don't know how things can evolve in
the future.
> Also, what justifies the use of a spin-lock in this case?
>
TBH, I always struggle to choose between mutexes and spin-locks. I chose
spin-lock because it only protects one memory-mapped register so I think
it doesn't worth going to sleep when waiting for the lock.
Best regards,
Bastien
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 2/6] memory: ti-aemif: Export aemif_set_cs_timings()
2024-11-12 9:13 ` Bastien Curutchet
@ 2024-11-12 11:17 ` Miquel Raynal
0 siblings, 0 replies; 13+ messages in thread
From: Miquel Raynal @ 2024-11-12 11:17 UTC (permalink / raw)
To: Bastien Curutchet
Cc: Santosh Shilimkar, Krzysztof Kozlowski, Richard Weinberger,
Vignesh Raghavendra, linux-kernel, linux-mtd, Thomas Petazzoni,
Herve Codina, Christopher Cordahi
Hi Bastien,
On 12/11/2024 at 10:13:38 +01, Bastien Curutchet <bastien.curutchet@bootlin.com> wrote:
> Hi Miquèl,
>
> On 11/11/24 8:21 PM, Miquel Raynal wrote:
>> Hello Bastien,
>> On 06/11/2024 at 09:55:03 +01, Bastien Curutchet
>> <bastien.curutchet@bootlin.com> wrote:
>>
>>> Export the aemif_set_cs_timing() symbol so it can be used by other
>>> drivers
>>>
>>> Add a spinlock to protect the CS configuration register from concurrent
>>> accesses.
>> What concurrent accesses are you trying to protect yourself against?
>> I fail to see the use case, but TBH I haven't tried hard enough maybe.
>>
>
> The register that handles the CS configuration belongs to the AEMIF
> component but it will also be accessed by the AEMIF 'children' with the
> aemif_set_cs_timing() function. So far, concurrent accesses shouldn't
> occur because the AEMIF configures the CS timings only once and does so
> before probing its 'children'; but I don't know how things can evolve in
> the future.
Okay, and I guess we can have more than one children, in this case
indeed we need serialization.
>> Also, what justifies the use of a spin-lock in this case?
>>
>
> TBH, I always struggle to choose between mutexes and spin-locks. I chose
> spin-lock because it only protects one memory-mapped register so I think
> it doesn't worth going to sleep when waiting for the lock.
In general, I believe in a path that is not a hot-path and if you can
sleep, you should go for a mutex.
Cheers,
Miquèl
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 6/6] mtd: rawnand: davinci: Implement setup_interface() operation
2024-11-11 19:32 ` Miquel Raynal
@ 2024-11-12 12:53 ` Bastien Curutchet
0 siblings, 0 replies; 13+ messages in thread
From: Bastien Curutchet @ 2024-11-12 12:53 UTC (permalink / raw)
To: Miquel Raynal, bastien.curutchet
Cc: Santosh Shilimkar, Krzysztof Kozlowski, Richard Weinberger,
Vignesh Raghavendra, linux-kernel, linux-mtd, Thomas Petazzoni,
Herve Codina, Christopher Cordahi
Hi Miquèl,
On 11/11/24 8:32 PM, Miquel Raynal wrote:
> Hi Bastien,
>
> On 06/11/2024 at 09:55:07 +01, Bastien Curutchet <bastien.curutchet@bootlin.com> wrote:
>
>> The setup_interface() operation isn't implemented. It forces the driver
>> to use the ONFI mode 0, though it could use more optimal modes.
>>
>> Implement the setup_interface() operation. It uses the
>> aemif_set_cs_timings() function from the AEMIF driver to update the
>> chip select timings. The calculation of the register's contents is
>> directly extracted from §20.3.2.3 of the DaVinci TRM [1]
>>
>> These timings are previously set by the AEMIF driver itself from
>> device-tree properties. Therefore, IMHO, failing to update them in the
>> setup_interface() isn't critical, which is why 0 is returned even when
>> timings aren't updated.
>
> Did you experience failures? Because I'd be more conservative and error
> out loudly when something is wrong. In general it is a safest approach
> on the long term. Here maybe you have good reasons to do differently, in
> this case I am all ears.
>
The DaVinci's configuration isn't very wide so if its reference clock
rate is too high, mode 0 timings can be too slow to fit while higher
modes will fit. So returning -EINVAL here will cause the probe to fail
with 'Failed to configure data interface to SDR timing mode 0' while
higher modes would have worked.
>> MAX_TH_PS and MAX_TSU_PS are the worst case timings based on the
>> Keystone2 and DaVinci datasheets.
>>
>> [1] : https://www.ti.com/lit/ug/spruh77c/spruh77c.pdf
>>
>> Signed-off-by: Bastien Curutchet <bastien.curutchet@bootlin.com>
>> ---
>> drivers/mtd/nand/raw/davinci_nand.c | 78 +++++++++++++++++++++++++++++
>> 1 file changed, 78 insertions(+)
>>
>> diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c
>> index 563045c7ce08..2d0c564c8d17 100644
>> --- a/drivers/mtd/nand/raw/davinci_nand.c
>> +++ b/drivers/mtd/nand/raw/davinci_nand.c
>> @@ -14,6 +14,7 @@
>> #include <linux/err.h>
>> #include <linux/iopoll.h>
>> #include <linux/kernel.h>
>> +#include <linux/memory/ti-aemif.h>
>> #include <linux/module.h>
>> #include <linux/mtd/rawnand.h>
>> #include <linux/mtd/partitions.h>
>> @@ -44,6 +45,9 @@
>> #define MASK_ALE 0x08
>> #define MASK_CLE 0x10
>>
>> +#define MAX_TSU_PS 3000 /* Input setup time in ps */
>> +#define MAX_TH_PS 1600 /* Input hold time in ps */
>> +
>> struct davinci_nand_pdata {
>> uint32_t mask_ale;
>> uint32_t mask_cle;
>> @@ -120,6 +124,7 @@ struct davinci_nand_info {
>> uint32_t core_chipsel;
>>
>> struct clk *clk;
>> + struct aemif_device *aemif;
>> };
>>
>> static DEFINE_SPINLOCK(davinci_nand_lock);
>> @@ -767,9 +772,81 @@ static int davinci_nand_exec_op(struct nand_chip *chip,
>> return 0;
>> }
>>
>> +#define TO_CYCLES(ps, period_ns) (DIV_ROUND_UP((ps) / 1000, (period_ns)))
>> +
>> +static int davinci_nand_setup_interface(struct nand_chip *chip, int chipnr,
>> + const struct nand_interface_config *conf)
>> +{
>> + struct davinci_nand_info *info = to_davinci_nand(nand_to_mtd(chip));
>> + const struct nand_sdr_timings *sdr;
>> + struct aemif_cs_timings timings;
>> + s32 cfg, min, cyc_ns;
>> +
>> + cyc_ns = 1000000000 / clk_get_rate(info->clk);
>> +
>> + sdr = nand_get_sdr_timings(conf);
>> + if (IS_ERR(sdr))
>> + return PTR_ERR(sdr);
>> +
>> + cfg = TO_CYCLES(sdr->tCLR_min, cyc_ns) - 1;
>> + timings.rsetup = cfg > 0 ? cfg : 0;
>> +
>> + cfg = max_t(s32, TO_CYCLES(sdr->tREA_max + MAX_TSU_PS, cyc_ns),
>> + TO_CYCLES(sdr->tRP_min, cyc_ns)) - 1;
>> + timings.rstrobe = cfg > 0 ? cfg : 0;
>> +
>> + min = TO_CYCLES(sdr->tCEA_max + MAX_TSU_PS, cyc_ns) - 2;
>> + while ((s32)(timings.rsetup + timings.rstrobe) < min)
>> + timings.rstrobe++;
>
> I see a lot of while loops which just stop if you reach a min/max, I
> believe a slightly more robust approach should be attempted, and
> returning errors when these limits are crossed would be probably
> beneficial on the long term?
>
This comes from the DaVinci NAND controller's documentation (cf p908 of
https://www.ti.com/lit/ug/spruh77c/spruh77c.pdf). A first formula, gives
the RSETUP timing, a second one the RSTROBE timing and then a third
formula gives a constraint that has to be met by the sum of RSETUP and
RSTROBE.
Then the validity of the timings themselves is checked by the
aemif_set_cs_timings() function.
It works the same way for WSETUP, WSTROBE and WHOLD with the other
while() loops.
>> +
>> + cfg = TO_CYCLES((s32)(MAX_TH_PS - sdr->tCHZ_max), cyc_ns) - 1;
>> + timings.rhold = cfg > 0 ? cfg : 0;
>> +
>> + min = TO_CYCLES(sdr->tRC_min, cyc_ns) - 3;
>> + while ((s32)(timings.rsetup + timings.rstrobe + timings.rhold) < min)
>> + timings.rhold++;
>> +
>> + cfg = TO_CYCLES((s32)(sdr->tRHZ_max - (timings.rhold + 1) * cyc_ns * 1000), cyc_ns);
>> + cfg = max_t(s32, cfg, TO_CYCLES(sdr->tCHZ_max, cyc_ns)) - 1;
>> + timings.ta = cfg > 0 ? cfg : 0;
>> +
>> + cfg = TO_CYCLES(sdr->tWP_min, cyc_ns) - 1;
>> + timings.wstrobe = cfg > 0 ? cfg : 0;
>> +
>> + cfg = max_t(s32, TO_CYCLES(sdr->tCLS_min, cyc_ns), TO_CYCLES(sdr->tALS_min, cyc_ns));
>> + cfg = max_t(s32, cfg, TO_CYCLES(sdr->tCS_min, cyc_ns)) - 1;
>> + timings.wsetup = cfg > 0 ? cfg : 0;
>> +
>> + min = TO_CYCLES(sdr->tDS_min, cyc_ns) - 2;
>> + while ((s32)(timings.wsetup + timings.wstrobe) < min)
>> + timings.wstrobe++;
>> +
>> + cfg = max_t(s32, TO_CYCLES(sdr->tCLH_min, cyc_ns), TO_CYCLES(sdr->tALH_min, cyc_ns));
>> + cfg = max_t(s32, cfg, TO_CYCLES(sdr->tCH_min, cyc_ns));
>> + cfg = max_t(s32, cfg, TO_CYCLES(sdr->tDH_min, cyc_ns)) - 1;
>> + timings.whold = cfg > 0 ? cfg : 0;
>> +
>> + min = TO_CYCLES(sdr->tWC_min, cyc_ns) - 2;
>> + while ((s32)(timings.wsetup + timings.wstrobe + timings.whold) < min)
>> + timings.whold++;
>> +
>> + dev_dbg(&info->pdev->dev, "RSETUP %x RSTROBE %x RHOLD %x\n",
>> + timings.rsetup, timings.rstrobe, timings.rhold);
>> + dev_dbg(&info->pdev->dev, "TA %x\n", timings.ta);
>> + dev_dbg(&info->pdev->dev, "WSETUP %x WSTROBE %x WHOLD %x\n",
>> + timings.wsetup, timings.wstrobe, timings.whold);
>> +
>
> Here you probably want to exit in the NAND_DATA_IFACE_CHECK_ONLY case.
>
I had missed this NAND_DATA_IFACE_CHECK_ONLY feature. In fact,
additionnal checks are done in the aemif_set_cs_timings() below. I'll
add an 'aemif_check_cs_timings()' function in AEMIF's driver in next
iteration.
>> + if (aemif_set_cs_timings(info->aemif, info->core_chipsel, &timings) < 0)
>> + dev_info(&info->pdev->dev,
>> + "Failed to dynamically update the CS timings, keep them unchanged");
>> +
>> + return 0;
>> +}
>> +
>
> Thanks,
> Miquèl
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2024-11-12 12:54 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-06 8:55 [PATCH v2 0/6] Implement setup_interface() in the DaVinci NAND controller Bastien Curutchet
2024-11-06 8:55 ` [PATCH v2 1/6] memory: ti-aemif: Create aemif_set_cs_timings() Bastien Curutchet
2024-11-11 19:16 ` Miquel Raynal
2024-11-06 8:55 ` [PATCH v2 2/6] memory: ti-aemif: Export aemif_set_cs_timings() Bastien Curutchet
2024-11-11 19:21 ` Miquel Raynal
2024-11-12 9:13 ` Bastien Curutchet
2024-11-12 11:17 ` Miquel Raynal
2024-11-06 8:55 ` [PATCH v2 3/6] mtd: rawnand: davinci: Always depends on TI_AEMIF Bastien Curutchet
2024-11-06 8:55 ` [PATCH v2 4/6] mtd: rawnand: davinci: Order headers alphabetically Bastien Curutchet
2024-11-06 8:55 ` [PATCH v2 5/6] mtd: rawnand: davinci: Add clock resource Bastien Curutchet
2024-11-06 8:55 ` [PATCH v2 6/6] mtd: rawnand: davinci: Implement setup_interface() operation Bastien Curutchet
2024-11-11 19:32 ` Miquel Raynal
2024-11-12 12:53 ` Bastien Curutchet
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).