root/drivers/mtd/nand/raw/ingenic/jz4740_ecc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. jz4740_ecc_reset
  2. jz4740_ecc_calculate
  3. jz_nand_correct_data
  4. jz4740_ecc_correct
  5. jz4740_ecc_disable

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * JZ4740 ECC controller driver
   4  *
   5  * Copyright (c) 2019 Paul Cercueil <paul@crapouillou.net>
   6  *
   7  * based on jz4740-nand.c
   8  */
   9 
  10 #include <linux/bitops.h>
  11 #include <linux/device.h>
  12 #include <linux/io.h>
  13 #include <linux/module.h>
  14 #include <linux/of_platform.h>
  15 #include <linux/platform_device.h>
  16 
  17 #include "ingenic_ecc.h"
  18 
  19 #define JZ_REG_NAND_ECC_CTRL    0x00
  20 #define JZ_REG_NAND_DATA        0x04
  21 #define JZ_REG_NAND_PAR0        0x08
  22 #define JZ_REG_NAND_PAR1        0x0C
  23 #define JZ_REG_NAND_PAR2        0x10
  24 #define JZ_REG_NAND_IRQ_STAT    0x14
  25 #define JZ_REG_NAND_IRQ_CTRL    0x18
  26 #define JZ_REG_NAND_ERR(x)      (0x1C + ((x) << 2))
  27 
  28 #define JZ_NAND_ECC_CTRL_PAR_READY      BIT(4)
  29 #define JZ_NAND_ECC_CTRL_ENCODING       BIT(3)
  30 #define JZ_NAND_ECC_CTRL_RS             BIT(2)
  31 #define JZ_NAND_ECC_CTRL_RESET          BIT(1)
  32 #define JZ_NAND_ECC_CTRL_ENABLE         BIT(0)
  33 
  34 #define JZ_NAND_STATUS_ERR_COUNT        (BIT(31) | BIT(30) | BIT(29))
  35 #define JZ_NAND_STATUS_PAD_FINISH       BIT(4)
  36 #define JZ_NAND_STATUS_DEC_FINISH       BIT(3)
  37 #define JZ_NAND_STATUS_ENC_FINISH       BIT(2)
  38 #define JZ_NAND_STATUS_UNCOR_ERROR      BIT(1)
  39 #define JZ_NAND_STATUS_ERROR            BIT(0)
  40 
  41 static const uint8_t empty_block_ecc[] = {
  42         0xcd, 0x9d, 0x90, 0x58, 0xf4, 0x8b, 0xff, 0xb7, 0x6f
  43 };
  44 
  45 static void jz4740_ecc_reset(struct ingenic_ecc *ecc, bool calc_ecc)
  46 {
  47         uint32_t reg;
  48 
  49         /* Clear interrupt status */
  50         writel(0, ecc->base + JZ_REG_NAND_IRQ_STAT);
  51 
  52         /* Initialize and enable ECC hardware */
  53         reg = readl(ecc->base + JZ_REG_NAND_ECC_CTRL);
  54         reg |= JZ_NAND_ECC_CTRL_RESET;
  55         reg |= JZ_NAND_ECC_CTRL_ENABLE;
  56         reg |= JZ_NAND_ECC_CTRL_RS;
  57         if (calc_ecc) /* calculate ECC from data */
  58                 reg |= JZ_NAND_ECC_CTRL_ENCODING;
  59         else /* correct data from ECC */
  60                 reg &= ~JZ_NAND_ECC_CTRL_ENCODING;
  61 
  62         writel(reg, ecc->base + JZ_REG_NAND_ECC_CTRL);
  63 }
  64 
  65 static int jz4740_ecc_calculate(struct ingenic_ecc *ecc,
  66                                 struct ingenic_ecc_params *params,
  67                                 const u8 *buf, u8 *ecc_code)
  68 {
  69         uint32_t reg, status;
  70         unsigned int timeout = 1000;
  71         int i;
  72 
  73         jz4740_ecc_reset(ecc, true);
  74 
  75         do {
  76                 status = readl(ecc->base + JZ_REG_NAND_IRQ_STAT);
  77         } while (!(status & JZ_NAND_STATUS_ENC_FINISH) && --timeout);
  78 
  79         if (timeout == 0)
  80                 return -ETIMEDOUT;
  81 
  82         reg = readl(ecc->base + JZ_REG_NAND_ECC_CTRL);
  83         reg &= ~JZ_NAND_ECC_CTRL_ENABLE;
  84         writel(reg, ecc->base + JZ_REG_NAND_ECC_CTRL);
  85 
  86         for (i = 0; i < params->bytes; ++i)
  87                 ecc_code[i] = readb(ecc->base + JZ_REG_NAND_PAR0 + i);
  88 
  89         /*
  90          * If the written data is completely 0xff, we also want to write 0xff as
  91          * ECC, otherwise we will get in trouble when doing subpage writes.
  92          */
  93         if (memcmp(ecc_code, empty_block_ecc, ARRAY_SIZE(empty_block_ecc)) == 0)
  94                 memset(ecc_code, 0xff, ARRAY_SIZE(empty_block_ecc));
  95 
  96         return 0;
  97 }
  98 
  99 static void jz_nand_correct_data(uint8_t *buf, int index, int mask)
 100 {
 101         int offset = index & 0x7;
 102         uint16_t data;
 103 
 104         index += (index >> 3);
 105 
 106         data = buf[index];
 107         data |= buf[index + 1] << 8;
 108 
 109         mask ^= (data >> offset) & 0x1ff;
 110         data &= ~(0x1ff << offset);
 111         data |= (mask << offset);
 112 
 113         buf[index] = data & 0xff;
 114         buf[index + 1] = (data >> 8) & 0xff;
 115 }
 116 
 117 static int jz4740_ecc_correct(struct ingenic_ecc *ecc,
 118                               struct ingenic_ecc_params *params,
 119                               u8 *buf, u8 *ecc_code)
 120 {
 121         int i, error_count, index;
 122         uint32_t reg, status, error;
 123         unsigned int timeout = 1000;
 124 
 125         jz4740_ecc_reset(ecc, false);
 126 
 127         for (i = 0; i < params->bytes; ++i)
 128                 writeb(ecc_code[i], ecc->base + JZ_REG_NAND_PAR0 + i);
 129 
 130         reg = readl(ecc->base + JZ_REG_NAND_ECC_CTRL);
 131         reg |= JZ_NAND_ECC_CTRL_PAR_READY;
 132         writel(reg, ecc->base + JZ_REG_NAND_ECC_CTRL);
 133 
 134         do {
 135                 status = readl(ecc->base + JZ_REG_NAND_IRQ_STAT);
 136         } while (!(status & JZ_NAND_STATUS_DEC_FINISH) && --timeout);
 137 
 138         if (timeout == 0)
 139                 return -ETIMEDOUT;
 140 
 141         reg = readl(ecc->base + JZ_REG_NAND_ECC_CTRL);
 142         reg &= ~JZ_NAND_ECC_CTRL_ENABLE;
 143         writel(reg, ecc->base + JZ_REG_NAND_ECC_CTRL);
 144 
 145         if (status & JZ_NAND_STATUS_ERROR) {
 146                 if (status & JZ_NAND_STATUS_UNCOR_ERROR)
 147                         return -EBADMSG;
 148 
 149                 error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29;
 150 
 151                 for (i = 0; i < error_count; ++i) {
 152                         error = readl(ecc->base + JZ_REG_NAND_ERR(i));
 153                         index = ((error >> 16) & 0x1ff) - 1;
 154                         if (index >= 0 && index < params->size)
 155                                 jz_nand_correct_data(buf, index, error & 0x1ff);
 156                 }
 157 
 158                 return error_count;
 159         }
 160 
 161         return 0;
 162 }
 163 
 164 static void jz4740_ecc_disable(struct ingenic_ecc *ecc)
 165 {
 166         u32 reg;
 167 
 168         writel(0, ecc->base + JZ_REG_NAND_IRQ_STAT);
 169         reg = readl(ecc->base + JZ_REG_NAND_ECC_CTRL);
 170         reg &= ~JZ_NAND_ECC_CTRL_ENABLE;
 171         writel(reg, ecc->base + JZ_REG_NAND_ECC_CTRL);
 172 }
 173 
 174 static const struct ingenic_ecc_ops jz4740_ecc_ops = {
 175         .disable = jz4740_ecc_disable,
 176         .calculate = jz4740_ecc_calculate,
 177         .correct = jz4740_ecc_correct,
 178 };
 179 
 180 static const struct of_device_id jz4740_ecc_dt_match[] = {
 181         { .compatible = "ingenic,jz4740-ecc", .data = &jz4740_ecc_ops },
 182         {},
 183 };
 184 MODULE_DEVICE_TABLE(of, jz4740_ecc_dt_match);
 185 
 186 static struct platform_driver jz4740_ecc_driver = {
 187         .probe          = ingenic_ecc_probe,
 188         .driver = {
 189                 .name   = "jz4740-ecc",
 190                 .of_match_table = jz4740_ecc_dt_match,
 191         },
 192 };
 193 module_platform_driver(jz4740_ecc_driver);
 194 
 195 MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
 196 MODULE_DESCRIPTION("Ingenic JZ4740 ECC controller driver");
 197 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */