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