Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * JZ4780 BCH controller driver
0004  *
0005  * Copyright (c) 2015 Imagination Technologies
0006  * Author: Alex Smith <alex.smith@imgtec.com>
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 /* Timeout for BCH calculation/correction. */
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     /* Clear interrupt status. */
0068     writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
0069 
0070     /* Set up BCH count register. */
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     /* Initialise and enable BCH. */
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      * While we could use interrupts here and sleep until the operation
0144      * completes, the controller works fairly quickly (usually a few
0145      * microseconds) and so the overhead of sleeping until we get an
0146      * interrupt quite noticeably decreases performance.
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, &reg)) {
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     /* Correct any detected errors. */
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");