linux/drivers/mtd/nand/raw/omap_elm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Error Location Module
   4 *
   5 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
   6 */
   7
   8#define DRIVER_NAME     "omap-elm"
   9
  10#include <linux/platform_device.h>
  11#include <linux/module.h>
  12#include <linux/interrupt.h>
  13#include <linux/io.h>
  14#include <linux/of.h>
  15#include <linux/sched.h>
  16#include <linux/pm_runtime.h>
  17#include <linux/platform_data/elm.h>
  18
  19#define ELM_SYSCONFIG                   0x010
  20#define ELM_IRQSTATUS                   0x018
  21#define ELM_IRQENABLE                   0x01c
  22#define ELM_LOCATION_CONFIG             0x020
  23#define ELM_PAGE_CTRL                   0x080
  24#define ELM_SYNDROME_FRAGMENT_0         0x400
  25#define ELM_SYNDROME_FRAGMENT_1         0x404
  26#define ELM_SYNDROME_FRAGMENT_2         0x408
  27#define ELM_SYNDROME_FRAGMENT_3         0x40c
  28#define ELM_SYNDROME_FRAGMENT_4         0x410
  29#define ELM_SYNDROME_FRAGMENT_5         0x414
  30#define ELM_SYNDROME_FRAGMENT_6         0x418
  31#define ELM_LOCATION_STATUS             0x800
  32#define ELM_ERROR_LOCATION_0            0x880
  33
  34/* ELM Interrupt Status Register */
  35#define INTR_STATUS_PAGE_VALID          BIT(8)
  36
  37/* ELM Interrupt Enable Register */
  38#define INTR_EN_PAGE_MASK               BIT(8)
  39
  40/* ELM Location Configuration Register */
  41#define ECC_BCH_LEVEL_MASK              0x3
  42
  43/* ELM syndrome */
  44#define ELM_SYNDROME_VALID              BIT(16)
  45
  46/* ELM_LOCATION_STATUS Register */
  47#define ECC_CORRECTABLE_MASK            BIT(8)
  48#define ECC_NB_ERRORS_MASK              0x1f
  49
  50/* ELM_ERROR_LOCATION_0-15 Registers */
  51#define ECC_ERROR_LOCATION_MASK         0x1fff
  52
  53#define ELM_ECC_SIZE                    0x7ff
  54
  55#define SYNDROME_FRAGMENT_REG_SIZE      0x40
  56#define ERROR_LOCATION_SIZE             0x100
  57
  58struct elm_registers {
  59        u32 elm_irqenable;
  60        u32 elm_sysconfig;
  61        u32 elm_location_config;
  62        u32 elm_page_ctrl;
  63        u32 elm_syndrome_fragment_6[ERROR_VECTOR_MAX];
  64        u32 elm_syndrome_fragment_5[ERROR_VECTOR_MAX];
  65        u32 elm_syndrome_fragment_4[ERROR_VECTOR_MAX];
  66        u32 elm_syndrome_fragment_3[ERROR_VECTOR_MAX];
  67        u32 elm_syndrome_fragment_2[ERROR_VECTOR_MAX];
  68        u32 elm_syndrome_fragment_1[ERROR_VECTOR_MAX];
  69        u32 elm_syndrome_fragment_0[ERROR_VECTOR_MAX];
  70};
  71
  72struct elm_info {
  73        struct device *dev;
  74        void __iomem *elm_base;
  75        struct completion elm_completion;
  76        struct list_head list;
  77        enum bch_ecc bch_type;
  78        struct elm_registers elm_regs;
  79        int ecc_steps;
  80        int ecc_syndrome_size;
  81};
  82
  83static LIST_HEAD(elm_devices);
  84
  85static void elm_write_reg(struct elm_info *info, int offset, u32 val)
  86{
  87        writel(val, info->elm_base + offset);
  88}
  89
  90static u32 elm_read_reg(struct elm_info *info, int offset)
  91{
  92        return readl(info->elm_base + offset);
  93}
  94
  95/**
  96 * elm_config - Configure ELM module
  97 * @dev:        ELM device
  98 * @bch_type:   Type of BCH ecc
  99 */
 100int elm_config(struct device *dev, enum bch_ecc bch_type,
 101        int ecc_steps, int ecc_step_size, int ecc_syndrome_size)
 102{
 103        u32 reg_val;
 104        struct elm_info *info = dev_get_drvdata(dev);
 105
 106        if (!info) {
 107                dev_err(dev, "Unable to configure elm - device not probed?\n");
 108                return -EPROBE_DEFER;
 109        }
 110        /* ELM cannot detect ECC errors for chunks > 1KB */
 111        if (ecc_step_size > ((ELM_ECC_SIZE + 1) / 2)) {
 112                dev_err(dev, "unsupported config ecc-size=%d\n", ecc_step_size);
 113                return -EINVAL;
 114        }
 115        /* ELM support 8 error syndrome process */
 116        if (ecc_steps > ERROR_VECTOR_MAX) {
 117                dev_err(dev, "unsupported config ecc-step=%d\n", ecc_steps);
 118                return -EINVAL;
 119        }
 120
 121        reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16);
 122        elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val);
 123        info->bch_type          = bch_type;
 124        info->ecc_steps         = ecc_steps;
 125        info->ecc_syndrome_size = ecc_syndrome_size;
 126
 127        return 0;
 128}
 129EXPORT_SYMBOL(elm_config);
 130
 131/**
 132 * elm_configure_page_mode - Enable/Disable page mode
 133 * @info:       elm info
 134 * @index:      index number of syndrome fragment vector
 135 * @enable:     enable/disable flag for page mode
 136 *
 137 * Enable page mode for syndrome fragment index
 138 */
 139static void elm_configure_page_mode(struct elm_info *info, int index,
 140                bool enable)
 141{
 142        u32 reg_val;
 143
 144        reg_val = elm_read_reg(info, ELM_PAGE_CTRL);
 145        if (enable)
 146                reg_val |= BIT(index);  /* enable page mode */
 147        else
 148                reg_val &= ~BIT(index); /* disable page mode */
 149
 150        elm_write_reg(info, ELM_PAGE_CTRL, reg_val);
 151}
 152
 153/**
 154 * elm_load_syndrome - Load ELM syndrome reg
 155 * @info:       elm info
 156 * @err_vec:    elm error vectors
 157 * @ecc:        buffer with calculated ecc
 158 *
 159 * Load syndrome fragment registers with calculated ecc in reverse order.
 160 */
 161static void elm_load_syndrome(struct elm_info *info,
 162                struct elm_errorvec *err_vec, u8 *ecc)
 163{
 164        int i, offset;
 165        u32 val;
 166
 167        for (i = 0; i < info->ecc_steps; i++) {
 168
 169                /* Check error reported */
 170                if (err_vec[i].error_reported) {
 171                        elm_configure_page_mode(info, i, true);
 172                        offset = ELM_SYNDROME_FRAGMENT_0 +
 173                                SYNDROME_FRAGMENT_REG_SIZE * i;
 174                        switch (info->bch_type) {
 175                        case BCH8_ECC:
 176                                /* syndrome fragment 0 = ecc[9-12B] */
 177                                val = cpu_to_be32(*(u32 *) &ecc[9]);
 178                                elm_write_reg(info, offset, val);
 179
 180                                /* syndrome fragment 1 = ecc[5-8B] */
 181                                offset += 4;
 182                                val = cpu_to_be32(*(u32 *) &ecc[5]);
 183                                elm_write_reg(info, offset, val);
 184
 185                                /* syndrome fragment 2 = ecc[1-4B] */
 186                                offset += 4;
 187                                val = cpu_to_be32(*(u32 *) &ecc[1]);
 188                                elm_write_reg(info, offset, val);
 189
 190                                /* syndrome fragment 3 = ecc[0B] */
 191                                offset += 4;
 192                                val = ecc[0];
 193                                elm_write_reg(info, offset, val);
 194                                break;
 195                        case BCH4_ECC:
 196                                /* syndrome fragment 0 = ecc[20-52b] bits */
 197                                val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) |
 198                                        ((ecc[2] & 0xf) << 28);
 199                                elm_write_reg(info, offset, val);
 200
 201                                /* syndrome fragment 1 = ecc[0-20b] bits */
 202                                offset += 4;
 203                                val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
 204                                elm_write_reg(info, offset, val);
 205                                break;
 206                        case BCH16_ECC:
 207                                val = cpu_to_be32(*(u32 *) &ecc[22]);
 208                                elm_write_reg(info, offset, val);
 209                                offset += 4;
 210                                val = cpu_to_be32(*(u32 *) &ecc[18]);
 211                                elm_write_reg(info, offset, val);
 212                                offset += 4;
 213                                val = cpu_to_be32(*(u32 *) &ecc[14]);
 214                                elm_write_reg(info, offset, val);
 215                                offset += 4;
 216                                val = cpu_to_be32(*(u32 *) &ecc[10]);
 217                                elm_write_reg(info, offset, val);
 218                                offset += 4;
 219                                val = cpu_to_be32(*(u32 *) &ecc[6]);
 220                                elm_write_reg(info, offset, val);
 221                                offset += 4;
 222                                val = cpu_to_be32(*(u32 *) &ecc[2]);
 223                                elm_write_reg(info, offset, val);
 224                                offset += 4;
 225                                val = cpu_to_be32(*(u32 *) &ecc[0]) >> 16;
 226                                elm_write_reg(info, offset, val);
 227                                break;
 228                        default:
 229                                pr_err("invalid config bch_type\n");
 230                        }
 231                }
 232
 233                /* Update ecc pointer with ecc byte size */
 234                ecc += info->ecc_syndrome_size;
 235        }
 236}
 237
 238/**
 239 * elm_start_processing - start elm syndrome processing
 240 * @info:       elm info
 241 * @err_vec:    elm error vectors
 242 *
 243 * Set syndrome valid bit for syndrome fragment registers for which
 244 * elm syndrome fragment registers are loaded. This enables elm module
 245 * to start processing syndrome vectors.
 246 */
 247static void elm_start_processing(struct elm_info *info,
 248                struct elm_errorvec *err_vec)
 249{
 250        int i, offset;
 251        u32 reg_val;
 252
 253        /*
 254         * Set syndrome vector valid, so that ELM module
 255         * will process it for vectors error is reported
 256         */
 257        for (i = 0; i < info->ecc_steps; i++) {
 258                if (err_vec[i].error_reported) {
 259                        offset = ELM_SYNDROME_FRAGMENT_6 +
 260                                SYNDROME_FRAGMENT_REG_SIZE * i;
 261                        reg_val = elm_read_reg(info, offset);
 262                        reg_val |= ELM_SYNDROME_VALID;
 263                        elm_write_reg(info, offset, reg_val);
 264                }
 265        }
 266}
 267
 268/**
 269 * elm_error_correction - locate correctable error position
 270 * @info:       elm info
 271 * @err_vec:    elm error vectors
 272 *
 273 * On completion of processing by elm module, error location status
 274 * register updated with correctable/uncorrectable error information.
 275 * In case of correctable errors, number of errors located from
 276 * elm location status register & read the positions from
 277 * elm error location register.
 278 */
 279static void elm_error_correction(struct elm_info *info,
 280                struct elm_errorvec *err_vec)
 281{
 282        int i, j, errors = 0;
 283        int offset;
 284        u32 reg_val;
 285
 286        for (i = 0; i < info->ecc_steps; i++) {
 287
 288                /* Check error reported */
 289                if (err_vec[i].error_reported) {
 290                        offset = ELM_LOCATION_STATUS + ERROR_LOCATION_SIZE * i;
 291                        reg_val = elm_read_reg(info, offset);
 292
 293                        /* Check correctable error or not */
 294                        if (reg_val & ECC_CORRECTABLE_MASK) {
 295                                offset = ELM_ERROR_LOCATION_0 +
 296                                        ERROR_LOCATION_SIZE * i;
 297
 298                                /* Read count of correctable errors */
 299                                err_vec[i].error_count = reg_val &
 300                                        ECC_NB_ERRORS_MASK;
 301
 302                                /* Update the error locations in error vector */
 303                                for (j = 0; j < err_vec[i].error_count; j++) {
 304
 305                                        reg_val = elm_read_reg(info, offset);
 306                                        err_vec[i].error_loc[j] = reg_val &
 307                                                ECC_ERROR_LOCATION_MASK;
 308
 309                                        /* Update error location register */
 310                                        offset += 4;
 311                                }
 312
 313                                errors += err_vec[i].error_count;
 314                        } else {
 315                                err_vec[i].error_uncorrectable = true;
 316                        }
 317
 318                        /* Clearing interrupts for processed error vectors */
 319                        elm_write_reg(info, ELM_IRQSTATUS, BIT(i));
 320
 321                        /* Disable page mode */
 322                        elm_configure_page_mode(info, i, false);
 323                }
 324        }
 325}
 326
 327/**
 328 * elm_decode_bch_error_page - Locate error position
 329 * @dev:        device pointer
 330 * @ecc_calc:   calculated ECC bytes from GPMC
 331 * @err_vec:    elm error vectors
 332 *
 333 * Called with one or more error reported vectors & vectors with
 334 * error reported is updated in err_vec[].error_reported
 335 */
 336void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
 337                struct elm_errorvec *err_vec)
 338{
 339        struct elm_info *info = dev_get_drvdata(dev);
 340        u32 reg_val;
 341
 342        /* Enable page mode interrupt */
 343        reg_val = elm_read_reg(info, ELM_IRQSTATUS);
 344        elm_write_reg(info, ELM_IRQSTATUS, reg_val & INTR_STATUS_PAGE_VALID);
 345        elm_write_reg(info, ELM_IRQENABLE, INTR_EN_PAGE_MASK);
 346
 347        /* Load valid ecc byte to syndrome fragment register */
 348        elm_load_syndrome(info, err_vec, ecc_calc);
 349
 350        /* Enable syndrome processing for which syndrome fragment is updated */
 351        elm_start_processing(info, err_vec);
 352
 353        /* Wait for ELM module to finish locating error correction */
 354        wait_for_completion(&info->elm_completion);
 355
 356        /* Disable page mode interrupt */
 357        reg_val = elm_read_reg(info, ELM_IRQENABLE);
 358        elm_write_reg(info, ELM_IRQENABLE, reg_val & ~INTR_EN_PAGE_MASK);
 359        elm_error_correction(info, err_vec);
 360}
 361EXPORT_SYMBOL(elm_decode_bch_error_page);
 362
 363static irqreturn_t elm_isr(int this_irq, void *dev_id)
 364{
 365        u32 reg_val;
 366        struct elm_info *info = dev_id;
 367
 368        reg_val = elm_read_reg(info, ELM_IRQSTATUS);
 369
 370        /* All error vectors processed */
 371        if (reg_val & INTR_STATUS_PAGE_VALID) {
 372                elm_write_reg(info, ELM_IRQSTATUS,
 373                                reg_val & INTR_STATUS_PAGE_VALID);
 374                complete(&info->elm_completion);
 375                return IRQ_HANDLED;
 376        }
 377
 378        return IRQ_NONE;
 379}
 380
 381static int elm_probe(struct platform_device *pdev)
 382{
 383        int ret = 0;
 384        struct resource *res, *irq;
 385        struct elm_info *info;
 386
 387        info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
 388        if (!info)
 389                return -ENOMEM;
 390
 391        info->dev = &pdev->dev;
 392
 393        irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 394        if (!irq) {
 395                dev_err(&pdev->dev, "no irq resource defined\n");
 396                return -ENODEV;
 397        }
 398
 399        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 400        info->elm_base = devm_ioremap_resource(&pdev->dev, res);
 401        if (IS_ERR(info->elm_base))
 402                return PTR_ERR(info->elm_base);
 403
 404        ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0,
 405                        pdev->name, info);
 406        if (ret) {
 407                dev_err(&pdev->dev, "failure requesting %pr\n", irq);
 408                return ret;
 409        }
 410
 411        pm_runtime_enable(&pdev->dev);
 412        if (pm_runtime_get_sync(&pdev->dev) < 0) {
 413                ret = -EINVAL;
 414                pm_runtime_disable(&pdev->dev);
 415                dev_err(&pdev->dev, "can't enable clock\n");
 416                return ret;
 417        }
 418
 419        init_completion(&info->elm_completion);
 420        INIT_LIST_HEAD(&info->list);
 421        list_add(&info->list, &elm_devices);
 422        platform_set_drvdata(pdev, info);
 423        return ret;
 424}
 425
 426static int elm_remove(struct platform_device *pdev)
 427{
 428        pm_runtime_put_sync(&pdev->dev);
 429        pm_runtime_disable(&pdev->dev);
 430        return 0;
 431}
 432
 433#ifdef CONFIG_PM_SLEEP
 434/**
 435 * elm_context_save
 436 * saves ELM configurations to preserve them across Hardware powered-down
 437 */
 438static int elm_context_save(struct elm_info *info)
 439{
 440        struct elm_registers *regs = &info->elm_regs;
 441        enum bch_ecc bch_type = info->bch_type;
 442        u32 offset = 0, i;
 443
 444        regs->elm_irqenable       = elm_read_reg(info, ELM_IRQENABLE);
 445        regs->elm_sysconfig       = elm_read_reg(info, ELM_SYSCONFIG);
 446        regs->elm_location_config = elm_read_reg(info, ELM_LOCATION_CONFIG);
 447        regs->elm_page_ctrl       = elm_read_reg(info, ELM_PAGE_CTRL);
 448        for (i = 0; i < ERROR_VECTOR_MAX; i++) {
 449                offset = i * SYNDROME_FRAGMENT_REG_SIZE;
 450                switch (bch_type) {
 451                case BCH16_ECC:
 452                        regs->elm_syndrome_fragment_6[i] = elm_read_reg(info,
 453                                        ELM_SYNDROME_FRAGMENT_6 + offset);
 454                        regs->elm_syndrome_fragment_5[i] = elm_read_reg(info,
 455                                        ELM_SYNDROME_FRAGMENT_5 + offset);
 456                        regs->elm_syndrome_fragment_4[i] = elm_read_reg(info,
 457                                        ELM_SYNDROME_FRAGMENT_4 + offset);
 458                        /* fall through */
 459                case BCH8_ECC:
 460                        regs->elm_syndrome_fragment_3[i] = elm_read_reg(info,
 461                                        ELM_SYNDROME_FRAGMENT_3 + offset);
 462                        regs->elm_syndrome_fragment_2[i] = elm_read_reg(info,
 463                                        ELM_SYNDROME_FRAGMENT_2 + offset);
 464                        /* fall through */
 465                case BCH4_ECC:
 466                        regs->elm_syndrome_fragment_1[i] = elm_read_reg(info,
 467                                        ELM_SYNDROME_FRAGMENT_1 + offset);
 468                        regs->elm_syndrome_fragment_0[i] = elm_read_reg(info,
 469                                        ELM_SYNDROME_FRAGMENT_0 + offset);
 470                        break;
 471                default:
 472                        return -EINVAL;
 473                }
 474                /* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs
 475                 * to be saved for all BCH schemes*/
 476                regs->elm_syndrome_fragment_6[i] = elm_read_reg(info,
 477                                        ELM_SYNDROME_FRAGMENT_6 + offset);
 478        }
 479        return 0;
 480}
 481
 482/**
 483 * elm_context_restore
 484 * writes configurations saved duing power-down back into ELM registers
 485 */
 486static int elm_context_restore(struct elm_info *info)
 487{
 488        struct elm_registers *regs = &info->elm_regs;
 489        enum bch_ecc bch_type = info->bch_type;
 490        u32 offset = 0, i;
 491
 492        elm_write_reg(info, ELM_IRQENABLE,       regs->elm_irqenable);
 493        elm_write_reg(info, ELM_SYSCONFIG,       regs->elm_sysconfig);
 494        elm_write_reg(info, ELM_LOCATION_CONFIG, regs->elm_location_config);
 495        elm_write_reg(info, ELM_PAGE_CTRL,       regs->elm_page_ctrl);
 496        for (i = 0; i < ERROR_VECTOR_MAX; i++) {
 497                offset = i * SYNDROME_FRAGMENT_REG_SIZE;
 498                switch (bch_type) {
 499                case BCH16_ECC:
 500                        elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset,
 501                                        regs->elm_syndrome_fragment_6[i]);
 502                        elm_write_reg(info, ELM_SYNDROME_FRAGMENT_5 + offset,
 503                                        regs->elm_syndrome_fragment_5[i]);
 504                        elm_write_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset,
 505                                        regs->elm_syndrome_fragment_4[i]);
 506                        /* fall through */
 507                case BCH8_ECC:
 508                        elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset,
 509                                        regs->elm_syndrome_fragment_3[i]);
 510                        elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset,
 511                                        regs->elm_syndrome_fragment_2[i]);
 512                        /* fall through */
 513                case BCH4_ECC:
 514                        elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset,
 515                                        regs->elm_syndrome_fragment_1[i]);
 516                        elm_write_reg(info, ELM_SYNDROME_FRAGMENT_0 + offset,
 517                                        regs->elm_syndrome_fragment_0[i]);
 518                        break;
 519                default:
 520                        return -EINVAL;
 521                }
 522                /* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */
 523                elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset,
 524                                        regs->elm_syndrome_fragment_6[i] &
 525                                                         ELM_SYNDROME_VALID);
 526        }
 527        return 0;
 528}
 529
 530static int elm_suspend(struct device *dev)
 531{
 532        struct elm_info *info = dev_get_drvdata(dev);
 533        elm_context_save(info);
 534        pm_runtime_put_sync(dev);
 535        return 0;
 536}
 537
 538static int elm_resume(struct device *dev)
 539{
 540        struct elm_info *info = dev_get_drvdata(dev);
 541        pm_runtime_get_sync(dev);
 542        elm_context_restore(info);
 543        return 0;
 544}
 545#endif
 546
 547static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume);
 548
 549#ifdef CONFIG_OF
 550static const struct of_device_id elm_of_match[] = {
 551        { .compatible = "ti,am3352-elm" },
 552        {},
 553};
 554MODULE_DEVICE_TABLE(of, elm_of_match);
 555#endif
 556
 557static struct platform_driver elm_driver = {
 558        .driver = {
 559                .name   = DRIVER_NAME,
 560                .of_match_table = of_match_ptr(elm_of_match),
 561                .pm     = &elm_pm_ops,
 562        },
 563        .probe  = elm_probe,
 564        .remove = elm_remove,
 565};
 566
 567module_platform_driver(elm_driver);
 568
 569MODULE_DESCRIPTION("ELM driver for BCH error correction");
 570MODULE_AUTHOR("Texas Instruments");
 571MODULE_ALIAS("platform:" DRIVER_NAME);
 572MODULE_LICENSE("GPL v2");
 573