[PATCH v6] mtd: rawnand: use bit-wise majority to recover the contents of ONFI parameter

2018-05-10 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if all parameter pages have invalid
CRC values, the bit-wise majority may be used to recover the contents of
the parameter pages from the parameter page copies present.

Signed-off-by: Jane Wan <jane@nokia.com>
---
v6: support the cases that srcbufs are not contiguous
v5: make the bit-wise majority functon generic
v4: move the bit-wise majority code in a separate function
v3: fix warning message detected by kbuild test robot
v2: rebase the changes on top of v4.17-rc1

 drivers/mtd/nand/raw/nand_base.c |   52 ++
 1 file changed, 47 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 72f3a89..acf905c 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -5087,6 +5087,35 @@ static int nand_flash_detect_ext_param_page(struct 
nand_chip *chip,
 }
 
 /*
+ * Recover data with bit-wise majority
+ */
+static void nand_bit_wise_majority(const void **srcbufs,
+  unsigned int nsrcbufs,
+  void *dstbuf,
+  unsigned int bufsize)
+{
+   int i, j, k;
+
+   for (i = 0; i < bufsize; i++) {
+   u8 cnt, val;
+
+   val = 0;
+   for (j = 0; j < 8; j++) {
+   cnt = 0;
+   for (k = 0; k < nsrcbufs; k++) {
+   const u8 *srcbuf = srcbufs[k];
+
+   if (srcbuf[i] & BIT(j))
+   cnt++;
+   }
+   if (cnt > nsrcbufs / 2)
+   val |= BIT(j);
+   }
+   ((u8 *)dstbuf)[i] = val;
+   }
+}
+
+/*
  * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
  */
 static int nand_flash_detect_onfi(struct nand_chip *chip)
@@ -5102,7 +5131,7 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
return 0;
 
/* ONFI chip: allocate a buffer to hold its parameter page */
-   p = kzalloc(sizeof(*p), GFP_KERNEL);
+   p = kzalloc((sizeof(*p) * 3), GFP_KERNEL);
if (!p)
return -ENOMEM;
 
@@ -5113,21 +5142,34 @@ static int nand_flash_detect_onfi(struct nand_chip 
*chip)
}
 
for (i = 0; i < 3; i++) {
-   ret = nand_read_data_op(chip, p, sizeof(*p), true);
+   ret = nand_read_data_op(chip, [i], sizeof(*p), true);
if (ret) {
ret = 0;
goto free_onfi_param_page;
}
 
-   if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+   if (onfi_crc16(ONFI_CRC_BASE, (u8 *)[i], 254) ==
le16_to_cpu(p->crc)) {
+   if (i)
+   memcpy(p, [i], sizeof(*p));
break;
}
}
 
if (i == 3) {
-   pr_err("Could not find valid ONFI parameter page; aborting\n");
-   goto free_onfi_param_page;
+   const void *srcbufs[3] = {p, p + 1, p + 2};
+
+   pr_err("Could not find valid ONFI parameter page\n");
+   pr_info("Recover ONFI params with bit-wise majority\n");
+
+   nand_bit_wise_majority(srcbufs, ARRAY_SIZE(srcbufs), p,
+  sizeof(*p));
+
+   if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) !=
+   le16_to_cpu(p->crc)) {
+   pr_err("ONFI parameter recovery failed, aborting\n");
+   goto free_onfi_param_page;
+   }
}
 
/* Check version */
-- 
1.7.9.5



[PATCH v6] mtd: rawnand: use bit-wise majority to recover the contents of ONFI parameter

2018-05-10 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if all parameter pages have invalid
CRC values, the bit-wise majority may be used to recover the contents of
the parameter pages from the parameter page copies present.

Signed-off-by: Jane Wan 
---
v6: support the cases that srcbufs are not contiguous
v5: make the bit-wise majority functon generic
v4: move the bit-wise majority code in a separate function
v3: fix warning message detected by kbuild test robot
v2: rebase the changes on top of v4.17-rc1

 drivers/mtd/nand/raw/nand_base.c |   52 ++
 1 file changed, 47 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 72f3a89..acf905c 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -5087,6 +5087,35 @@ static int nand_flash_detect_ext_param_page(struct 
nand_chip *chip,
 }
 
 /*
+ * Recover data with bit-wise majority
+ */
+static void nand_bit_wise_majority(const void **srcbufs,
+  unsigned int nsrcbufs,
+  void *dstbuf,
+  unsigned int bufsize)
+{
+   int i, j, k;
+
+   for (i = 0; i < bufsize; i++) {
+   u8 cnt, val;
+
+   val = 0;
+   for (j = 0; j < 8; j++) {
+   cnt = 0;
+   for (k = 0; k < nsrcbufs; k++) {
+   const u8 *srcbuf = srcbufs[k];
+
+   if (srcbuf[i] & BIT(j))
+   cnt++;
+   }
+   if (cnt > nsrcbufs / 2)
+   val |= BIT(j);
+   }
+   ((u8 *)dstbuf)[i] = val;
+   }
+}
+
+/*
  * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
  */
 static int nand_flash_detect_onfi(struct nand_chip *chip)
@@ -5102,7 +5131,7 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
return 0;
 
/* ONFI chip: allocate a buffer to hold its parameter page */
-   p = kzalloc(sizeof(*p), GFP_KERNEL);
+   p = kzalloc((sizeof(*p) * 3), GFP_KERNEL);
if (!p)
return -ENOMEM;
 
@@ -5113,21 +5142,34 @@ static int nand_flash_detect_onfi(struct nand_chip 
*chip)
}
 
for (i = 0; i < 3; i++) {
-   ret = nand_read_data_op(chip, p, sizeof(*p), true);
+   ret = nand_read_data_op(chip, [i], sizeof(*p), true);
if (ret) {
ret = 0;
goto free_onfi_param_page;
}
 
-   if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+   if (onfi_crc16(ONFI_CRC_BASE, (u8 *)[i], 254) ==
le16_to_cpu(p->crc)) {
+   if (i)
+   memcpy(p, [i], sizeof(*p));
break;
}
}
 
if (i == 3) {
-   pr_err("Could not find valid ONFI parameter page; aborting\n");
-   goto free_onfi_param_page;
+   const void *srcbufs[3] = {p, p + 1, p + 2};
+
+   pr_err("Could not find valid ONFI parameter page\n");
+   pr_info("Recover ONFI params with bit-wise majority\n");
+
+   nand_bit_wise_majority(srcbufs, ARRAY_SIZE(srcbufs), p,
+  sizeof(*p));
+
+   if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) !=
+   le16_to_cpu(p->crc)) {
+   pr_err("ONFI parameter recovery failed, aborting\n");
+   goto free_onfi_param_page;
+   }
}
 
/* Check version */
-- 
1.7.9.5



[PATCH v5 2/2] mtd: rawnand: use bit-wise majority to recover the contents of ONFI parameter

2018-05-09 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if all parameter pages have invalid
CRC values, the bit-wise majority may be used to recover the contents of
the parameter pages from the parameter page copies present.

Signed-off-by: Jane Wan <jane@nokia.com>
---
 drivers/mtd/nand/raw/nand_base.c |   46 +-
 1 file changed, 41 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 72f3a89..a7c2507 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -5086,6 +5086,34 @@ static int nand_flash_detect_ext_param_page(struct 
nand_chip *chip,
return ret;
 }
 
+#define GET_BIT(bit, val)   (((val) >> (bit)) & 0x01)
+
+/*
+ * Recover data with bit-wise majority
+ */
+static void nand_bit_wise_majority(const void **srcbufs,
+  void *dstbuf,
+  unsigned int nbufs,
+  unsigned int bufsize)
+{
+   int i, j, k;
+   u8 v, m;
+   u8 *p;
+
+   p = *(u8 **)srcbufs;
+   for (i = 0; i < bufsize; i++) {
+   v = 0;
+   for (j = 0; j < 8; j++) {
+   m = 0;
+   for (k = 0; k < nbufs; k++)
+   m += GET_BIT(j, p[k*bufsize + i]);
+   if (m > nbufs/2)
+   v |= BIT(j);
+   }
+   ((u8 *)dstbuf)[i] = v;
+   }
+}
+
 /*
  * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
  */
@@ -5102,7 +5130,7 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
return 0;
 
/* ONFI chip: allocate a buffer to hold its parameter page */
-   p = kzalloc(sizeof(*p), GFP_KERNEL);
+   p = kzalloc((sizeof(*p) * 3), GFP_KERNEL);
if (!p)
return -ENOMEM;
 
@@ -5113,21 +5141,29 @@ static int nand_flash_detect_onfi(struct nand_chip 
*chip)
}
 
for (i = 0; i < 3; i++) {
-   ret = nand_read_data_op(chip, p, sizeof(*p), true);
+   ret = nand_read_data_op(chip, [i], sizeof(*p), true);
if (ret) {
ret = 0;
goto free_onfi_param_page;
}
 
-   if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+   if (onfi_crc16(ONFI_CRC_BASE, (u8 *)[i], 254) ==
le16_to_cpu(p->crc)) {
+   if (i)
+   memcpy(p, [i], sizeof(*p));
break;
}
}
 
if (i == 3) {
-   pr_err("Could not find valid ONFI parameter page; aborting\n");
-   goto free_onfi_param_page;
+   pr_err("Could not find valid ONFI parameter page\n");
+   pr_info("Recover ONFI params with bit-wise majority\n");
+   nand_bit_wise_majority((const void **), p, 3, sizeof(*p));
+   if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) !=
+   le16_to_cpu(p->crc)) {
+   pr_err("ONFI parameter recovery failed, aborting\n");
+   goto free_onfi_param_page;
+   }
}
 
/* Check version */
-- 
1.7.9.5



[PATCH v5 2/2] mtd: rawnand: use bit-wise majority to recover the contents of ONFI parameter

2018-05-09 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if all parameter pages have invalid
CRC values, the bit-wise majority may be used to recover the contents of
the parameter pages from the parameter page copies present.

Signed-off-by: Jane Wan 
---
 drivers/mtd/nand/raw/nand_base.c |   46 +-
 1 file changed, 41 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 72f3a89..a7c2507 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -5086,6 +5086,34 @@ static int nand_flash_detect_ext_param_page(struct 
nand_chip *chip,
return ret;
 }
 
+#define GET_BIT(bit, val)   (((val) >> (bit)) & 0x01)
+
+/*
+ * Recover data with bit-wise majority
+ */
+static void nand_bit_wise_majority(const void **srcbufs,
+  void *dstbuf,
+  unsigned int nbufs,
+  unsigned int bufsize)
+{
+   int i, j, k;
+   u8 v, m;
+   u8 *p;
+
+   p = *(u8 **)srcbufs;
+   for (i = 0; i < bufsize; i++) {
+   v = 0;
+   for (j = 0; j < 8; j++) {
+   m = 0;
+   for (k = 0; k < nbufs; k++)
+   m += GET_BIT(j, p[k*bufsize + i]);
+   if (m > nbufs/2)
+   v |= BIT(j);
+   }
+   ((u8 *)dstbuf)[i] = v;
+   }
+}
+
 /*
  * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
  */
@@ -5102,7 +5130,7 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
return 0;
 
/* ONFI chip: allocate a buffer to hold its parameter page */
-   p = kzalloc(sizeof(*p), GFP_KERNEL);
+   p = kzalloc((sizeof(*p) * 3), GFP_KERNEL);
if (!p)
return -ENOMEM;
 
@@ -5113,21 +5141,29 @@ static int nand_flash_detect_onfi(struct nand_chip 
*chip)
}
 
for (i = 0; i < 3; i++) {
-   ret = nand_read_data_op(chip, p, sizeof(*p), true);
+   ret = nand_read_data_op(chip, [i], sizeof(*p), true);
if (ret) {
ret = 0;
goto free_onfi_param_page;
}
 
-   if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+   if (onfi_crc16(ONFI_CRC_BASE, (u8 *)[i], 254) ==
le16_to_cpu(p->crc)) {
+   if (i)
+   memcpy(p, [i], sizeof(*p));
break;
}
}
 
if (i == 3) {
-   pr_err("Could not find valid ONFI parameter page; aborting\n");
-   goto free_onfi_param_page;
+   pr_err("Could not find valid ONFI parameter page\n");
+   pr_info("Recover ONFI params with bit-wise majority\n");
+   nand_bit_wise_majority((const void **), p, 3, sizeof(*p));
+   if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) !=
+   le16_to_cpu(p->crc)) {
+   pr_err("ONFI parameter recovery failed, aborting\n");
+   goto free_onfi_param_page;
+   }
}
 
/* Check version */
-- 
1.7.9.5



[PATCH v4 2/2] mtd: rawnand: use bit-wise majority to recover the contents of ONFI parameter

2018-05-08 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if all parameter pages have invalid
CRC values, the bit-wise majority may be used to recover the contents of
the parameter pages from the parameter page copies present.

Signed-off-by: Jane Wan <jane@nokia.com>
---
 drivers/mtd/nand/raw/nand_base.c |   49 ++
 1 file changed, 44 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 72f3a89..dfc341c 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -5086,6 +5086,38 @@ static int nand_flash_detect_ext_param_page(struct 
nand_chip *chip,
return ret;
 }
 
+#define GET_BIT(bit, val)   (((val) >> (bit)) & 0x01)
+
+/*
+ * Recover NAND parameter page with bit-wise majority
+ */
+static int onfi_recover_param(struct nand_onfi_params *p, int pages)
+{
+   int i, j, k;
+   u8 v, m;
+   u8 *buf;
+
+   buf = (u8 *)p;
+   for (i = 0; i < sizeof(*p); i++) {
+   v = 0;
+   for (j = 0; j < 8; j++) {
+   m = 0;
+   for (k = 0; k < pages; k++)
+   m += GET_BIT(j, buf[k*sizeof(*p) + i]);
+   if (m > pages/2)
+   v |= BIT(j);
+   }
+   ((u8 *)p)[i] = v;
+   }
+
+   if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) ==
+   le16_to_cpu(p->crc)) {
+   return 1;
+   }
+
+   return 0;
+}
+
 /*
  * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
  */
@@ -5102,7 +5134,7 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
return 0;
 
/* ONFI chip: allocate a buffer to hold its parameter page */
-   p = kzalloc(sizeof(*p), GFP_KERNEL);
+   p = kzalloc((sizeof(*p) * 3), GFP_KERNEL);
if (!p)
return -ENOMEM;
 
@@ -5113,21 +5145,28 @@ static int nand_flash_detect_onfi(struct nand_chip 
*chip)
}
 
for (i = 0; i < 3; i++) {
-   ret = nand_read_data_op(chip, p, sizeof(*p), true);
+   ret = nand_read_data_op(chip, [i], sizeof(*p), true);
if (ret) {
ret = 0;
goto free_onfi_param_page;
}
 
-   if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+   if (onfi_crc16(ONFI_CRC_BASE, (u8 *)[i], 254) ==
le16_to_cpu(p->crc)) {
+   if (i)
+   memcpy(p, [i], sizeof(*p));
break;
}
}
 
if (i == 3) {
-   pr_err("Could not find valid ONFI parameter page; aborting\n");
-   goto free_onfi_param_page;
+   pr_err("Could not find valid ONFI parameter page\n");
+   pr_info("Recover ONFI params with bit-wise majority\n");
+   ret = onfi_recover_param(p, 3);
+   if (ret == 0) {
+   pr_err("ONFI parameter recovery failed, aborting\n");
+   goto free_onfi_param_page;
+   }
}
 
/* Check version */
-- 
1.7.9.5



[PATCH v4 2/2] mtd: rawnand: use bit-wise majority to recover the contents of ONFI parameter

2018-05-08 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if all parameter pages have invalid
CRC values, the bit-wise majority may be used to recover the contents of
the parameter pages from the parameter page copies present.

Signed-off-by: Jane Wan 
---
 drivers/mtd/nand/raw/nand_base.c |   49 ++
 1 file changed, 44 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 72f3a89..dfc341c 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -5086,6 +5086,38 @@ static int nand_flash_detect_ext_param_page(struct 
nand_chip *chip,
return ret;
 }
 
+#define GET_BIT(bit, val)   (((val) >> (bit)) & 0x01)
+
+/*
+ * Recover NAND parameter page with bit-wise majority
+ */
+static int onfi_recover_param(struct nand_onfi_params *p, int pages)
+{
+   int i, j, k;
+   u8 v, m;
+   u8 *buf;
+
+   buf = (u8 *)p;
+   for (i = 0; i < sizeof(*p); i++) {
+   v = 0;
+   for (j = 0; j < 8; j++) {
+   m = 0;
+   for (k = 0; k < pages; k++)
+   m += GET_BIT(j, buf[k*sizeof(*p) + i]);
+   if (m > pages/2)
+   v |= BIT(j);
+   }
+   ((u8 *)p)[i] = v;
+   }
+
+   if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) ==
+   le16_to_cpu(p->crc)) {
+   return 1;
+   }
+
+   return 0;
+}
+
 /*
  * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
  */
@@ -5102,7 +5134,7 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
return 0;
 
/* ONFI chip: allocate a buffer to hold its parameter page */
-   p = kzalloc(sizeof(*p), GFP_KERNEL);
+   p = kzalloc((sizeof(*p) * 3), GFP_KERNEL);
if (!p)
return -ENOMEM;
 
@@ -5113,21 +5145,28 @@ static int nand_flash_detect_onfi(struct nand_chip 
*chip)
}
 
for (i = 0; i < 3; i++) {
-   ret = nand_read_data_op(chip, p, sizeof(*p), true);
+   ret = nand_read_data_op(chip, [i], sizeof(*p), true);
if (ret) {
ret = 0;
goto free_onfi_param_page;
}
 
-   if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+   if (onfi_crc16(ONFI_CRC_BASE, (u8 *)[i], 254) ==
le16_to_cpu(p->crc)) {
+   if (i)
+   memcpy(p, [i], sizeof(*p));
break;
}
}
 
if (i == 3) {
-   pr_err("Could not find valid ONFI parameter page; aborting\n");
-   goto free_onfi_param_page;
+   pr_err("Could not find valid ONFI parameter page\n");
+   pr_info("Recover ONFI params with bit-wise majority\n");
+   ret = onfi_recover_param(p, 3);
+   if (ret == 0) {
+   pr_err("ONFI parameter recovery failed, aborting\n");
+   goto free_onfi_param_page;
+   }
}
 
/* Check version */
-- 
1.7.9.5



[PATCH v4 1/2] mtd: rawnand: fsl_ifc: fix FSL NAND driver to read all ONFI parameter pages

2018-05-08 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if the CRC of the first parameter page
read is not valid, the host should read redundant parameter page copies.
Fix FSL NAND driver to read the two redundant copies which are mandatory
in the specification.

Signed-off-by: Jane Wan <jane@nokia.com>
---
 drivers/mtd/nand/raw/fsl_ifc_nand.c |   17 ++---
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c 
b/drivers/mtd/nand/raw/fsl_ifc_nand.c
index 61aae02..98aac1f 100644
--- a/drivers/mtd/nand/raw/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
@@ -342,9 +342,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned 
int command,
 
case NAND_CMD_READID:
case NAND_CMD_PARAM: {
+   /*
+* For READID, read 8 bytes that are currently used.
+* For PARAM, read all 3 copies of 256-bytes pages.
+*/
+   int len = 8;
int timing = IFC_FIR_OP_RB;
-   if (command == NAND_CMD_PARAM)
+   if (command == NAND_CMD_PARAM) {
timing = IFC_FIR_OP_RBCD;
+   len = 256 * 3;
+   }
 
ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
  (IFC_FIR_OP_UA  << IFC_NAND_FIR0_OP1_SHIFT) |
@@ -354,12 +361,8 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned 
int command,
  >ifc_nand.nand_fcr0);
ifc_out32(column, >ifc_nand.row3);
 
-   /*
-* although currently it's 8 bytes for READID, we always read
-* the maximum 256 bytes(for PARAM)
-*/
-   ifc_out32(256, >ifc_nand.nand_fbcr);
-   ifc_nand_ctrl->read_bytes = 256;
+   ifc_out32(len, >ifc_nand.nand_fbcr);
+   ifc_nand_ctrl->read_bytes = len;
 
set_addr(mtd, 0, 0, 0);
fsl_ifc_run_command(mtd);
-- 
1.7.9.5



[PATCH v4 1/2] mtd: rawnand: fsl_ifc: fix FSL NAND driver to read all ONFI parameter pages

2018-05-08 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if the CRC of the first parameter page
read is not valid, the host should read redundant parameter page copies.
Fix FSL NAND driver to read the two redundant copies which are mandatory
in the specification.

Signed-off-by: Jane Wan 
---
 drivers/mtd/nand/raw/fsl_ifc_nand.c |   17 ++---
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c 
b/drivers/mtd/nand/raw/fsl_ifc_nand.c
index 61aae02..98aac1f 100644
--- a/drivers/mtd/nand/raw/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
@@ -342,9 +342,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned 
int command,
 
case NAND_CMD_READID:
case NAND_CMD_PARAM: {
+   /*
+* For READID, read 8 bytes that are currently used.
+* For PARAM, read all 3 copies of 256-bytes pages.
+*/
+   int len = 8;
int timing = IFC_FIR_OP_RB;
-   if (command == NAND_CMD_PARAM)
+   if (command == NAND_CMD_PARAM) {
timing = IFC_FIR_OP_RBCD;
+   len = 256 * 3;
+   }
 
ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
  (IFC_FIR_OP_UA  << IFC_NAND_FIR0_OP1_SHIFT) |
@@ -354,12 +361,8 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned 
int command,
  >ifc_nand.nand_fcr0);
ifc_out32(column, >ifc_nand.row3);
 
-   /*
-* although currently it's 8 bytes for READID, we always read
-* the maximum 256 bytes(for PARAM)
-*/
-   ifc_out32(256, >ifc_nand.nand_fbcr);
-   ifc_nand_ctrl->read_bytes = 256;
+   ifc_out32(len, >ifc_nand.nand_fbcr);
+   ifc_nand_ctrl->read_bytes = len;
 
set_addr(mtd, 0, 0, 0);
fsl_ifc_run_command(mtd);
-- 
1.7.9.5



[PATCH v3 2/2] mtd: rawnand: fsl_ifc: use bit-wise majority to recover the contents of ONFI parameter

2018-05-07 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if all parameter pages have invalid
CRC values, the bit-wise majority may be used to recover the contents of
the parameter pages from the parameter page copies present.

Signed-off-by: Jane Wan <jane@nokia.com>
---
 drivers/mtd/nand/raw/nand_base.c |   41 ++
 1 file changed, 33 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 72f3a89..48f2dec 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -5086,15 +5086,18 @@ static int nand_flash_detect_ext_param_page(struct 
nand_chip *chip,
return ret;
 }
 
+#define GET_BIT(bit, val)   (((val) >> (bit)) & 0x01)
+
 /*
  * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
  */
 static int nand_flash_detect_onfi(struct nand_chip *chip)
 {
struct mtd_info *mtd = nand_to_mtd(chip);
-   struct nand_onfi_params *p;
+   struct nand_onfi_params *p = NULL;
char id[4];
-   int i, ret, val;
+   int i, ret, val, pagesize;
+   u8 *buf = NULL;
 
/* Try ONFI for unknown chip or LP */
ret = nand_readid_op(chip, 0x20, id, sizeof(id));
@@ -5102,8 +5105,9 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
return 0;
 
/* ONFI chip: allocate a buffer to hold its parameter page */
-   p = kzalloc(sizeof(*p), GFP_KERNEL);
-   if (!p)
+   pagesize = sizeof(*p);
+   buf = kzalloc((pagesize * 3), GFP_KERNEL);
+   if (!buf)
return -ENOMEM;
 
ret = nand_read_param_page_op(chip, 0, NULL, 0);
@@ -5113,7 +5117,8 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
}
 
for (i = 0; i < 3; i++) {
-   ret = nand_read_data_op(chip, p, sizeof(*p), true);
+   p = (struct nand_onfi_params *)[i*pagesize];
+   ret = nand_read_data_op(chip, p, pagesize, true);
if (ret) {
ret = 0;
goto free_onfi_param_page;
@@ -5126,8 +5131,27 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
}
 
if (i == 3) {
-   pr_err("Could not find valid ONFI parameter page; aborting\n");
-   goto free_onfi_param_page;
+   int j, k, l;
+   u8 v, m;
+
+   pr_err("Could not find valid ONFI parameter page\n");
+   pr_info("Recover ONFI params with bit-wise majority\n");
+   for (j = 0; j < pagesize; j++) {
+   v = 0;
+   for (k = 0; k < 8; k++) {
+   m = 0;
+   for (l = 0; l < 3; l++)
+   m += GET_BIT(k, buf[l*pagesize + j]);
+   if (m > 1)
+   v |= BIT(k);
+   }
+   ((u8 *)p)[j] = v;
+   }
+   if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) !=
+   le16_to_cpu(p->crc)) {
+   pr_err("ONFI parameter recovery failed, aborting\n");
+   goto free_onfi_param_page;
+   }
}
 
/* Check version */
@@ -5220,7 +5244,8 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
   sizeof(p->vendor));
 
 free_onfi_param_page:
-   kfree(p);
+   if (buf != NULL)
+   kfree(buf);
return ret;
 }
 
-- 
1.7.9.5



[PATCH v3 2/2] mtd: rawnand: fsl_ifc: use bit-wise majority to recover the contents of ONFI parameter

2018-05-07 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if all parameter pages have invalid
CRC values, the bit-wise majority may be used to recover the contents of
the parameter pages from the parameter page copies present.

Signed-off-by: Jane Wan 
---
 drivers/mtd/nand/raw/nand_base.c |   41 ++
 1 file changed, 33 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 72f3a89..48f2dec 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -5086,15 +5086,18 @@ static int nand_flash_detect_ext_param_page(struct 
nand_chip *chip,
return ret;
 }
 
+#define GET_BIT(bit, val)   (((val) >> (bit)) & 0x01)
+
 /*
  * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
  */
 static int nand_flash_detect_onfi(struct nand_chip *chip)
 {
struct mtd_info *mtd = nand_to_mtd(chip);
-   struct nand_onfi_params *p;
+   struct nand_onfi_params *p = NULL;
char id[4];
-   int i, ret, val;
+   int i, ret, val, pagesize;
+   u8 *buf = NULL;
 
/* Try ONFI for unknown chip or LP */
ret = nand_readid_op(chip, 0x20, id, sizeof(id));
@@ -5102,8 +5105,9 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
return 0;
 
/* ONFI chip: allocate a buffer to hold its parameter page */
-   p = kzalloc(sizeof(*p), GFP_KERNEL);
-   if (!p)
+   pagesize = sizeof(*p);
+   buf = kzalloc((pagesize * 3), GFP_KERNEL);
+   if (!buf)
return -ENOMEM;
 
ret = nand_read_param_page_op(chip, 0, NULL, 0);
@@ -5113,7 +5117,8 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
}
 
for (i = 0; i < 3; i++) {
-   ret = nand_read_data_op(chip, p, sizeof(*p), true);
+   p = (struct nand_onfi_params *)[i*pagesize];
+   ret = nand_read_data_op(chip, p, pagesize, true);
if (ret) {
ret = 0;
goto free_onfi_param_page;
@@ -5126,8 +5131,27 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
}
 
if (i == 3) {
-   pr_err("Could not find valid ONFI parameter page; aborting\n");
-   goto free_onfi_param_page;
+   int j, k, l;
+   u8 v, m;
+
+   pr_err("Could not find valid ONFI parameter page\n");
+   pr_info("Recover ONFI params with bit-wise majority\n");
+   for (j = 0; j < pagesize; j++) {
+   v = 0;
+   for (k = 0; k < 8; k++) {
+   m = 0;
+   for (l = 0; l < 3; l++)
+   m += GET_BIT(k, buf[l*pagesize + j]);
+   if (m > 1)
+   v |= BIT(k);
+   }
+   ((u8 *)p)[j] = v;
+   }
+   if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) !=
+   le16_to_cpu(p->crc)) {
+   pr_err("ONFI parameter recovery failed, aborting\n");
+   goto free_onfi_param_page;
+   }
}
 
/* Check version */
@@ -5220,7 +5244,8 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
   sizeof(p->vendor));
 
 free_onfi_param_page:
-   kfree(p);
+   if (buf != NULL)
+   kfree(buf);
return ret;
 }
 
-- 
1.7.9.5



[PATCH v2 2/2] mtd: rawnand: fsl_ifc: use bit-wise majority to recover the contents of ONFI parameter

2018-05-04 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if all parameter pages have invalid
CRC values, the bit-wise majority may be used to recover the contents of
the parameter pages from the parameter page copies present.

Signed-off-by: Jane Wan <jane@nokia.com>
---
 drivers/mtd/nand/raw/nand_base.c |   36 ++--
 1 file changed, 30 insertions(+), 6 deletions(-)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 72f3a89..464c4fb 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -5086,6 +5086,8 @@ static int nand_flash_detect_ext_param_page(struct 
nand_chip *chip,
return ret;
 }
 
+#define GET_BIT(bit, val)   (((val) >> (bit)) & 0x01)
+
 /*
  * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
  */
@@ -5094,7 +5096,8 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_onfi_params *p;
char id[4];
-   int i, ret, val;
+   int i, ret, val, pagesize;
+   u8 *buf;
 
/* Try ONFI for unknown chip or LP */
ret = nand_readid_op(chip, 0x20, id, sizeof(id));
@@ -5102,8 +5105,9 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
return 0;
 
/* ONFI chip: allocate a buffer to hold its parameter page */
-   p = kzalloc(sizeof(*p), GFP_KERNEL);
-   if (!p)
+   pagesize = sizeof(*p);
+   buf = kzalloc((pagesize * 3), GFP_KERNEL);
+   if (!buf)
return -ENOMEM;
 
ret = nand_read_param_page_op(chip, 0, NULL, 0);
@@ -5113,7 +5117,8 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
}
 
for (i = 0; i < 3; i++) {
-   ret = nand_read_data_op(chip, p, sizeof(*p), true);
+   p = (struct nand_onfi_params *)[i*pagesize];
+   ret = nand_read_data_op(chip, p, pagesize, true);
if (ret) {
ret = 0;
goto free_onfi_param_page;
@@ -5126,8 +5131,27 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
}
 
if (i == 3) {
-   pr_err("Could not find valid ONFI parameter page; aborting\n");
-   goto free_onfi_param_page;
+   int j, k, l;
+   u8 v, m;
+
+   pr_err("Could not find valid ONFI parameter page\n");
+   pr_info("Recover ONFI params with bit-wise majority\n");
+   for (j = 0; j < pagesize; j++) {
+   v = 0;
+   for (k = 0; k < 8; k++) {
+   m = 0;
+   for (l = 0; l < 3; l++)
+   m += GET_BIT(k, buf[l*pagesize + j]);
+   if (m > 1)
+   v |= BIT(k);
+   }
+   ((u8 *)p)[j] = v;
+   }
+   if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) !=
+   le16_to_cpu(p->crc)) {
+   pr_err("ONFI parameter recovery failed, aborting\n");
+   goto free_onfi_param_page;
+   }
}
 
/* Check version */
-- 
1.7.9.5



[PATCH v2 2/2] mtd: rawnand: fsl_ifc: use bit-wise majority to recover the contents of ONFI parameter

2018-05-04 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if all parameter pages have invalid
CRC values, the bit-wise majority may be used to recover the contents of
the parameter pages from the parameter page copies present.

Signed-off-by: Jane Wan 
---
 drivers/mtd/nand/raw/nand_base.c |   36 ++--
 1 file changed, 30 insertions(+), 6 deletions(-)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 72f3a89..464c4fb 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -5086,6 +5086,8 @@ static int nand_flash_detect_ext_param_page(struct 
nand_chip *chip,
return ret;
 }
 
+#define GET_BIT(bit, val)   (((val) >> (bit)) & 0x01)
+
 /*
  * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
  */
@@ -5094,7 +5096,8 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_onfi_params *p;
char id[4];
-   int i, ret, val;
+   int i, ret, val, pagesize;
+   u8 *buf;
 
/* Try ONFI for unknown chip or LP */
ret = nand_readid_op(chip, 0x20, id, sizeof(id));
@@ -5102,8 +5105,9 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
return 0;
 
/* ONFI chip: allocate a buffer to hold its parameter page */
-   p = kzalloc(sizeof(*p), GFP_KERNEL);
-   if (!p)
+   pagesize = sizeof(*p);
+   buf = kzalloc((pagesize * 3), GFP_KERNEL);
+   if (!buf)
return -ENOMEM;
 
ret = nand_read_param_page_op(chip, 0, NULL, 0);
@@ -5113,7 +5117,8 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
}
 
for (i = 0; i < 3; i++) {
-   ret = nand_read_data_op(chip, p, sizeof(*p), true);
+   p = (struct nand_onfi_params *)[i*pagesize];
+   ret = nand_read_data_op(chip, p, pagesize, true);
if (ret) {
ret = 0;
goto free_onfi_param_page;
@@ -5126,8 +5131,27 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
}
 
if (i == 3) {
-   pr_err("Could not find valid ONFI parameter page; aborting\n");
-   goto free_onfi_param_page;
+   int j, k, l;
+   u8 v, m;
+
+   pr_err("Could not find valid ONFI parameter page\n");
+   pr_info("Recover ONFI params with bit-wise majority\n");
+   for (j = 0; j < pagesize; j++) {
+   v = 0;
+   for (k = 0; k < 8; k++) {
+   m = 0;
+   for (l = 0; l < 3; l++)
+   m += GET_BIT(k, buf[l*pagesize + j]);
+   if (m > 1)
+   v |= BIT(k);
+   }
+   ((u8 *)p)[j] = v;
+   }
+   if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) !=
+   le16_to_cpu(p->crc)) {
+   pr_err("ONFI parameter recovery failed, aborting\n");
+   goto free_onfi_param_page;
+   }
}
 
/* Check version */
-- 
1.7.9.5



[PATCH v2 1/2] mtd: rawnand: fsl_ifc: fix FSL NAND driver to read all ONFI parameter pages

2018-05-04 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if the CRC of the first parameter page
read is not valid, the host should read redundant parameter page copies.
Fix FSL NAND driver to read the two redundant copies which are mandatory
in the specification.

Signed-off-by: Jane Wan <jane@nokia.com>
---
 drivers/mtd/nand/raw/fsl_ifc_nand.c |   17 ++---
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c 
b/drivers/mtd/nand/raw/fsl_ifc_nand.c
index 61aae02..98aac1f 100644
--- a/drivers/mtd/nand/raw/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
@@ -342,9 +342,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned 
int command,
 
case NAND_CMD_READID:
case NAND_CMD_PARAM: {
+   /*
+* For READID, read 8 bytes that are currently used.
+* For PARAM, read all 3 copies of 256-bytes pages.
+*/
+   int len = 8;
int timing = IFC_FIR_OP_RB;
-   if (command == NAND_CMD_PARAM)
+   if (command == NAND_CMD_PARAM) {
timing = IFC_FIR_OP_RBCD;
+   len = 256 * 3;
+   }
 
ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
  (IFC_FIR_OP_UA  << IFC_NAND_FIR0_OP1_SHIFT) |
@@ -354,12 +361,8 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned 
int command,
  >ifc_nand.nand_fcr0);
ifc_out32(column, >ifc_nand.row3);
 
-   /*
-* although currently it's 8 bytes for READID, we always read
-* the maximum 256 bytes(for PARAM)
-*/
-   ifc_out32(256, >ifc_nand.nand_fbcr);
-   ifc_nand_ctrl->read_bytes = 256;
+   ifc_out32(len, >ifc_nand.nand_fbcr);
+   ifc_nand_ctrl->read_bytes = len;
 
set_addr(mtd, 0, 0, 0);
fsl_ifc_run_command(mtd);
-- 
1.7.9.5



[PATCH v2 1/2] mtd: rawnand: fsl_ifc: fix FSL NAND driver to read all ONFI parameter pages

2018-05-04 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if the CRC of the first parameter page
read is not valid, the host should read redundant parameter page copies.
Fix FSL NAND driver to read the two redundant copies which are mandatory
in the specification.

Signed-off-by: Jane Wan 
---
 drivers/mtd/nand/raw/fsl_ifc_nand.c |   17 ++---
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c 
b/drivers/mtd/nand/raw/fsl_ifc_nand.c
index 61aae02..98aac1f 100644
--- a/drivers/mtd/nand/raw/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
@@ -342,9 +342,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned 
int command,
 
case NAND_CMD_READID:
case NAND_CMD_PARAM: {
+   /*
+* For READID, read 8 bytes that are currently used.
+* For PARAM, read all 3 copies of 256-bytes pages.
+*/
+   int len = 8;
int timing = IFC_FIR_OP_RB;
-   if (command == NAND_CMD_PARAM)
+   if (command == NAND_CMD_PARAM) {
timing = IFC_FIR_OP_RBCD;
+   len = 256 * 3;
+   }
 
ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
  (IFC_FIR_OP_UA  << IFC_NAND_FIR0_OP1_SHIFT) |
@@ -354,12 +361,8 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned 
int command,
  >ifc_nand.nand_fcr0);
ifc_out32(column, >ifc_nand.row3);
 
-   /*
-* although currently it's 8 bytes for READID, we always read
-* the maximum 256 bytes(for PARAM)
-*/
-   ifc_out32(256, >ifc_nand.nand_fbcr);
-   ifc_nand_ctrl->read_bytes = 256;
+   ifc_out32(len, >ifc_nand.nand_fbcr);
+   ifc_nand_ctrl->read_bytes = len;
 
set_addr(mtd, 0, 0, 0);
fsl_ifc_run_command(mtd);
-- 
1.7.9.5



[PATCH 2/2] Use bit-wise majority to recover the contents of ONFI parameter

2018-04-26 Thread Jane Wan
Signed-off-by: Jane Wan <jane@nokia.com>
---
 drivers/mtd/nand/nand_base.c |   35 +++
 1 file changed, 31 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index c2e1232..161b523 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3153,8 +3153,10 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, 
struct nand_chip *chip,
int *busw)
 {
struct nand_onfi_params *p = >onfi_params;
-   int i, j;
-   int val;
+   int i, j, k, len, val;
+   uint8_t copy[3][256], v8;
+
+   len = (sizeof(*p) > 256) ? 256 : sizeof(*p);
 
/* Try ONFI for unknown chip or LP */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
@@ -3170,11 +3172,36 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, 
struct nand_chip *chip,
le16_to_cpu(p->crc)) {
break;
}
+   pr_err("CRC of parameter page %d is not valid\n", i);
+   for (j = 0; j < len; j++)
+   copy[i][j] = ((uint8_t *)p)[j];
}
 
if (i == 3) {
-   pr_err("Could not find valid ONFI parameter page; aborting\n");
-   return 0;
+   pr_err("Could not find valid ONFI parameter page\n");
+   pr_info("Recover ONFI parameters with bit-wise majority\n");
+   for (j = 0; j < len; j++) {
+   if (copy[0][j] == copy[1][j] ||
+   copy[0][j] == copy[2][j]) {
+   ((uint8_t *)p)[j] = copy[0][j];
+   } else if (copy[1][j] == copy[2][j]) {
+   ((uint8_t *)p)[j] = copy[1][j];
+   } else {
+   ((uint8_t *)p)[j] = 0;
+   for (k = 0; k < 8; k++) {
+   v8 = (copy[0][j] >> k) & 0x1;
+   v8 += (copy[1][j] >> k) & 0x1;
+   v8 += (copy[2][j] >> k) & 0x1;
+   if (v8 > 1)
+   ((uint8_t *)p)[j] |= (1 << k);
+   }
+   }
+   }
+   if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) !=
+   le16_to_cpu(p->crc)) {
+   pr_err("ONFI parameter recovery failed, aborting\n");
+   return 0;
+   }
}
 
/* Check version */
-- 
1.7.9.5



[PATCH 2/2] Use bit-wise majority to recover the contents of ONFI parameter

2018-04-26 Thread Jane Wan
Signed-off-by: Jane Wan 
---
 drivers/mtd/nand/nand_base.c |   35 +++
 1 file changed, 31 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index c2e1232..161b523 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3153,8 +3153,10 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, 
struct nand_chip *chip,
int *busw)
 {
struct nand_onfi_params *p = >onfi_params;
-   int i, j;
-   int val;
+   int i, j, k, len, val;
+   uint8_t copy[3][256], v8;
+
+   len = (sizeof(*p) > 256) ? 256 : sizeof(*p);
 
/* Try ONFI for unknown chip or LP */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
@@ -3170,11 +3172,36 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, 
struct nand_chip *chip,
le16_to_cpu(p->crc)) {
break;
}
+   pr_err("CRC of parameter page %d is not valid\n", i);
+   for (j = 0; j < len; j++)
+   copy[i][j] = ((uint8_t *)p)[j];
}
 
if (i == 3) {
-   pr_err("Could not find valid ONFI parameter page; aborting\n");
-   return 0;
+   pr_err("Could not find valid ONFI parameter page\n");
+   pr_info("Recover ONFI parameters with bit-wise majority\n");
+   for (j = 0; j < len; j++) {
+   if (copy[0][j] == copy[1][j] ||
+   copy[0][j] == copy[2][j]) {
+   ((uint8_t *)p)[j] = copy[0][j];
+   } else if (copy[1][j] == copy[2][j]) {
+   ((uint8_t *)p)[j] = copy[1][j];
+   } else {
+   ((uint8_t *)p)[j] = 0;
+   for (k = 0; k < 8; k++) {
+   v8 = (copy[0][j] >> k) & 0x1;
+   v8 += (copy[1][j] >> k) & 0x1;
+   v8 += (copy[2][j] >> k) & 0x1;
+   if (v8 > 1)
+   ((uint8_t *)p)[j] |= (1 << k);
+   }
+   }
+   }
+   if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) !=
+   le16_to_cpu(p->crc)) {
+   pr_err("ONFI parameter recovery failed, aborting\n");
+   return 0;
+   }
}
 
/* Check version */
-- 
1.7.9.5



[PATCH 1/2] Fix FSL NAND driver to read all ONFI parameter pages

2018-04-26 Thread Jane Wan
Signed-off-by: Jane Wan <jane@nokia.com>
---
 drivers/mtd/nand/fsl_ifc_nand.c |   10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
index ca36b35..a3cf6ca 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -413,6 +413,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned 
int command,
struct fsl_ifc_mtd *priv = chip->priv;
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
+   int len;
 
/* clear the read buffer */
ifc_nand_ctrl->read_bytes = 0;
@@ -462,11 +463,12 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, 
unsigned int command,
ifc_out32(column, >ifc_nand.row3);
 
/*
-* although currently it's 8 bytes for READID, we always read
-* the maximum 256 bytes(for PARAM)
+* For READID, read 8 bytes that are currently used.
+* For PARAM, read all 3 copies of 256-bytes pages.
 */
-   ifc_out32(256, >ifc_nand.nand_fbcr);
-   ifc_nand_ctrl->read_bytes = 256;
+   len = (command == NAND_CMD_PARAM) ? (3 * 256) : 8;
+   ifc_out32(len, >ifc_nand.nand_fbcr);
+   ifc_nand_ctrl->read_bytes = len;
 
set_addr(mtd, 0, 0, 0);
fsl_ifc_run_command(mtd);
-- 
1.7.9.5



[PATCH 1/2] Fix FSL NAND driver to read all ONFI parameter pages

2018-04-26 Thread Jane Wan
Signed-off-by: Jane Wan 
---
 drivers/mtd/nand/fsl_ifc_nand.c |   10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
index ca36b35..a3cf6ca 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -413,6 +413,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned 
int command,
struct fsl_ifc_mtd *priv = chip->priv;
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
+   int len;
 
/* clear the read buffer */
ifc_nand_ctrl->read_bytes = 0;
@@ -462,11 +463,12 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, 
unsigned int command,
ifc_out32(column, >ifc_nand.row3);
 
/*
-* although currently it's 8 bytes for READID, we always read
-* the maximum 256 bytes(for PARAM)
+* For READID, read 8 bytes that are currently used.
+* For PARAM, read all 3 copies of 256-bytes pages.
 */
-   ifc_out32(256, >ifc_nand.nand_fbcr);
-   ifc_nand_ctrl->read_bytes = 256;
+   len = (command == NAND_CMD_PARAM) ? (3 * 256) : 8;
+   ifc_out32(len, >ifc_nand.nand_fbcr);
+   ifc_nand_ctrl->read_bytes = len;
 
set_addr(mtd, 0, 0, 0);
fsl_ifc_run_command(mtd);
-- 
1.7.9.5



[PATCH 0/2] Fix fsl_ifc_nand reading ONFI parameters to meet ONFI spec

2018-04-26 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if the CRC of the first parameter page
read is not valid, the host should read redundant parameter page copies
until it finds a valid copy.  If all three parameter pages have invalid
CRC values, the bit-wise majority may be used to recover the contents of
the parameter pages from the parameter page copies present.

The FSL NAND driver only reads the first page.  The first patch fixes the
driver to read all three parameter pages.  The second patch is the change
for using bit-wise majority to recover the contents of ONFI parameter.

Jane Wan (2):
  Fix FSL NAND driver to read all ONFI parameter pages
  Use bit-wise majority to recover the contents of ONFI parameter

 drivers/mtd/nand/fsl_ifc_nand.c |   10 ++
 drivers/mtd/nand/nand_base.c|   35 +++
 2 files changed, 37 insertions(+), 8 deletions(-)

-- 
1.7.9.5



[PATCH 0/2] Fix fsl_ifc_nand reading ONFI parameters to meet ONFI spec

2018-04-26 Thread Jane Wan
Per ONFI specification (Rev. 4.0), if the CRC of the first parameter page
read is not valid, the host should read redundant parameter page copies
until it finds a valid copy.  If all three parameter pages have invalid
CRC values, the bit-wise majority may be used to recover the contents of
the parameter pages from the parameter page copies present.

The FSL NAND driver only reads the first page.  The first patch fixes the
driver to read all three parameter pages.  The second patch is the change
for using bit-wise majority to recover the contents of ONFI parameter.

Jane Wan (2):
  Fix FSL NAND driver to read all ONFI parameter pages
  Use bit-wise majority to recover the contents of ONFI parameter

 drivers/mtd/nand/fsl_ifc_nand.c |   10 ++
 drivers/mtd/nand/nand_base.c|   35 +++
 2 files changed, 37 insertions(+), 8 deletions(-)

-- 
1.7.9.5



[PATCH] Fix an error that can cause fsl espi task blocked for more than 120 seconds

2015-05-01 Thread Jane Wan
Incorrect condition is used in spin_event_timeout().  When the TX is done, the 
SPIE_NF bit in ESPI_SPIE register is set to 1 to indicate the Tx FIFO is not 
full.  If the bit is 0, it indicates the Tx FIFO is full.

Due to this error, if the Tx FIFO is full at the beginning, but becomes not 
full after handling the Rx FIFO (the SPIE_NF bit is set), the 
spin_event_timeout() returns with timeout occurred.  It causes the interrupt 
handler not to send completion notification to the thread that called 
wait_for_complete() waiting for the notification.

Signed-off-by: Jane Wan 
---
 drivers/spi/spi-fsl-espi.c |6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index 9011e5d..333d5c2 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -551,9 +551,13 @@ void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
 
/* spin until TX is done */
ret = spin_event_timeout(((events = mpc8xxx_spi_read_reg(
-   _base->event)) & SPIE_NF) == 0, 1000, 0);
+   _base->event)) & SPIE_NF), 1000, 0);
if (!ret) {
dev_err(mspi->dev, "tired waiting for SPIE_NF\n");
+
+   /* Clear the SPIE bits */
+   mpc8xxx_spi_write_reg(_base->event, events);
+   complete(>done);
return;
}
}
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


[PATCH] Fix an error that can cause fsl espi task blocked for more than 120 seconds

2015-05-01 Thread Jane Wan
Incorrect condition is used in spin_event_timeout().  When the TX is done, the 
SPIE_NF bit in ESPI_SPIE register is set to 1 to indicate the Tx FIFO is not 
full.  If the bit is 0, it indicates the Tx FIFO is full.

Due to this error, if the Tx FIFO is full at the beginning, but becomes not 
full after handling the Rx FIFO (the SPIE_NF bit is set), the 
spin_event_timeout() returns with timeout occurred.  It causes the interrupt 
handler not to send completion notification to the thread that called 
wait_for_complete() waiting for the notification.

Signed-off-by: Jane Wan jane@gainspeed.com
---
 drivers/spi/spi-fsl-espi.c |6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index 9011e5d..333d5c2 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -551,9 +551,13 @@ void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
 
/* spin until TX is done */
ret = spin_event_timeout(((events = mpc8xxx_spi_read_reg(
-   reg_base-event))  SPIE_NF) == 0, 1000, 0);
+   reg_base-event))  SPIE_NF), 1000, 0);
if (!ret) {
dev_err(mspi-dev, tired waiting for SPIE_NF\n);
+
+   /* Clear the SPIE bits */
+   mpc8xxx_spi_write_reg(reg_base-event, events);
+   complete(mspi-done);
return;
}
}
-- 
1.7.9.5

--
To unsubscribe from this list: send the line unsubscribe linux-kernel in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


[PATCH] Configure FSL eSPI CSBEF and CSAFT

2014-04-16 Thread Jane Wan
Make FSL eSPI CSnBEF and CSnAFT fields in ESPI_SPMODEn registers
(n=0,1,2,3) configurable through device tree.

CSnBEF is the chip select setup time.  It's the delay in bits from the
activation of chip select pin to the first clock for data frame.

CSnAFT is the chip select hold time.  It's the delay in bits from the
last clock for data frame to the deactivation of chip select pin.

The FSL eSPI driver hardcodes CSnBEF and CSnAFT to 0.  Need to set
them to a different value for some device.

Signed-off-by: Jane Wan 
---
 Documentation/devicetree/bindings/spi/fsl-spi.txt |6 
 drivers/spi/spi-fsl-espi.c|   34 ++---
 2 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/spi/fsl-spi.txt 
b/Documentation/devicetree/bindings/spi/fsl-spi.txt
index b032dd7..2c7fd41 100644
--- a/Documentation/devicetree/bindings/spi/fsl-spi.txt
+++ b/Documentation/devicetree/bindings/spi/fsl-spi.txt
@@ -42,6 +42,10 @@ Required properties:
 - interrupts : should contain eSPI interrupt, the device has one interrupt.
 - fsl,espi-num-chipselects : the number of the chipselect signals.
 
+Optional properties:
+- fsl,csbef: chip select assertion time in bits before frame starts
+- fsl,csaft: chip select negation time in bits after frame ends
+
 Example:
spi@11 {
#address-cells = <1>;
@@ -51,4 +55,6 @@ Example:
interrupts = <53 0x2>;
interrupt-parent = <>;
fsl,espi-num-chipselects = <4>;
+   fsl,csbef = <1>;
+   fsl,csaft = <1>;
};
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index 428dc7a..7ff9463 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -590,8 +590,10 @@ static struct spi_master * fsl_espi_probe(struct device 
*dev,
struct spi_master *master;
struct mpc8xxx_spi *mpc8xxx_spi;
struct fsl_espi_reg *reg_base;
-   u32 regval;
-   int i, ret = 0;
+   struct device_node *nc;
+   const __be32 *prop;
+   u32 regval, csmode;
+   int i, len, ret = 0;
 
master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
if (!master) {
@@ -638,8 +640,32 @@ static struct spi_master * fsl_espi_probe(struct device 
*dev,
mpc8xxx_spi_write_reg(_base->event, 0x);
 
/* Init eSPI CS mode register */
-   for (i = 0; i < pdata->max_chipselect; i++)
-   mpc8xxx_spi_write_reg(_base->csmode[i], CSMODE_INIT_VAL);
+   for_each_available_child_of_node(master->dev.of_node, nc) {
+   /* get chip select */
+   prop = of_get_property(nc, "reg", );
+   if (!prop || len < sizeof(*prop))
+   continue;
+   i = be32_to_cpup(prop);
+   if (i < 0 || i >= pdata->max_chipselect)
+   continue;
+
+   csmode = CSMODE_INIT_VAL;
+   /* check if CSBEF is set in device tree */
+   prop = of_get_property(nc, "fsl,csbef", );
+   if (prop && len >= sizeof(*prop)) {
+   csmode &= ~(CSMODE_BEF(0xf));
+   csmode |= CSMODE_BEF(be32_to_cpup(prop));
+   }
+   /* check if CSAFT is set in device tree */
+   prop = of_get_property(nc, "fsl,csaft", );
+   if (prop && len >= sizeof(*prop)) {
+   csmode &= ~(CSMODE_AFT(0xf));
+   csmode |= CSMODE_AFT(be32_to_cpup(prop));
+   }
+   mpc8xxx_spi_write_reg(_base->csmode[i], csmode);
+
+   dev_info(dev, "cs=%d, init_csmode=0x%x\n", i, csmode);
+   }
 
/* Enable SPI interface */
regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


RE: [PATCH] Configure FSL eSPI CSBEF, CSAFT, and whether to send all received data to user

2014-04-16 Thread Jane Wan
On Mon, Apr 14, 2014 at 09:51:56PM +0100, Mark Brown wrote:
> On Sat, Apr 12, 2014 at 11:48:36AM -0700, Jane Wan wrote:
> > Make FSL eSPI CSnBEF and CSnAFT in ESPI_SPMODEn registers (n=0,1,2,3)
> > configurable through device tree.  FSL eSPI driver hardcodes them to 0.
> > Some device requires different value.
> 
> What do these do?

CSnBEF is the chip select setup time.  It's the delay in bits from the 
activation 
of chip select pin to the first clock for data frame.

CSnAFT is the chip select hold time.  It's the delay in bits from the last 
clock 
for data frame to the deactivation of chip select pin.

> > Allow FSL eSPI driver configurable whether to send all received data
> > form slave devices to user.  When user wants to send n_tx bytes and
> > receives n_rx bytes, FSL eSPI driver sends (n_tx + n_rx) bytes on MOSI.
> > For the received (n_tx + n_rx) bytes from MISO, current FSL eSPI
> > driver drops the first n_tx bytes, only passes the last n_rx bytes to user.
> > Some device driver has problem with this.  It requires to know all
> > bytes that the slave device puts on MISO.
> 
> This sounds like a separate patch to the first one, the described behaviour is
> definitely buggy and any correctly implemented Linux driver that does
> bidirectional I/O will have trouble with it.  It should be split out from the 
> new DT
> bindings which are a new feature.
> 
> > ---
> >  drivers/spi/spi-fsl-espi.c   |   68 
> > ++---
> -
> >  1 files changed, 61 insertions(+), 7 deletions(-)
> 
> All DT binding changes need to be documented in the binding document.

The binding document is updated.  The new changes (for CSnBEF and CSnAFT only)  
will 
look like the following.  If this is ok, I'll send it as a separate patch for 
further discussion.

---
 Documentation/devicetree/bindings/spi/fsl-spi.txt |6 
 drivers/spi/spi-fsl-espi.c|   34 ++---
 2 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/spi/fsl-spi.txt 
b/Documentation/devicetree/bindings/spi/fsl-spi.txt
index b032dd7..2c7fd41 100644
--- a/Documentation/devicetree/bindings/spi/fsl-spi.txt
+++ b/Documentation/devicetree/bindings/spi/fsl-spi.txt
@@ -42,6 +42,10 @@ Required properties:
 - interrupts : should contain eSPI interrupt, the device has one interrupt.
 - fsl,espi-num-chipselects : the number of the chipselect signals.
 
+Optional properties:
+- fsl,csbef: chip select assertion time in bits before frame starts
+- fsl,csaft: chip select negation time in bits after frame ends
+
 Example:
spi@11 {
#address-cells = <1>;
@@ -51,4 +55,6 @@ Example:
interrupts = <53 0x2>;
interrupt-parent = <>;
fsl,espi-num-chipselects = <4>;
+   fsl,csbef = <1>;
+   fsl,csaft = <1>;
};
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 
428dc7a..7ff9463 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -590,8 +590,10 @@ static struct spi_master * fsl_espi_probe(struct device 
*dev,
struct spi_master *master;
struct mpc8xxx_spi *mpc8xxx_spi;
struct fsl_espi_reg *reg_base;
-   u32 regval;
-   int i, ret = 0;
+   struct device_node *nc;
+   const __be32 *prop;
+   u32 regval, csmode;
+   int i, len, ret = 0;
 
master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
if (!master) {
@@ -638,8 +640,32 @@ static struct spi_master * fsl_espi_probe(struct device 
*dev,
mpc8xxx_spi_write_reg(_base->event, 0x);
 
/* Init eSPI CS mode register */
-   for (i = 0; i < pdata->max_chipselect; i++)
-   mpc8xxx_spi_write_reg(_base->csmode[i], CSMODE_INIT_VAL);
+   for_each_available_child_of_node(master->dev.of_node, nc) {
+   /* get chip select */
+   prop = of_get_property(nc, "reg", );
+   if (!prop || len < sizeof(*prop))
+   continue;
+   i = be32_to_cpup(prop);
+   if (i < 0 || i >= pdata->max_chipselect)
+   continue;
+
+   csmode = CSMODE_INIT_VAL;
+   /* check if CSBEF is set in device tree */
+   prop = of_get_property(nc, "fsl,csbef", );
+   if (prop && len >= sizeof(*prop)) {
+   csmode &= ~(CSMODE_BEF(0xf));
+   csmode |= CSMODE_BEF(be32_to_cpup(prop));
+   }
+   /* check if CSAFT is set in device tree */
+   prop = of_get_property(nc, "fsl,csaft", );
+   if (prop && len >= sizeof(*prop)) {
+  

RE: [PATCH] Configure FSL eSPI CSBEF, CSAFT, and whether to send all received data to user

2014-04-16 Thread Jane Wan
On Mon, Apr 14, 2014 at 09:51:56PM +0100, Mark Brown wrote:
 On Sat, Apr 12, 2014 at 11:48:36AM -0700, Jane Wan wrote:
  Make FSL eSPI CSnBEF and CSnAFT in ESPI_SPMODEn registers (n=0,1,2,3)
  configurable through device tree.  FSL eSPI driver hardcodes them to 0.
  Some device requires different value.
 
 What do these do?

CSnBEF is the chip select setup time.  It's the delay in bits from the 
activation 
of chip select pin to the first clock for data frame.

CSnAFT is the chip select hold time.  It's the delay in bits from the last 
clock 
for data frame to the deactivation of chip select pin.

  Allow FSL eSPI driver configurable whether to send all received data
  form slave devices to user.  When user wants to send n_tx bytes and
  receives n_rx bytes, FSL eSPI driver sends (n_tx + n_rx) bytes on MOSI.
  For the received (n_tx + n_rx) bytes from MISO, current FSL eSPI
  driver drops the first n_tx bytes, only passes the last n_rx bytes to user.
  Some device driver has problem with this.  It requires to know all
  bytes that the slave device puts on MISO.
 
 This sounds like a separate patch to the first one, the described behaviour is
 definitely buggy and any correctly implemented Linux driver that does
 bidirectional I/O will have trouble with it.  It should be split out from the 
 new DT
 bindings which are a new feature.
 
  ---
   drivers/spi/spi-fsl-espi.c   |   68 
  ++---
 -
   1 files changed, 61 insertions(+), 7 deletions(-)
 
 All DT binding changes need to be documented in the binding document.

The binding document is updated.  The new changes (for CSnBEF and CSnAFT only)  
will 
look like the following.  If this is ok, I'll send it as a separate patch for 
further discussion.

---
 Documentation/devicetree/bindings/spi/fsl-spi.txt |6 
 drivers/spi/spi-fsl-espi.c|   34 ++---
 2 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/spi/fsl-spi.txt 
b/Documentation/devicetree/bindings/spi/fsl-spi.txt
index b032dd7..2c7fd41 100644
--- a/Documentation/devicetree/bindings/spi/fsl-spi.txt
+++ b/Documentation/devicetree/bindings/spi/fsl-spi.txt
@@ -42,6 +42,10 @@ Required properties:
 - interrupts : should contain eSPI interrupt, the device has one interrupt.
 - fsl,espi-num-chipselects : the number of the chipselect signals.
 
+Optional properties:
+- fsl,csbef: chip select assertion time in bits before frame starts
+- fsl,csaft: chip select negation time in bits after frame ends
+
 Example:
spi@11 {
#address-cells = 1;
@@ -51,4 +55,6 @@ Example:
interrupts = 53 0x2;
interrupt-parent = mpic;
fsl,espi-num-chipselects = 4;
+   fsl,csbef = 1;
+   fsl,csaft = 1;
};
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 
428dc7a..7ff9463 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -590,8 +590,10 @@ static struct spi_master * fsl_espi_probe(struct device 
*dev,
struct spi_master *master;
struct mpc8xxx_spi *mpc8xxx_spi;
struct fsl_espi_reg *reg_base;
-   u32 regval;
-   int i, ret = 0;
+   struct device_node *nc;
+   const __be32 *prop;
+   u32 regval, csmode;
+   int i, len, ret = 0;
 
master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
if (!master) {
@@ -638,8 +640,32 @@ static struct spi_master * fsl_espi_probe(struct device 
*dev,
mpc8xxx_spi_write_reg(reg_base-event, 0x);
 
/* Init eSPI CS mode register */
-   for (i = 0; i  pdata-max_chipselect; i++)
-   mpc8xxx_spi_write_reg(reg_base-csmode[i], CSMODE_INIT_VAL);
+   for_each_available_child_of_node(master-dev.of_node, nc) {
+   /* get chip select */
+   prop = of_get_property(nc, reg, len);
+   if (!prop || len  sizeof(*prop))
+   continue;
+   i = be32_to_cpup(prop);
+   if (i  0 || i = pdata-max_chipselect)
+   continue;
+
+   csmode = CSMODE_INIT_VAL;
+   /* check if CSBEF is set in device tree */
+   prop = of_get_property(nc, fsl,csbef, len);
+   if (prop  len = sizeof(*prop)) {
+   csmode = ~(CSMODE_BEF(0xf));
+   csmode |= CSMODE_BEF(be32_to_cpup(prop));
+   }
+   /* check if CSAFT is set in device tree */
+   prop = of_get_property(nc, fsl,csaft, len);
+   if (prop  len = sizeof(*prop)) {
+   csmode = ~(CSMODE_AFT(0xf));
+   csmode |= CSMODE_AFT(be32_to_cpup(prop));
+   }
+   mpc8xxx_spi_write_reg(reg_base-csmode[i], csmode);
+
+   dev_info(dev, cs=%d, init_csmode=0x%x\n, i, csmode);
+   }
 
/* Enable SPI interface

[PATCH] Configure FSL eSPI CSBEF and CSAFT

2014-04-16 Thread Jane Wan
Make FSL eSPI CSnBEF and CSnAFT fields in ESPI_SPMODEn registers
(n=0,1,2,3) configurable through device tree.

CSnBEF is the chip select setup time.  It's the delay in bits from the
activation of chip select pin to the first clock for data frame.

CSnAFT is the chip select hold time.  It's the delay in bits from the
last clock for data frame to the deactivation of chip select pin.

The FSL eSPI driver hardcodes CSnBEF and CSnAFT to 0.  Need to set
them to a different value for some device.

Signed-off-by: Jane Wan jane@gainspeed.com
---
 Documentation/devicetree/bindings/spi/fsl-spi.txt |6 
 drivers/spi/spi-fsl-espi.c|   34 ++---
 2 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/spi/fsl-spi.txt 
b/Documentation/devicetree/bindings/spi/fsl-spi.txt
index b032dd7..2c7fd41 100644
--- a/Documentation/devicetree/bindings/spi/fsl-spi.txt
+++ b/Documentation/devicetree/bindings/spi/fsl-spi.txt
@@ -42,6 +42,10 @@ Required properties:
 - interrupts : should contain eSPI interrupt, the device has one interrupt.
 - fsl,espi-num-chipselects : the number of the chipselect signals.
 
+Optional properties:
+- fsl,csbef: chip select assertion time in bits before frame starts
+- fsl,csaft: chip select negation time in bits after frame ends
+
 Example:
spi@11 {
#address-cells = 1;
@@ -51,4 +55,6 @@ Example:
interrupts = 53 0x2;
interrupt-parent = mpic;
fsl,espi-num-chipselects = 4;
+   fsl,csbef = 1;
+   fsl,csaft = 1;
};
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index 428dc7a..7ff9463 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -590,8 +590,10 @@ static struct spi_master * fsl_espi_probe(struct device 
*dev,
struct spi_master *master;
struct mpc8xxx_spi *mpc8xxx_spi;
struct fsl_espi_reg *reg_base;
-   u32 regval;
-   int i, ret = 0;
+   struct device_node *nc;
+   const __be32 *prop;
+   u32 regval, csmode;
+   int i, len, ret = 0;
 
master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
if (!master) {
@@ -638,8 +640,32 @@ static struct spi_master * fsl_espi_probe(struct device 
*dev,
mpc8xxx_spi_write_reg(reg_base-event, 0x);
 
/* Init eSPI CS mode register */
-   for (i = 0; i  pdata-max_chipselect; i++)
-   mpc8xxx_spi_write_reg(reg_base-csmode[i], CSMODE_INIT_VAL);
+   for_each_available_child_of_node(master-dev.of_node, nc) {
+   /* get chip select */
+   prop = of_get_property(nc, reg, len);
+   if (!prop || len  sizeof(*prop))
+   continue;
+   i = be32_to_cpup(prop);
+   if (i  0 || i = pdata-max_chipselect)
+   continue;
+
+   csmode = CSMODE_INIT_VAL;
+   /* check if CSBEF is set in device tree */
+   prop = of_get_property(nc, fsl,csbef, len);
+   if (prop  len = sizeof(*prop)) {
+   csmode = ~(CSMODE_BEF(0xf));
+   csmode |= CSMODE_BEF(be32_to_cpup(prop));
+   }
+   /* check if CSAFT is set in device tree */
+   prop = of_get_property(nc, fsl,csaft, len);
+   if (prop  len = sizeof(*prop)) {
+   csmode = ~(CSMODE_AFT(0xf));
+   csmode |= CSMODE_AFT(be32_to_cpup(prop));
+   }
+   mpc8xxx_spi_write_reg(reg_base-csmode[i], csmode);
+
+   dev_info(dev, cs=%d, init_csmode=0x%x\n, i, csmode);
+   }
 
/* Enable SPI interface */
regval = pdata-initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
-- 
1.7.9.5

--
To unsubscribe from this list: send the line unsubscribe linux-kernel in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


[PATCH] Configure FSL eSPI CSBEF, CSAFT, and whether to send all received data to user

2014-04-12 Thread Jane Wan
Make FSL eSPI properties configurable through device tree.  The
configurable parameters include CSnBEF and CSnAFT in ESPI_SPMODEn
registers (n=0,1,2,3), and whether to send all received data to user.

The FSL eSPI driver hardcodes CSnBEF and CSnAFT to 0.  Some device
needs to set them to different values.

When user sends n_tx bytes and receives n_rx bytes on FSL eSPI
interface, the FSL eSPI driver sends (n_tx + n_rx) bytes on MOSI.
Simultaniously, there are (n_tx + n_rx) received on MISO.  The FSL
eSPI driver only passes the last n_rx bytes to user device driver.
The other n_tx bytes received from the slave device are dropped.  
Some device has issue with this mechanism.  The device driver
requires to know all bytes that the slave device puts on MISO.

Part of this fix is based on a previous patch that was not included,
see 
http://www.mail-archive.com/spi-devel-general@lists.sourceforge.net/msg08658.html

Test done:
- Kernel version 3.8.13
- Three devices attached to FSL eSPI interface with chip select 0-2.
- The device at cs0 and cs1 work with original FSL eSPI driver.
- The device driver at cs2 has issue with original FSL eSPI driver.
  It requires to receive all data that the slave device puts on MISO.
  It also requires CS2BEF in SPI_SPMODE2 register to be set to 1.
- After set proper values in the device tree (as example below), all
  three  devices work fine with the FSL eSPI driver.
spi@11 {
redpine@2 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "spidev";
reg = <2>;
spi-max-frequency = <1000>; /* input clock */
fsl,csbef = <1>; /* CS assertiion time in bits before 
frame start */
fsl,csaft = <1>; /* CS assertiion time in bits after 
frame end */
fsl,spi-raw-rxdata-to-user = <1>; /* send all rx data 
to user */
};
};


Jane Wan (1):
  Configure fsl espi CSBEF, CSAFT, and whether to send all received
data to user

 drivers/spi/spi-fsl-espi.c   |   68 ++
 1 files changed, 61 insertions(+), 7 deletions(-)

-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


[PATCH] Configure FSL eSPI CSBEF, CSAFT, and whether to send all received data to user

2014-04-12 Thread Jane Wan
Make FSL eSPI CSnBEF and CSnAFT in ESPI_SPMODEn registers (n=0,1,2,3)
configurable through device tree.  FSL eSPI driver hardcodes them to 0.
Some device requires different value.

Allow FSL eSPI driver configurable whether to send all received data
form slave devices to user.  When user wants to send n_tx bytes and
receives n_rx bytes, FSL eSPI driver sends (n_tx + n_rx) bytes on MOSI.
For the received (n_tx + n_rx) bytes from MISO, current FSL eSPI driver
drops the first n_tx bytes, only passes the last n_rx bytes to user.
Some device driver has problem with this.  It requires to know all bytes
that the slave device puts on MISO.

Part of this fix is based on a previous patch:
http://www.mail-archive.com/spi-devel-general@lists.sourceforge.net/msg08658.html

Signed-off-by: Jane Wan 
---
 drivers/spi/spi-fsl-espi.c   |   68 ++
 1 files changed, 61 insertions(+), 7 deletions(-)

diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index 0622165..660039c 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -45,6 +45,9 @@ struct fsl_espi_transfer {
int status;
 };
 
+/* whether to send all rx data to user per chip select */
+static u8 *spi_raw_rxdata_to_user;
+
 /* eSPI Controller mode register definitions */
 #define SPMODE_ENABLE  (1 << 31)
 #define SPMODE_LOOP(1 << 30)
@@ -398,12 +401,19 @@ static void fsl_espi_rw_trans(struct spi_message *m,
 
espi_trans->n_tx = n_tx;
espi_trans->n_rx = trans_len;
-   espi_trans->len = trans_len + n_tx;
+   if (spi_raw_rxdata_to_user[m->spi->chip_select])
+   espi_trans->len = n_tx;
+   else
+   espi_trans->len = trans_len + n_tx;
espi_trans->tx_buf = local_buf;
espi_trans->rx_buf = local_buf + n_tx;
fsl_espi_do_trans(m, espi_trans);
 
-   memcpy(rx_buf + pos, espi_trans->rx_buf + n_tx, trans_len);
+   if (spi_raw_rxdata_to_user[m->spi->chip_select])
+   memcpy(rx_buf + pos, espi_trans->rx_buf, trans_len);
+   else
+   memcpy(rx_buf + pos, espi_trans->rx_buf + n_tx,
+   trans_len);
 
if (loop > 0)
espi_trans->actual_length += espi_trans->len - n_tx;
@@ -433,7 +443,10 @@ static void fsl_espi_do_one_msg(struct spi_message *m)
 
espi_trans.n_tx = n_tx;
espi_trans.n_rx = n_rx;
-   espi_trans.len = n_tx + n_rx;
+   if (spi_raw_rxdata_to_user[m->spi->chip_select])
+   espi_trans.len = n_tx;
+   else
+   espi_trans.len = n_tx + n_rx;
espi_trans.actual_length = 0;
espi_trans.status = 0;
 
@@ -579,6 +592,7 @@ static irqreturn_t fsl_espi_irq(s32 irq, void *context_data)
 static void fsl_espi_remove(struct mpc8xxx_spi *mspi)
 {
iounmap(mspi->reg_base);
+   kfree(spi_raw_rxdata_to_user);
 }
 
 static struct spi_master * fsl_espi_probe(struct device *dev,
@@ -588,8 +602,10 @@ static struct spi_master * fsl_espi_probe(struct device 
*dev,
struct spi_master *master;
struct mpc8xxx_spi *mpc8xxx_spi;
struct fsl_espi_reg *reg_base;
-   u32 regval;
-   int i, ret = 0;
+   struct device_node *nc;
+   const __be32 *prop;
+   u32 regval, csmode;
+   int i, len, ret = 0;
 
master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
if (!master) {
@@ -635,9 +651,45 @@ static struct spi_master * fsl_espi_probe(struct device 
*dev,
mpc8xxx_spi_write_reg(_base->command, 0);
mpc8xxx_spi_write_reg(_base->event, 0x);
 
+   spi_raw_rxdata_to_user = kzalloc(pdata->max_chipselect, GFP_KERNEL);
+   if (!spi_raw_rxdata_to_user) {
+   ret = -ENOMEM;
+   goto err_spi_raw_rxdata_to_user;
+   }
+
/* Init eSPI CS mode register */
-   for (i = 0; i < pdata->max_chipselect; i++)
-   mpc8xxx_spi_write_reg(_base->csmode[i], CSMODE_INIT_VAL);
+   for_each_available_child_of_node(master->dev.of_node, nc) {
+   /* get chip select */
+   prop = of_get_property(nc, "reg", );
+   if (!prop || len < sizeof(*prop))
+   continue;
+   i = be32_to_cpup(prop);
+   if (i < 0 || i >= pdata->max_chipselect)
+   continue;
+
+   csmode = CSMODE_INIT_VAL;
+   /* check if CSBEF is set in device tree */
+   prop = of_get_property(nc, "fsl,csbef", );
+   if (prop && len >= sizeof(*prop)) {
+   csmode &= ~(CSMODE_BEF(0xf));
+   csmode |= CSMODE_BEF(be32_to_cpu

[PATCH] Configure FSL eSPI CSBEF, CSAFT, and whether to send all received data to user

2014-04-12 Thread Jane Wan
Make FSL eSPI CSnBEF and CSnAFT in ESPI_SPMODEn registers (n=0,1,2,3)
configurable through device tree.  FSL eSPI driver hardcodes them to 0.
Some device requires different value.

Allow FSL eSPI driver configurable whether to send all received data
form slave devices to user.  When user wants to send n_tx bytes and
receives n_rx bytes, FSL eSPI driver sends (n_tx + n_rx) bytes on MOSI.
For the received (n_tx + n_rx) bytes from MISO, current FSL eSPI driver
drops the first n_tx bytes, only passes the last n_rx bytes to user.
Some device driver has problem with this.  It requires to know all bytes
that the slave device puts on MISO.

Part of this fix is based on a previous patch:
http://www.mail-archive.com/spi-devel-general@lists.sourceforge.net/msg08658.html

Signed-off-by: Jane Wan jane@gainspeed.com
---
 drivers/spi/spi-fsl-espi.c   |   68 ++
 1 files changed, 61 insertions(+), 7 deletions(-)

diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index 0622165..660039c 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -45,6 +45,9 @@ struct fsl_espi_transfer {
int status;
 };
 
+/* whether to send all rx data to user per chip select */
+static u8 *spi_raw_rxdata_to_user;
+
 /* eSPI Controller mode register definitions */
 #define SPMODE_ENABLE  (1  31)
 #define SPMODE_LOOP(1  30)
@@ -398,12 +401,19 @@ static void fsl_espi_rw_trans(struct spi_message *m,
 
espi_trans-n_tx = n_tx;
espi_trans-n_rx = trans_len;
-   espi_trans-len = trans_len + n_tx;
+   if (spi_raw_rxdata_to_user[m-spi-chip_select])
+   espi_trans-len = n_tx;
+   else
+   espi_trans-len = trans_len + n_tx;
espi_trans-tx_buf = local_buf;
espi_trans-rx_buf = local_buf + n_tx;
fsl_espi_do_trans(m, espi_trans);
 
-   memcpy(rx_buf + pos, espi_trans-rx_buf + n_tx, trans_len);
+   if (spi_raw_rxdata_to_user[m-spi-chip_select])
+   memcpy(rx_buf + pos, espi_trans-rx_buf, trans_len);
+   else
+   memcpy(rx_buf + pos, espi_trans-rx_buf + n_tx,
+   trans_len);
 
if (loop  0)
espi_trans-actual_length += espi_trans-len - n_tx;
@@ -433,7 +443,10 @@ static void fsl_espi_do_one_msg(struct spi_message *m)
 
espi_trans.n_tx = n_tx;
espi_trans.n_rx = n_rx;
-   espi_trans.len = n_tx + n_rx;
+   if (spi_raw_rxdata_to_user[m-spi-chip_select])
+   espi_trans.len = n_tx;
+   else
+   espi_trans.len = n_tx + n_rx;
espi_trans.actual_length = 0;
espi_trans.status = 0;
 
@@ -579,6 +592,7 @@ static irqreturn_t fsl_espi_irq(s32 irq, void *context_data)
 static void fsl_espi_remove(struct mpc8xxx_spi *mspi)
 {
iounmap(mspi-reg_base);
+   kfree(spi_raw_rxdata_to_user);
 }
 
 static struct spi_master * fsl_espi_probe(struct device *dev,
@@ -588,8 +602,10 @@ static struct spi_master * fsl_espi_probe(struct device 
*dev,
struct spi_master *master;
struct mpc8xxx_spi *mpc8xxx_spi;
struct fsl_espi_reg *reg_base;
-   u32 regval;
-   int i, ret = 0;
+   struct device_node *nc;
+   const __be32 *prop;
+   u32 regval, csmode;
+   int i, len, ret = 0;
 
master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
if (!master) {
@@ -635,9 +651,45 @@ static struct spi_master * fsl_espi_probe(struct device 
*dev,
mpc8xxx_spi_write_reg(reg_base-command, 0);
mpc8xxx_spi_write_reg(reg_base-event, 0x);
 
+   spi_raw_rxdata_to_user = kzalloc(pdata-max_chipselect, GFP_KERNEL);
+   if (!spi_raw_rxdata_to_user) {
+   ret = -ENOMEM;
+   goto err_spi_raw_rxdata_to_user;
+   }
+
/* Init eSPI CS mode register */
-   for (i = 0; i  pdata-max_chipselect; i++)
-   mpc8xxx_spi_write_reg(reg_base-csmode[i], CSMODE_INIT_VAL);
+   for_each_available_child_of_node(master-dev.of_node, nc) {
+   /* get chip select */
+   prop = of_get_property(nc, reg, len);
+   if (!prop || len  sizeof(*prop))
+   continue;
+   i = be32_to_cpup(prop);
+   if (i  0 || i = pdata-max_chipselect)
+   continue;
+
+   csmode = CSMODE_INIT_VAL;
+   /* check if CSBEF is set in device tree */
+   prop = of_get_property(nc, fsl,csbef, len);
+   if (prop  len = sizeof(*prop)) {
+   csmode = ~(CSMODE_BEF(0xf));
+   csmode |= CSMODE_BEF(be32_to_cpup(prop));
+   }
+   /* check if CSAFT is set in device tree */
+   prop = of_get_property(nc, fsl,csaft, len);
+   if (prop

[PATCH] Configure FSL eSPI CSBEF, CSAFT, and whether to send all received data to user

2014-04-12 Thread Jane Wan
Make FSL eSPI properties configurable through device tree.  The
configurable parameters include CSnBEF and CSnAFT in ESPI_SPMODEn
registers (n=0,1,2,3), and whether to send all received data to user.

The FSL eSPI driver hardcodes CSnBEF and CSnAFT to 0.  Some device
needs to set them to different values.

When user sends n_tx bytes and receives n_rx bytes on FSL eSPI
interface, the FSL eSPI driver sends (n_tx + n_rx) bytes on MOSI.
Simultaniously, there are (n_tx + n_rx) received on MISO.  The FSL
eSPI driver only passes the last n_rx bytes to user device driver.
The other n_tx bytes received from the slave device are dropped.  
Some device has issue with this mechanism.  The device driver
requires to know all bytes that the slave device puts on MISO.

Part of this fix is based on a previous patch that was not included,
see 
http://www.mail-archive.com/spi-devel-general@lists.sourceforge.net/msg08658.html

Test done:
- Kernel version 3.8.13
- Three devices attached to FSL eSPI interface with chip select 0-2.
- The device at cs0 and cs1 work with original FSL eSPI driver.
- The device driver at cs2 has issue with original FSL eSPI driver.
  It requires to receive all data that the slave device puts on MISO.
  It also requires CS2BEF in SPI_SPMODE2 register to be set to 1.
- After set proper values in the device tree (as example below), all
  three  devices work fine with the FSL eSPI driver.
spi@11 {
redpine@2 {
#address-cells = 1;
#size-cells = 1;
compatible = spidev;
reg = 2;
spi-max-frequency = 1000; /* input clock */
fsl,csbef = 1; /* CS assertiion time in bits before 
frame start */
fsl,csaft = 1; /* CS assertiion time in bits after 
frame end */
fsl,spi-raw-rxdata-to-user = 1; /* send all rx data 
to user */
};
};


Jane Wan (1):
  Configure fsl espi CSBEF, CSAFT, and whether to send all received
data to user

 drivers/spi/spi-fsl-espi.c   |   68 ++
 1 files changed, 61 insertions(+), 7 deletions(-)

-- 
1.7.9.5

--
To unsubscribe from this list: send the line unsubscribe linux-kernel in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/