1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * JZ4780 BCH controller 4 * 5 * Copyright (c) 2015 Imagination Technologies 6 * Author: Alex Smith <alex.smith@imgtec.com> 7 */ 8 9 #include <linux/bitops.h> 10 #include <linux/clk.h> 11 #include <linux/delay.h> 12 #include <linux/init.h> 13 #include <linux/iopoll.h> 14 #include <linux/module.h> 15 #include <linux/mutex.h> 16 #include <linux/of.h> 17 #include <linux/of_platform.h> 18 #include <linux/platform_device.h> 19 #include <linux/sched.h> 20 #include <linux/slab.h> 21 22 #include "jz4780_bch.h" 23 24 #define BCH_BHCR 0x0 25 #define BCH_BHCCR 0x8 26 #define BCH_BHCNT 0xc 27 #define BCH_BHDR 0x10 28 #define BCH_BHPAR0 0x14 29 #define BCH_BHERR0 0x84 30 #define BCH_BHINT 0x184 31 #define BCH_BHINTES 0x188 32 #define BCH_BHINTEC 0x18c 33 #define BCH_BHINTE 0x190 34 35 #define BCH_BHCR_BSEL_SHIFT 4 36 #define BCH_BHCR_BSEL_MASK (0x7f << BCH_BHCR_BSEL_SHIFT) 37 #define BCH_BHCR_ENCE BIT(2) 38 #define BCH_BHCR_INIT BIT(1) 39 #define BCH_BHCR_BCHE BIT(0) 40 41 #define BCH_BHCNT_PARITYSIZE_SHIFT 16 42 #define BCH_BHCNT_PARITYSIZE_MASK (0x7f << BCH_BHCNT_PARITYSIZE_SHIFT) 43 #define BCH_BHCNT_BLOCKSIZE_SHIFT 0 44 #define BCH_BHCNT_BLOCKSIZE_MASK (0x7ff << BCH_BHCNT_BLOCKSIZE_SHIFT) 45 46 #define BCH_BHERR_MASK_SHIFT 16 47 #define BCH_BHERR_MASK_MASK (0xffff << BCH_BHERR_MASK_SHIFT) 48 #define BCH_BHERR_INDEX_SHIFT 0 49 #define BCH_BHERR_INDEX_MASK (0x7ff << BCH_BHERR_INDEX_SHIFT) 50 51 #define BCH_BHINT_ERRC_SHIFT 24 52 #define BCH_BHINT_ERRC_MASK (0x7f << BCH_BHINT_ERRC_SHIFT) 53 #define BCH_BHINT_TERRC_SHIFT 16 54 #define BCH_BHINT_TERRC_MASK (0x7f << BCH_BHINT_TERRC_SHIFT) 55 #define BCH_BHINT_DECF BIT(3) 56 #define BCH_BHINT_ENCF BIT(2) 57 #define BCH_BHINT_UNCOR BIT(1) 58 #define BCH_BHINT_ERR BIT(0) 59 60 #define BCH_CLK_RATE (200 * 1000 * 1000) 61 62 /* Timeout for BCH calculation/correction. */ 63 #define BCH_TIMEOUT_US 100000 64 65 struct jz4780_bch { 66 struct device *dev; 67 void __iomem *base; 68 struct clk *clk; 69 struct mutex lock; 70 }; 71 72 static void jz4780_bch_reset(struct jz4780_bch *bch, 73 struct jz4780_bch_params *params, bool encode) 74 { 75 u32 reg; 76 77 /* Clear interrupt status. */ 78 writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT); 79 80 /* Set up BCH count register. */ 81 reg = params->size << BCH_BHCNT_BLOCKSIZE_SHIFT; 82 reg |= params->bytes << BCH_BHCNT_PARITYSIZE_SHIFT; 83 writel(reg, bch->base + BCH_BHCNT); 84 85 /* Initialise and enable BCH. */ 86 reg = BCH_BHCR_BCHE | BCH_BHCR_INIT; 87 reg |= params->strength << BCH_BHCR_BSEL_SHIFT; 88 if (encode) 89 reg |= BCH_BHCR_ENCE; 90 writel(reg, bch->base + BCH_BHCR); 91 } 92 93 static void jz4780_bch_disable(struct jz4780_bch *bch) 94 { 95 writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT); 96 writel(BCH_BHCR_BCHE, bch->base + BCH_BHCCR); 97 } 98 99 static void jz4780_bch_write_data(struct jz4780_bch *bch, const void *buf, 100 size_t size) 101 { 102 size_t size32 = size / sizeof(u32); 103 size_t size8 = size % sizeof(u32); 104 const u32 *src32; 105 const u8 *src8; 106 107 src32 = (const u32 *)buf; 108 while (size32--) 109 writel(*src32++, bch->base + BCH_BHDR); 110 111 src8 = (const u8 *)src32; 112 while (size8--) 113 writeb(*src8++, bch->base + BCH_BHDR); 114 } 115 116 static void jz4780_bch_read_parity(struct jz4780_bch *bch, void *buf, 117 size_t size) 118 { 119 size_t size32 = size / sizeof(u32); 120 size_t size8 = size % sizeof(u32); 121 u32 *dest32; 122 u8 *dest8; 123 u32 val, offset = 0; 124 125 dest32 = (u32 *)buf; 126 while (size32--) { 127 *dest32++ = readl(bch->base + BCH_BHPAR0 + offset); 128 offset += sizeof(u32); 129 } 130 131 dest8 = (u8 *)dest32; 132 val = readl(bch->base + BCH_BHPAR0 + offset); 133 switch (size8) { 134 case 3: 135 dest8[2] = (val >> 16) & 0xff; 136 /* fall through */ 137 case 2: 138 dest8[1] = (val >> 8) & 0xff; 139 /* fall through */ 140 case 1: 141 dest8[0] = val & 0xff; 142 break; 143 } 144 } 145 146 static bool jz4780_bch_wait_complete(struct jz4780_bch *bch, unsigned int irq, 147 u32 *status) 148 { 149 u32 reg; 150 int ret; 151 152 /* 153 * While we could use interrupts here and sleep until the operation 154 * completes, the controller works fairly quickly (usually a few 155 * microseconds) and so the overhead of sleeping until we get an 156 * interrupt quite noticeably decreases performance. 157 */ 158 ret = readl_poll_timeout(bch->base + BCH_BHINT, reg, 159 (reg & irq) == irq, 0, BCH_TIMEOUT_US); 160 if (ret) 161 return false; 162 163 if (status) 164 *status = reg; 165 166 writel(reg, bch->base + BCH_BHINT); 167 return true; 168 } 169 170 /** 171 * jz4780_bch_calculate() - calculate ECC for a data buffer 172 * @bch: BCH device. 173 * @params: BCH parameters. 174 * @buf: input buffer with raw data. 175 * @ecc_code: output buffer with ECC. 176 * 177 * Return: 0 on success, -ETIMEDOUT if timed out while waiting for BCH 178 * controller. 179 */ 180 int jz4780_bch_calculate(struct jz4780_bch *bch, struct jz4780_bch_params *params, 181 const u8 *buf, u8 *ecc_code) 182 { 183 int ret = 0; 184 185 mutex_lock(&bch->lock); 186 187 jz4780_bch_reset(bch, params, true); 188 jz4780_bch_write_data(bch, buf, params->size); 189 190 if (jz4780_bch_wait_complete(bch, BCH_BHINT_ENCF, NULL)) { 191 jz4780_bch_read_parity(bch, ecc_code, params->bytes); 192 } else { 193 dev_err(bch->dev, "timed out while calculating ECC\n"); 194 ret = -ETIMEDOUT; 195 } 196 197 jz4780_bch_disable(bch); 198 mutex_unlock(&bch->lock); 199 return ret; 200 } 201 EXPORT_SYMBOL(jz4780_bch_calculate); 202 203 /** 204 * jz4780_bch_correct() - detect and correct bit errors 205 * @bch: BCH device. 206 * @params: BCH parameters. 207 * @buf: raw data read from the chip. 208 * @ecc_code: ECC read from the chip. 209 * 210 * Given the raw data and the ECC read from the NAND device, detects and 211 * corrects errors in the data. 212 * 213 * Return: the number of bit errors corrected, -EBADMSG if there are too many 214 * errors to correct or -ETIMEDOUT if we timed out waiting for the controller. 215 */ 216 int jz4780_bch_correct(struct jz4780_bch *bch, struct jz4780_bch_params *params, 217 u8 *buf, u8 *ecc_code) 218 { 219 u32 reg, mask, index; 220 int i, ret, count; 221 222 mutex_lock(&bch->lock); 223 224 jz4780_bch_reset(bch, params, false); 225 jz4780_bch_write_data(bch, buf, params->size); 226 jz4780_bch_write_data(bch, ecc_code, params->bytes); 227 228 if (!jz4780_bch_wait_complete(bch, BCH_BHINT_DECF, ®)) { 229 dev_err(bch->dev, "timed out while correcting data\n"); 230 ret = -ETIMEDOUT; 231 goto out; 232 } 233 234 if (reg & BCH_BHINT_UNCOR) { 235 dev_warn(bch->dev, "uncorrectable ECC error\n"); 236 ret = -EBADMSG; 237 goto out; 238 } 239 240 /* Correct any detected errors. */ 241 if (reg & BCH_BHINT_ERR) { 242 count = (reg & BCH_BHINT_ERRC_MASK) >> BCH_BHINT_ERRC_SHIFT; 243 ret = (reg & BCH_BHINT_TERRC_MASK) >> BCH_BHINT_TERRC_SHIFT; 244 245 for (i = 0; i < count; i++) { 246 reg = readl(bch->base + BCH_BHERR0 + (i * 4)); 247 mask = (reg & BCH_BHERR_MASK_MASK) >> 248 BCH_BHERR_MASK_SHIFT; 249 index = (reg & BCH_BHERR_INDEX_MASK) >> 250 BCH_BHERR_INDEX_SHIFT; 251 buf[(index * 2) + 0] ^= mask; 252 buf[(index * 2) + 1] ^= mask >> 8; 253 } 254 } else { 255 ret = 0; 256 } 257 258 out: 259 jz4780_bch_disable(bch); 260 mutex_unlock(&bch->lock); 261 return ret; 262 } 263 EXPORT_SYMBOL(jz4780_bch_correct); 264 265 /** 266 * jz4780_bch_get() - get the BCH controller device 267 * @np: BCH device tree node. 268 * 269 * Gets the BCH controller device from the specified device tree node. The 270 * device must be released with jz4780_bch_release() when it is no longer being 271 * used. 272 * 273 * Return: a pointer to jz4780_bch, errors are encoded into the pointer. 274 * PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet. 275 */ 276 static struct jz4780_bch *jz4780_bch_get(struct device_node *np) 277 { 278 struct platform_device *pdev; 279 struct jz4780_bch *bch; 280 281 pdev = of_find_device_by_node(np); 282 if (!pdev) 283 return ERR_PTR(-EPROBE_DEFER); 284 285 bch = platform_get_drvdata(pdev); 286 if (!bch) { 287 put_device(&pdev->dev); 288 return ERR_PTR(-EPROBE_DEFER); 289 } 290 291 clk_prepare_enable(bch->clk); 292 293 return bch; 294 } 295 296 /** 297 * of_jz4780_bch_get() - get the BCH controller from a DT node 298 * @of_node: the node that contains a bch-controller property. 299 * 300 * Get the bch-controller property from the given device tree 301 * node and pass it to jz4780_bch_get to do the work. 302 * 303 * Return: a pointer to jz4780_bch, errors are encoded into the pointer. 304 * PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet. 305 */ 306 struct jz4780_bch *of_jz4780_bch_get(struct device_node *of_node) 307 { 308 struct jz4780_bch *bch = NULL; 309 struct device_node *np; 310 311 np = of_parse_phandle(of_node, "ingenic,bch-controller", 0); 312 313 if (np) { 314 bch = jz4780_bch_get(np); 315 of_node_put(np); 316 } 317 return bch; 318 } 319 EXPORT_SYMBOL(of_jz4780_bch_get); 320 321 /** 322 * jz4780_bch_release() - release the BCH controller device 323 * @bch: BCH device. 324 */ 325 void jz4780_bch_release(struct jz4780_bch *bch) 326 { 327 clk_disable_unprepare(bch->clk); 328 put_device(bch->dev); 329 } 330 EXPORT_SYMBOL(jz4780_bch_release); 331 332 static int jz4780_bch_probe(struct platform_device *pdev) 333 { 334 struct device *dev = &pdev->dev; 335 struct jz4780_bch *bch; 336 struct resource *res; 337 338 bch = devm_kzalloc(dev, sizeof(*bch), GFP_KERNEL); 339 if (!bch) 340 return -ENOMEM; 341 342 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 343 bch->base = devm_ioremap_resource(dev, res); 344 if (IS_ERR(bch->base)) 345 return PTR_ERR(bch->base); 346 347 jz4780_bch_disable(bch); 348 349 bch->clk = devm_clk_get(dev, NULL); 350 if (IS_ERR(bch->clk)) { 351 dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(bch->clk)); 352 return PTR_ERR(bch->clk); 353 } 354 355 clk_set_rate(bch->clk, BCH_CLK_RATE); 356 357 mutex_init(&bch->lock); 358 359 bch->dev = dev; 360 platform_set_drvdata(pdev, bch); 361 362 return 0; 363 } 364 365 static const struct of_device_id jz4780_bch_dt_match[] = { 366 { .compatible = "ingenic,jz4780-bch" }, 367 {}, 368 }; 369 MODULE_DEVICE_TABLE(of, jz4780_bch_dt_match); 370 371 static struct platform_driver jz4780_bch_driver = { 372 .probe = jz4780_bch_probe, 373 .driver = { 374 .name = "jz4780-bch", 375 .of_match_table = of_match_ptr(jz4780_bch_dt_match), 376 }, 377 }; 378 module_platform_driver(jz4780_bch_driver); 379 380 MODULE_AUTHOR("Alex Smith <alex@alex-smith.me.uk>"); 381 MODULE_AUTHOR("Harvey Hunt <harveyhuntnexus@gmail.com>"); 382 MODULE_DESCRIPTION("Ingenic JZ4780 BCH error correction driver"); 383 MODULE_LICENSE("GPL v2"); 384