summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Raynal <miquel.raynal@bootlin.com>2021-05-27 10:49:59 +0200
committerMiquel Raynal <miquel.raynal@bootlin.com>2021-06-18 09:45:20 +0200
commit76e12c104f627a24487fe1bfa0ed8563e36a5ce2 (patch)
treef7cf14af47f8b6ae7c28326a34b7e3fac1f5b191
parent23739c34f56c7eaa62d00b70dc8bf31b8244ef83 (diff)
mtd: rawnand: arasan: Finer grain NV-DDR configuration
Add support for the timings register which may improve a bit the overall throughput. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Link: https://lore.kernel.org/linux-mtd/20210527084959.208804-2-miquel.raynal@bootlin.com
-rw-r--r--drivers/mtd/nand/raw/arasan-nand-controller.c56
1 files changed, 54 insertions, 2 deletions
diff --git a/drivers/mtd/nand/raw/arasan-nand-controller.c b/drivers/mtd/nand/raw/arasan-nand-controller.c
index 5bcc680984f0..9cbcc698c64d 100644
--- a/drivers/mtd/nand/raw/arasan-nand-controller.c
+++ b/drivers/mtd/nand/raw/arasan-nand-controller.c
@@ -72,6 +72,15 @@
#define FLASH_STS_REG 0x28
+#define TIMING_REG 0x2C
+#define TCCS_TIME_500NS 0
+#define TCCS_TIME_300NS 3
+#define TCCS_TIME_200NS 2
+#define TCCS_TIME_100NS 1
+#define FAST_TCAD BIT(2)
+#define DQS_BUFF_SEL_IN(x) FIELD_PREP(GENMASK(6, 3), (x))
+#define DQS_BUFF_SEL_OUT(x) FIELD_PREP(GENMASK(18, 15), (x))
+
#define DATA_PORT_REG 0x30
#define ECC_CONF_REG 0x34
@@ -145,6 +154,7 @@ struct anfc_op {
* @page_sz: Register value of the page_sz field to use
* @clk: Expected clock frequency to use
* @data_iface: Data interface timing mode to use
+ * @timings: NV-DDR specific timings to use
* @ecc_conf: Hardware ECC configuration value
* @strength: Register value of the ECC strength
* @raddr_cycles: Row address cycle information
@@ -165,6 +175,7 @@ struct anand {
unsigned int page_sz;
unsigned long clk;
u32 data_iface;
+ u32 timings;
u32 ecc_conf;
u32 strength;
u16 raddr_cycles;
@@ -332,6 +343,7 @@ static int anfc_select_target(struct nand_chip *chip, int target)
/* Update the controller timings and the potential ECC configuration */
writel_relaxed(anand->data_iface, nfc->base + DATA_INTERFACE_REG);
+ writel_relaxed(anand->timings, nfc->base + TIMING_REG);
/* Update clock frequency */
if (nfc->cur_clk != anand->clk) {
@@ -955,6 +967,7 @@ static int anfc_setup_interface(struct nand_chip *chip, int target,
struct device_node *np = nfc->dev->of_node;
const struct nand_sdr_timings *sdr;
const struct nand_nvddr_timings *nvddr;
+ unsigned int tccs_min, dqs_mode, fast_tcad;
if (nand_interface_is_nvddr(conf)) {
nvddr = nand_get_nvddr_timings(conf);
@@ -969,13 +982,52 @@ static int anfc_setup_interface(struct nand_chip *chip, int target,
if (target < 0)
return 0;
- if (nand_interface_is_sdr(conf))
+ if (nand_interface_is_sdr(conf)) {
anand->data_iface = DIFACE_SDR |
DIFACE_SDR_MODE(conf->timings.mode);
- else
+ anand->timings = 0;
+ } else {
anand->data_iface = DIFACE_NVDDR |
DIFACE_DDR_MODE(conf->timings.mode);
+ if (conf->timings.nvddr.tCCS_min <= 100000)
+ tccs_min = TCCS_TIME_100NS;
+ else if (conf->timings.nvddr.tCCS_min <= 200000)
+ tccs_min = TCCS_TIME_200NS;
+ else if (conf->timings.nvddr.tCCS_min <= 300000)
+ tccs_min = TCCS_TIME_300NS;
+ else
+ tccs_min = TCCS_TIME_500NS;
+
+ fast_tcad = 0;
+ if (conf->timings.nvddr.tCAD_min < 45000)
+ fast_tcad = FAST_TCAD;
+
+ switch (conf->timings.mode) {
+ case 5:
+ case 4:
+ dqs_mode = 2;
+ break;
+ case 3:
+ dqs_mode = 3;
+ break;
+ case 2:
+ dqs_mode = 4;
+ break;
+ case 1:
+ dqs_mode = 5;
+ break;
+ case 0:
+ default:
+ dqs_mode = 6;
+ break;
+ }
+
+ anand->timings = tccs_min | fast_tcad |
+ DQS_BUFF_SEL_IN(dqs_mode) |
+ DQS_BUFF_SEL_OUT(dqs_mode);
+ }
+
anand->clk = ANFC_XLNX_SDR_DFLT_CORE_CLK;
/*