jz4780_bch.c 9.6 KB

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