0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/bitops.h>
0010 #include <linux/clk.h>
0011 #include <linux/device.h>
0012 #include <linux/io.h>
0013 #include <linux/iopoll.h>
0014 #include <linux/module.h>
0015 #include <linux/mutex.h>
0016 #include <linux/of_platform.h>
0017 #include <linux/platform_device.h>
0018
0019 #include "ingenic_ecc.h"
0020
0021 #define BCH_BHCR 0x0
0022 #define BCH_BHCCR 0x8
0023 #define BCH_BHCNT 0xc
0024 #define BCH_BHDR 0x10
0025 #define BCH_BHPAR0 0x14
0026 #define BCH_BHERR0 0x84
0027 #define BCH_BHINT 0x184
0028 #define BCH_BHINTES 0x188
0029 #define BCH_BHINTEC 0x18c
0030 #define BCH_BHINTE 0x190
0031
0032 #define BCH_BHCR_BSEL_SHIFT 4
0033 #define BCH_BHCR_BSEL_MASK (0x7f << BCH_BHCR_BSEL_SHIFT)
0034 #define BCH_BHCR_ENCE BIT(2)
0035 #define BCH_BHCR_INIT BIT(1)
0036 #define BCH_BHCR_BCHE BIT(0)
0037
0038 #define BCH_BHCNT_PARITYSIZE_SHIFT 16
0039 #define BCH_BHCNT_PARITYSIZE_MASK (0x7f << BCH_BHCNT_PARITYSIZE_SHIFT)
0040 #define BCH_BHCNT_BLOCKSIZE_SHIFT 0
0041 #define BCH_BHCNT_BLOCKSIZE_MASK (0x7ff << BCH_BHCNT_BLOCKSIZE_SHIFT)
0042
0043 #define BCH_BHERR_MASK_SHIFT 16
0044 #define BCH_BHERR_MASK_MASK (0xffff << BCH_BHERR_MASK_SHIFT)
0045 #define BCH_BHERR_INDEX_SHIFT 0
0046 #define BCH_BHERR_INDEX_MASK (0x7ff << BCH_BHERR_INDEX_SHIFT)
0047
0048 #define BCH_BHINT_ERRC_SHIFT 24
0049 #define BCH_BHINT_ERRC_MASK (0x7f << BCH_BHINT_ERRC_SHIFT)
0050 #define BCH_BHINT_TERRC_SHIFT 16
0051 #define BCH_BHINT_TERRC_MASK (0x7f << BCH_BHINT_TERRC_SHIFT)
0052 #define BCH_BHINT_DECF BIT(3)
0053 #define BCH_BHINT_ENCF BIT(2)
0054 #define BCH_BHINT_UNCOR BIT(1)
0055 #define BCH_BHINT_ERR BIT(0)
0056
0057 #define BCH_CLK_RATE (200 * 1000 * 1000)
0058
0059
0060 #define BCH_TIMEOUT_US 100000
0061
0062 static void jz4780_bch_reset(struct ingenic_ecc *bch,
0063 struct ingenic_ecc_params *params, bool encode)
0064 {
0065 u32 reg;
0066
0067
0068 writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
0069
0070
0071 reg = params->size << BCH_BHCNT_BLOCKSIZE_SHIFT;
0072 reg |= params->bytes << BCH_BHCNT_PARITYSIZE_SHIFT;
0073 writel(reg, bch->base + BCH_BHCNT);
0074
0075
0076 reg = BCH_BHCR_BCHE | BCH_BHCR_INIT;
0077 reg |= params->strength << BCH_BHCR_BSEL_SHIFT;
0078 if (encode)
0079 reg |= BCH_BHCR_ENCE;
0080 writel(reg, bch->base + BCH_BHCR);
0081 }
0082
0083 static void jz4780_bch_disable(struct ingenic_ecc *bch)
0084 {
0085 writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
0086 writel(BCH_BHCR_BCHE, bch->base + BCH_BHCCR);
0087 }
0088
0089 static void jz4780_bch_write_data(struct ingenic_ecc *bch, const void *buf,
0090 size_t size)
0091 {
0092 size_t size32 = size / sizeof(u32);
0093 size_t size8 = size % sizeof(u32);
0094 const u32 *src32;
0095 const u8 *src8;
0096
0097 src32 = (const u32 *)buf;
0098 while (size32--)
0099 writel(*src32++, bch->base + BCH_BHDR);
0100
0101 src8 = (const u8 *)src32;
0102 while (size8--)
0103 writeb(*src8++, bch->base + BCH_BHDR);
0104 }
0105
0106 static void jz4780_bch_read_parity(struct ingenic_ecc *bch, void *buf,
0107 size_t size)
0108 {
0109 size_t size32 = size / sizeof(u32);
0110 size_t size8 = size % sizeof(u32);
0111 u32 *dest32;
0112 u8 *dest8;
0113 u32 val, offset = 0;
0114
0115 dest32 = (u32 *)buf;
0116 while (size32--) {
0117 *dest32++ = readl(bch->base + BCH_BHPAR0 + offset);
0118 offset += sizeof(u32);
0119 }
0120
0121 dest8 = (u8 *)dest32;
0122 val = readl(bch->base + BCH_BHPAR0 + offset);
0123 switch (size8) {
0124 case 3:
0125 dest8[2] = (val >> 16) & 0xff;
0126 fallthrough;
0127 case 2:
0128 dest8[1] = (val >> 8) & 0xff;
0129 fallthrough;
0130 case 1:
0131 dest8[0] = val & 0xff;
0132 break;
0133 }
0134 }
0135
0136 static bool jz4780_bch_wait_complete(struct ingenic_ecc *bch, unsigned int irq,
0137 u32 *status)
0138 {
0139 u32 reg;
0140 int ret;
0141
0142
0143
0144
0145
0146
0147
0148 ret = readl_poll_timeout(bch->base + BCH_BHINT, reg,
0149 (reg & irq) == irq, 0, BCH_TIMEOUT_US);
0150 if (ret)
0151 return false;
0152
0153 if (status)
0154 *status = reg;
0155
0156 writel(reg, bch->base + BCH_BHINT);
0157 return true;
0158 }
0159
0160 static int jz4780_calculate(struct ingenic_ecc *bch,
0161 struct ingenic_ecc_params *params,
0162 const u8 *buf, u8 *ecc_code)
0163 {
0164 int ret = 0;
0165
0166 mutex_lock(&bch->lock);
0167
0168 jz4780_bch_reset(bch, params, true);
0169 jz4780_bch_write_data(bch, buf, params->size);
0170
0171 if (jz4780_bch_wait_complete(bch, BCH_BHINT_ENCF, NULL)) {
0172 jz4780_bch_read_parity(bch, ecc_code, params->bytes);
0173 } else {
0174 dev_err(bch->dev, "timed out while calculating ECC\n");
0175 ret = -ETIMEDOUT;
0176 }
0177
0178 jz4780_bch_disable(bch);
0179 mutex_unlock(&bch->lock);
0180 return ret;
0181 }
0182
0183 static int jz4780_correct(struct ingenic_ecc *bch,
0184 struct ingenic_ecc_params *params,
0185 u8 *buf, u8 *ecc_code)
0186 {
0187 u32 reg, mask, index;
0188 int i, ret, count;
0189
0190 mutex_lock(&bch->lock);
0191
0192 jz4780_bch_reset(bch, params, false);
0193 jz4780_bch_write_data(bch, buf, params->size);
0194 jz4780_bch_write_data(bch, ecc_code, params->bytes);
0195
0196 if (!jz4780_bch_wait_complete(bch, BCH_BHINT_DECF, ®)) {
0197 dev_err(bch->dev, "timed out while correcting data\n");
0198 ret = -ETIMEDOUT;
0199 goto out;
0200 }
0201
0202 if (reg & BCH_BHINT_UNCOR) {
0203 dev_warn(bch->dev, "uncorrectable ECC error\n");
0204 ret = -EBADMSG;
0205 goto out;
0206 }
0207
0208
0209 if (reg & BCH_BHINT_ERR) {
0210 count = (reg & BCH_BHINT_ERRC_MASK) >> BCH_BHINT_ERRC_SHIFT;
0211 ret = (reg & BCH_BHINT_TERRC_MASK) >> BCH_BHINT_TERRC_SHIFT;
0212
0213 for (i = 0; i < count; i++) {
0214 reg = readl(bch->base + BCH_BHERR0 + (i * 4));
0215 mask = (reg & BCH_BHERR_MASK_MASK) >>
0216 BCH_BHERR_MASK_SHIFT;
0217 index = (reg & BCH_BHERR_INDEX_MASK) >>
0218 BCH_BHERR_INDEX_SHIFT;
0219 buf[(index * 2) + 0] ^= mask;
0220 buf[(index * 2) + 1] ^= mask >> 8;
0221 }
0222 } else {
0223 ret = 0;
0224 }
0225
0226 out:
0227 jz4780_bch_disable(bch);
0228 mutex_unlock(&bch->lock);
0229 return ret;
0230 }
0231
0232 static int jz4780_bch_probe(struct platform_device *pdev)
0233 {
0234 struct ingenic_ecc *bch;
0235 int ret;
0236
0237 ret = ingenic_ecc_probe(pdev);
0238 if (ret)
0239 return ret;
0240
0241 bch = platform_get_drvdata(pdev);
0242 clk_set_rate(bch->clk, BCH_CLK_RATE);
0243
0244 return 0;
0245 }
0246
0247 static const struct ingenic_ecc_ops jz4780_bch_ops = {
0248 .disable = jz4780_bch_disable,
0249 .calculate = jz4780_calculate,
0250 .correct = jz4780_correct,
0251 };
0252
0253 static const struct of_device_id jz4780_bch_dt_match[] = {
0254 { .compatible = "ingenic,jz4780-bch", .data = &jz4780_bch_ops },
0255 {},
0256 };
0257 MODULE_DEVICE_TABLE(of, jz4780_bch_dt_match);
0258
0259 static struct platform_driver jz4780_bch_driver = {
0260 .probe = jz4780_bch_probe,
0261 .driver = {
0262 .name = "jz4780-bch",
0263 .of_match_table = jz4780_bch_dt_match,
0264 },
0265 };
0266 module_platform_driver(jz4780_bch_driver);
0267
0268 MODULE_AUTHOR("Alex Smith <alex@alex-smith.me.uk>");
0269 MODULE_AUTHOR("Harvey Hunt <harveyhuntnexus@gmail.com>");
0270 MODULE_DESCRIPTION("Ingenic JZ4780 BCH error correction driver");
0271 MODULE_LICENSE("GPL v2");