0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/bitops.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_BHCSR 0x4
0023 #define BCH_BHCCR 0x8
0024 #define BCH_BHCNT 0xc
0025 #define BCH_BHDR 0x10
0026 #define BCH_BHPAR0 0x14
0027 #define BCH_BHERR0 0x28
0028 #define BCH_BHINT 0x24
0029 #define BCH_BHINTES 0x3c
0030 #define BCH_BHINTEC 0x40
0031 #define BCH_BHINTE 0x38
0032
0033 #define BCH_BHCR_ENCE BIT(3)
0034 #define BCH_BHCR_BSEL BIT(2)
0035 #define BCH_BHCR_INIT BIT(1)
0036 #define BCH_BHCR_BCHE BIT(0)
0037
0038 #define BCH_BHCNT_DEC_COUNT_SHIFT 16
0039 #define BCH_BHCNT_DEC_COUNT_MASK (0x3ff << BCH_BHCNT_DEC_COUNT_SHIFT)
0040 #define BCH_BHCNT_ENC_COUNT_SHIFT 0
0041 #define BCH_BHCNT_ENC_COUNT_MASK (0x3ff << BCH_BHCNT_ENC_COUNT_SHIFT)
0042
0043 #define BCH_BHERR_INDEX0_SHIFT 0
0044 #define BCH_BHERR_INDEX0_MASK (0x1fff << BCH_BHERR_INDEX0_SHIFT)
0045 #define BCH_BHERR_INDEX1_SHIFT 16
0046 #define BCH_BHERR_INDEX1_MASK (0x1fff << BCH_BHERR_INDEX1_SHIFT)
0047
0048 #define BCH_BHINT_ERRC_SHIFT 28
0049 #define BCH_BHINT_ERRC_MASK (0xf << 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_ALL_0 BIT(5)
0053 #define BCH_BHINT_ALL_F BIT(4)
0054 #define BCH_BHINT_DECF BIT(3)
0055 #define BCH_BHINT_ENCF BIT(2)
0056 #define BCH_BHINT_UNCOR BIT(1)
0057 #define BCH_BHINT_ERR BIT(0)
0058
0059
0060 #define BCH_TIMEOUT_US 100000
0061
0062 static inline void jz4725b_bch_config_set(struct ingenic_ecc *bch, u32 cfg)
0063 {
0064 writel(cfg, bch->base + BCH_BHCSR);
0065 }
0066
0067 static inline void jz4725b_bch_config_clear(struct ingenic_ecc *bch, u32 cfg)
0068 {
0069 writel(cfg, bch->base + BCH_BHCCR);
0070 }
0071
0072 static int jz4725b_bch_reset(struct ingenic_ecc *bch,
0073 struct ingenic_ecc_params *params, bool calc_ecc)
0074 {
0075 u32 reg, max_value;
0076
0077
0078 writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
0079
0080
0081 jz4725b_bch_config_clear(bch, 0x1f);
0082 jz4725b_bch_config_set(bch, BCH_BHCR_BCHE);
0083
0084 if (params->strength == 8)
0085 jz4725b_bch_config_set(bch, BCH_BHCR_BSEL);
0086 else
0087 jz4725b_bch_config_clear(bch, BCH_BHCR_BSEL);
0088
0089 if (calc_ecc)
0090 jz4725b_bch_config_set(bch, BCH_BHCR_ENCE);
0091 else
0092 jz4725b_bch_config_clear(bch, BCH_BHCR_ENCE);
0093
0094 jz4725b_bch_config_set(bch, BCH_BHCR_INIT);
0095
0096 max_value = BCH_BHCNT_ENC_COUNT_MASK >> BCH_BHCNT_ENC_COUNT_SHIFT;
0097 if (params->size > max_value)
0098 return -EINVAL;
0099
0100 max_value = BCH_BHCNT_DEC_COUNT_MASK >> BCH_BHCNT_DEC_COUNT_SHIFT;
0101 if (params->size + params->bytes > max_value)
0102 return -EINVAL;
0103
0104
0105 reg = params->size << BCH_BHCNT_ENC_COUNT_SHIFT;
0106 reg |= (params->size + params->bytes) << BCH_BHCNT_DEC_COUNT_SHIFT;
0107 writel(reg, bch->base + BCH_BHCNT);
0108
0109 return 0;
0110 }
0111
0112 static void jz4725b_bch_disable(struct ingenic_ecc *bch)
0113 {
0114
0115 writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
0116
0117
0118 jz4725b_bch_config_clear(bch, BCH_BHCR_BCHE);
0119 }
0120
0121 static void jz4725b_bch_write_data(struct ingenic_ecc *bch, const u8 *buf,
0122 size_t size)
0123 {
0124 while (size--)
0125 writeb(*buf++, bch->base + BCH_BHDR);
0126 }
0127
0128 static void jz4725b_bch_read_parity(struct ingenic_ecc *bch, u8 *buf,
0129 size_t size)
0130 {
0131 size_t size32 = size / sizeof(u32);
0132 size_t size8 = size % sizeof(u32);
0133 u32 *dest32;
0134 u8 *dest8;
0135 u32 val, offset = 0;
0136
0137 dest32 = (u32 *)buf;
0138 while (size32--) {
0139 *dest32++ = readl_relaxed(bch->base + BCH_BHPAR0 + offset);
0140 offset += sizeof(u32);
0141 }
0142
0143 dest8 = (u8 *)dest32;
0144 val = readl_relaxed(bch->base + BCH_BHPAR0 + offset);
0145 switch (size8) {
0146 case 3:
0147 dest8[2] = (val >> 16) & 0xff;
0148 fallthrough;
0149 case 2:
0150 dest8[1] = (val >> 8) & 0xff;
0151 fallthrough;
0152 case 1:
0153 dest8[0] = val & 0xff;
0154 break;
0155 }
0156 }
0157
0158 static int jz4725b_bch_wait_complete(struct ingenic_ecc *bch, unsigned int irq,
0159 u32 *status)
0160 {
0161 u32 reg;
0162 int ret;
0163
0164
0165
0166
0167
0168
0169
0170 ret = readl_relaxed_poll_timeout(bch->base + BCH_BHINT, reg,
0171 reg & irq, 0, BCH_TIMEOUT_US);
0172 if (ret)
0173 return ret;
0174
0175 if (status)
0176 *status = reg;
0177
0178 writel(reg, bch->base + BCH_BHINT);
0179
0180 return 0;
0181 }
0182
0183 static int jz4725b_calculate(struct ingenic_ecc *bch,
0184 struct ingenic_ecc_params *params,
0185 const u8 *buf, u8 *ecc_code)
0186 {
0187 int ret;
0188
0189 mutex_lock(&bch->lock);
0190
0191 ret = jz4725b_bch_reset(bch, params, true);
0192 if (ret) {
0193 dev_err(bch->dev, "Unable to init BCH with given parameters\n");
0194 goto out_disable;
0195 }
0196
0197 jz4725b_bch_write_data(bch, buf, params->size);
0198
0199 ret = jz4725b_bch_wait_complete(bch, BCH_BHINT_ENCF, NULL);
0200 if (ret) {
0201 dev_err(bch->dev, "timed out while calculating ECC\n");
0202 goto out_disable;
0203 }
0204
0205 jz4725b_bch_read_parity(bch, ecc_code, params->bytes);
0206
0207 out_disable:
0208 jz4725b_bch_disable(bch);
0209 mutex_unlock(&bch->lock);
0210
0211 return ret;
0212 }
0213
0214 static int jz4725b_correct(struct ingenic_ecc *bch,
0215 struct ingenic_ecc_params *params,
0216 u8 *buf, u8 *ecc_code)
0217 {
0218 u32 reg, errors, bit;
0219 unsigned int i;
0220 int ret;
0221
0222 mutex_lock(&bch->lock);
0223
0224 ret = jz4725b_bch_reset(bch, params, false);
0225 if (ret) {
0226 dev_err(bch->dev, "Unable to init BCH with given parameters\n");
0227 goto out;
0228 }
0229
0230 jz4725b_bch_write_data(bch, buf, params->size);
0231 jz4725b_bch_write_data(bch, ecc_code, params->bytes);
0232
0233 ret = jz4725b_bch_wait_complete(bch, BCH_BHINT_DECF, ®);
0234 if (ret) {
0235 dev_err(bch->dev, "timed out while correcting data\n");
0236 goto out;
0237 }
0238
0239 if (reg & (BCH_BHINT_ALL_F | BCH_BHINT_ALL_0)) {
0240
0241 ret = 0;
0242 goto out;
0243 }
0244
0245 if (reg & BCH_BHINT_UNCOR) {
0246
0247 ret = -EBADMSG;
0248 goto out;
0249 }
0250
0251 errors = (reg & BCH_BHINT_ERRC_MASK) >> BCH_BHINT_ERRC_SHIFT;
0252
0253
0254 for (i = 0; i < errors; i++) {
0255 if (i & 1) {
0256 bit = (reg & BCH_BHERR_INDEX1_MASK) >> BCH_BHERR_INDEX1_SHIFT;
0257 } else {
0258 reg = readl(bch->base + BCH_BHERR0 + (i * 4));
0259 bit = (reg & BCH_BHERR_INDEX0_MASK) >> BCH_BHERR_INDEX0_SHIFT;
0260 }
0261
0262 buf[(bit >> 3)] ^= BIT(bit & 0x7);
0263 }
0264
0265 out:
0266 jz4725b_bch_disable(bch);
0267 mutex_unlock(&bch->lock);
0268
0269 return ret;
0270 }
0271
0272 static const struct ingenic_ecc_ops jz4725b_bch_ops = {
0273 .disable = jz4725b_bch_disable,
0274 .calculate = jz4725b_calculate,
0275 .correct = jz4725b_correct,
0276 };
0277
0278 static const struct of_device_id jz4725b_bch_dt_match[] = {
0279 { .compatible = "ingenic,jz4725b-bch", .data = &jz4725b_bch_ops },
0280 {},
0281 };
0282 MODULE_DEVICE_TABLE(of, jz4725b_bch_dt_match);
0283
0284 static struct platform_driver jz4725b_bch_driver = {
0285 .probe = ingenic_ecc_probe,
0286 .driver = {
0287 .name = "jz4725b-bch",
0288 .of_match_table = jz4725b_bch_dt_match,
0289 },
0290 };
0291 module_platform_driver(jz4725b_bch_driver);
0292
0293 MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
0294 MODULE_DESCRIPTION("Ingenic JZ4725B BCH controller driver");
0295 MODULE_LICENSE("GPL v2");