linux/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * JZ47xx ECC common code
   4 *
   5 * Copyright (c) 2015 Imagination Technologies
   6 * Author: Alex Smith <alex.smith@imgtec.com>
   7 */
   8
   9#include <linux/clk.h>
  10#include <linux/init.h>
  11#include <linux/module.h>
  12#include <linux/of_platform.h>
  13#include <linux/platform_device.h>
  14
  15#include "ingenic_ecc.h"
  16
  17/**
  18 * ingenic_ecc_calculate() - calculate ECC for a data buffer
  19 * @ecc: ECC device.
  20 * @params: ECC parameters.
  21 * @buf: input buffer with raw data.
  22 * @ecc_code: output buffer with ECC.
  23 *
  24 * Return: 0 on success, -ETIMEDOUT if timed out while waiting for ECC
  25 * controller.
  26 */
  27int ingenic_ecc_calculate(struct ingenic_ecc *ecc,
  28                          struct ingenic_ecc_params *params,
  29                          const u8 *buf, u8 *ecc_code)
  30{
  31        return ecc->ops->calculate(ecc, params, buf, ecc_code);
  32}
  33
  34/**
  35 * ingenic_ecc_correct() - detect and correct bit errors
  36 * @ecc: ECC device.
  37 * @params: ECC parameters.
  38 * @buf: raw data read from the chip.
  39 * @ecc_code: ECC read from the chip.
  40 *
  41 * Given the raw data and the ECC read from the NAND device, detects and
  42 * corrects errors in the data.
  43 *
  44 * Return: the number of bit errors corrected, -EBADMSG if there are too many
  45 * errors to correct or -ETIMEDOUT if we timed out waiting for the controller.
  46 */
  47int ingenic_ecc_correct(struct ingenic_ecc *ecc,
  48                        struct ingenic_ecc_params *params,
  49                        u8 *buf, u8 *ecc_code)
  50{
  51        return ecc->ops->correct(ecc, params, buf, ecc_code);
  52}
  53
  54/**
  55 * ingenic_ecc_get() - get the ECC controller device
  56 * @np: ECC device tree node.
  57 *
  58 * Gets the ECC controller device from the specified device tree node. The
  59 * device must be released with ingenic_ecc_release() when it is no longer being
  60 * used.
  61 *
  62 * Return: a pointer to ingenic_ecc, errors are encoded into the pointer.
  63 * PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet.
  64 */
  65static struct ingenic_ecc *ingenic_ecc_get(struct device_node *np)
  66{
  67        struct platform_device *pdev;
  68        struct ingenic_ecc *ecc;
  69
  70        pdev = of_find_device_by_node(np);
  71        if (!pdev || !platform_get_drvdata(pdev))
  72                return ERR_PTR(-EPROBE_DEFER);
  73
  74        ecc = platform_get_drvdata(pdev);
  75        clk_prepare_enable(ecc->clk);
  76
  77        return ecc;
  78}
  79
  80/**
  81 * of_ingenic_ecc_get() - get the ECC controller from a DT node
  82 * @of_node: the node that contains an ecc-engine property.
  83 *
  84 * Get the ecc-engine property from the given device tree
  85 * node and pass it to ingenic_ecc_get to do the work.
  86 *
  87 * Return: a pointer to ingenic_ecc, errors are encoded into the pointer.
  88 * PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet.
  89 */
  90struct ingenic_ecc *of_ingenic_ecc_get(struct device_node *of_node)
  91{
  92        struct ingenic_ecc *ecc = NULL;
  93        struct device_node *np;
  94
  95        np = of_parse_phandle(of_node, "ecc-engine", 0);
  96
  97        /*
  98         * If the ecc-engine property is not found, check for the deprecated
  99         * ingenic,bch-controller property
 100         */
 101        if (!np)
 102                np = of_parse_phandle(of_node, "ingenic,bch-controller", 0);
 103
 104        if (np) {
 105                ecc = ingenic_ecc_get(np);
 106                of_node_put(np);
 107        }
 108        return ecc;
 109}
 110
 111/**
 112 * ingenic_ecc_release() - release the ECC controller device
 113 * @ecc: ECC device.
 114 */
 115void ingenic_ecc_release(struct ingenic_ecc *ecc)
 116{
 117        clk_disable_unprepare(ecc->clk);
 118        put_device(ecc->dev);
 119}
 120
 121int ingenic_ecc_probe(struct platform_device *pdev)
 122{
 123        struct device *dev = &pdev->dev;
 124        struct ingenic_ecc *ecc;
 125
 126        ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL);
 127        if (!ecc)
 128                return -ENOMEM;
 129
 130        ecc->ops = device_get_match_data(dev);
 131        if (!ecc->ops)
 132                return -EINVAL;
 133
 134        ecc->base = devm_platform_ioremap_resource(pdev, 0);
 135        if (IS_ERR(ecc->base))
 136                return PTR_ERR(ecc->base);
 137
 138        ecc->ops->disable(ecc);
 139
 140        ecc->clk = devm_clk_get(dev, NULL);
 141        if (IS_ERR(ecc->clk)) {
 142                dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(ecc->clk));
 143                return PTR_ERR(ecc->clk);
 144        }
 145
 146        mutex_init(&ecc->lock);
 147
 148        ecc->dev = dev;
 149        platform_set_drvdata(pdev, ecc);
 150
 151        return 0;
 152}
 153EXPORT_SYMBOL(ingenic_ecc_probe);
 154