Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * JZ4740 ECC controller driver
0004  *
0005  * Copyright (c) 2019 Paul Cercueil <paul@crapouillou.net>
0006  *
0007  * based on jz4740-nand.c
0008  */
0009 
0010 #include <linux/bitops.h>
0011 #include <linux/device.h>
0012 #include <linux/io.h>
0013 #include <linux/module.h>
0014 #include <linux/of_platform.h>
0015 #include <linux/platform_device.h>
0016 
0017 #include "ingenic_ecc.h"
0018 
0019 #define JZ_REG_NAND_ECC_CTRL    0x00
0020 #define JZ_REG_NAND_DATA    0x04
0021 #define JZ_REG_NAND_PAR0    0x08
0022 #define JZ_REG_NAND_PAR1    0x0C
0023 #define JZ_REG_NAND_PAR2    0x10
0024 #define JZ_REG_NAND_IRQ_STAT    0x14
0025 #define JZ_REG_NAND_IRQ_CTRL    0x18
0026 #define JZ_REG_NAND_ERR(x)  (0x1C + ((x) << 2))
0027 
0028 #define JZ_NAND_ECC_CTRL_PAR_READY  BIT(4)
0029 #define JZ_NAND_ECC_CTRL_ENCODING   BIT(3)
0030 #define JZ_NAND_ECC_CTRL_RS     BIT(2)
0031 #define JZ_NAND_ECC_CTRL_RESET      BIT(1)
0032 #define JZ_NAND_ECC_CTRL_ENABLE     BIT(0)
0033 
0034 #define JZ_NAND_STATUS_ERR_COUNT    (BIT(31) | BIT(30) | BIT(29))
0035 #define JZ_NAND_STATUS_PAD_FINISH   BIT(4)
0036 #define JZ_NAND_STATUS_DEC_FINISH   BIT(3)
0037 #define JZ_NAND_STATUS_ENC_FINISH   BIT(2)
0038 #define JZ_NAND_STATUS_UNCOR_ERROR  BIT(1)
0039 #define JZ_NAND_STATUS_ERROR        BIT(0)
0040 
0041 static const uint8_t empty_block_ecc[] = {
0042     0xcd, 0x9d, 0x90, 0x58, 0xf4, 0x8b, 0xff, 0xb7, 0x6f
0043 };
0044 
0045 static void jz4740_ecc_reset(struct ingenic_ecc *ecc, bool calc_ecc)
0046 {
0047     uint32_t reg;
0048 
0049     /* Clear interrupt status */
0050     writel(0, ecc->base + JZ_REG_NAND_IRQ_STAT);
0051 
0052     /* Initialize and enable ECC hardware */
0053     reg = readl(ecc->base + JZ_REG_NAND_ECC_CTRL);
0054     reg |= JZ_NAND_ECC_CTRL_RESET;
0055     reg |= JZ_NAND_ECC_CTRL_ENABLE;
0056     reg |= JZ_NAND_ECC_CTRL_RS;
0057     if (calc_ecc) /* calculate ECC from data */
0058         reg |= JZ_NAND_ECC_CTRL_ENCODING;
0059     else /* correct data from ECC */
0060         reg &= ~JZ_NAND_ECC_CTRL_ENCODING;
0061 
0062     writel(reg, ecc->base + JZ_REG_NAND_ECC_CTRL);
0063 }
0064 
0065 static int jz4740_ecc_calculate(struct ingenic_ecc *ecc,
0066                 struct ingenic_ecc_params *params,
0067                 const u8 *buf, u8 *ecc_code)
0068 {
0069     uint32_t reg, status;
0070     unsigned int timeout = 1000;
0071     int i;
0072 
0073     jz4740_ecc_reset(ecc, true);
0074 
0075     do {
0076         status = readl(ecc->base + JZ_REG_NAND_IRQ_STAT);
0077     } while (!(status & JZ_NAND_STATUS_ENC_FINISH) && --timeout);
0078 
0079     if (timeout == 0)
0080         return -ETIMEDOUT;
0081 
0082     reg = readl(ecc->base + JZ_REG_NAND_ECC_CTRL);
0083     reg &= ~JZ_NAND_ECC_CTRL_ENABLE;
0084     writel(reg, ecc->base + JZ_REG_NAND_ECC_CTRL);
0085 
0086     for (i = 0; i < params->bytes; ++i)
0087         ecc_code[i] = readb(ecc->base + JZ_REG_NAND_PAR0 + i);
0088 
0089     /*
0090      * If the written data is completely 0xff, we also want to write 0xff as
0091      * ECC, otherwise we will get in trouble when doing subpage writes.
0092      */
0093     if (memcmp(ecc_code, empty_block_ecc, sizeof(empty_block_ecc)) == 0)
0094         memset(ecc_code, 0xff, sizeof(empty_block_ecc));
0095 
0096     return 0;
0097 }
0098 
0099 static void jz_nand_correct_data(uint8_t *buf, int index, int mask)
0100 {
0101     int offset = index & 0x7;
0102     uint16_t data;
0103 
0104     index += (index >> 3);
0105 
0106     data = buf[index];
0107     data |= buf[index + 1] << 8;
0108 
0109     mask ^= (data >> offset) & 0x1ff;
0110     data &= ~(0x1ff << offset);
0111     data |= (mask << offset);
0112 
0113     buf[index] = data & 0xff;
0114     buf[index + 1] = (data >> 8) & 0xff;
0115 }
0116 
0117 static int jz4740_ecc_correct(struct ingenic_ecc *ecc,
0118                   struct ingenic_ecc_params *params,
0119                   u8 *buf, u8 *ecc_code)
0120 {
0121     int i, error_count, index;
0122     uint32_t reg, status, error;
0123     unsigned int timeout = 1000;
0124 
0125     jz4740_ecc_reset(ecc, false);
0126 
0127     for (i = 0; i < params->bytes; ++i)
0128         writeb(ecc_code[i], ecc->base + JZ_REG_NAND_PAR0 + i);
0129 
0130     reg = readl(ecc->base + JZ_REG_NAND_ECC_CTRL);
0131     reg |= JZ_NAND_ECC_CTRL_PAR_READY;
0132     writel(reg, ecc->base + JZ_REG_NAND_ECC_CTRL);
0133 
0134     do {
0135         status = readl(ecc->base + JZ_REG_NAND_IRQ_STAT);
0136     } while (!(status & JZ_NAND_STATUS_DEC_FINISH) && --timeout);
0137 
0138     if (timeout == 0)
0139         return -ETIMEDOUT;
0140 
0141     reg = readl(ecc->base + JZ_REG_NAND_ECC_CTRL);
0142     reg &= ~JZ_NAND_ECC_CTRL_ENABLE;
0143     writel(reg, ecc->base + JZ_REG_NAND_ECC_CTRL);
0144 
0145     if (status & JZ_NAND_STATUS_ERROR) {
0146         if (status & JZ_NAND_STATUS_UNCOR_ERROR)
0147             return -EBADMSG;
0148 
0149         error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29;
0150 
0151         for (i = 0; i < error_count; ++i) {
0152             error = readl(ecc->base + JZ_REG_NAND_ERR(i));
0153             index = ((error >> 16) & 0x1ff) - 1;
0154             if (index >= 0 && index < params->size)
0155                 jz_nand_correct_data(buf, index, error & 0x1ff);
0156         }
0157 
0158         return error_count;
0159     }
0160 
0161     return 0;
0162 }
0163 
0164 static void jz4740_ecc_disable(struct ingenic_ecc *ecc)
0165 {
0166     u32 reg;
0167 
0168     writel(0, ecc->base + JZ_REG_NAND_IRQ_STAT);
0169     reg = readl(ecc->base + JZ_REG_NAND_ECC_CTRL);
0170     reg &= ~JZ_NAND_ECC_CTRL_ENABLE;
0171     writel(reg, ecc->base + JZ_REG_NAND_ECC_CTRL);
0172 }
0173 
0174 static const struct ingenic_ecc_ops jz4740_ecc_ops = {
0175     .disable = jz4740_ecc_disable,
0176     .calculate = jz4740_ecc_calculate,
0177     .correct = jz4740_ecc_correct,
0178 };
0179 
0180 static const struct of_device_id jz4740_ecc_dt_match[] = {
0181     { .compatible = "ingenic,jz4740-ecc", .data = &jz4740_ecc_ops },
0182     {},
0183 };
0184 MODULE_DEVICE_TABLE(of, jz4740_ecc_dt_match);
0185 
0186 static struct platform_driver jz4740_ecc_driver = {
0187     .probe      = ingenic_ecc_probe,
0188     .driver = {
0189         .name   = "jz4740-ecc",
0190         .of_match_table = jz4740_ecc_dt_match,
0191     },
0192 };
0193 module_platform_driver(jz4740_ecc_driver);
0194 
0195 MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
0196 MODULE_DESCRIPTION("Ingenic JZ4740 ECC controller driver");
0197 MODULE_LICENSE("GPL v2");