diff options
author | Boris Brezillon <boris.brezillon@free-electrons.com> | 2016-04-15 09:10:30 -0400 |
---|---|---|
committer | Boris Brezillon <boris.brezillon@free-electrons.com> | 2016-06-06 07:48:32 -0400 |
commit | 614049a8d9048756bde4531cd1065b491c1af275 (patch) | |
tree | 917ed5002d798623ca61def45066a252163df57b /drivers/mtd | |
parent | decba6d47869f3b5f057df5add52ece92d8e3d22 (diff) |
mtd: nand: sunxi: add support for DMA assisted operations
The sunxi NAND controller is able to pipeline ECC operations only when
operated in DMA mode, which improves a lot NAND throughput while keeping
CPU usage low.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/sunxi_nand.c | 330 |
1 files changed, 323 insertions, 7 deletions
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index a83a690688b4..ef7f6dfde80b 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c | |||
@@ -153,6 +153,7 @@ | |||
153 | 153 | ||
154 | /* define bit use in NFC_ECC_ST */ | 154 | /* define bit use in NFC_ECC_ST */ |
155 | #define NFC_ECC_ERR(x) BIT(x) | 155 | #define NFC_ECC_ERR(x) BIT(x) |
156 | #define NFC_ECC_ERR_MSK GENMASK(15, 0) | ||
156 | #define NFC_ECC_PAT_FOUND(x) BIT(x + 16) | 157 | #define NFC_ECC_PAT_FOUND(x) BIT(x + 16) |
157 | #define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff) | 158 | #define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff) |
158 | 159 | ||
@@ -273,6 +274,7 @@ struct sunxi_nfc { | |||
273 | unsigned long clk_rate; | 274 | unsigned long clk_rate; |
274 | struct list_head chips; | 275 | struct list_head chips; |
275 | struct completion complete; | 276 | struct completion complete; |
277 | struct dma_chan *dmac; | ||
276 | }; | 278 | }; |
277 | 279 | ||
278 | static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl) | 280 | static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl) |
@@ -365,6 +367,67 @@ static int sunxi_nfc_rst(struct sunxi_nfc *nfc) | |||
365 | return ret; | 367 | return ret; |
366 | } | 368 | } |
367 | 369 | ||
370 | static int sunxi_nfc_dma_op_prepare(struct mtd_info *mtd, const void *buf, | ||
371 | int chunksize, int nchunks, | ||
372 | enum dma_data_direction ddir, | ||
373 | struct scatterlist *sg) | ||
374 | { | ||
375 | struct nand_chip *nand = mtd_to_nand(mtd); | ||
376 | struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); | ||
377 | struct dma_async_tx_descriptor *dmad; | ||
378 | enum dma_transfer_direction tdir; | ||
379 | dma_cookie_t dmat; | ||
380 | int ret; | ||
381 | |||
382 | if (ddir == DMA_FROM_DEVICE) | ||
383 | tdir = DMA_DEV_TO_MEM; | ||
384 | else | ||
385 | tdir = DMA_MEM_TO_DEV; | ||
386 | |||
387 | sg_init_one(sg, buf, nchunks * chunksize); | ||
388 | ret = dma_map_sg(nfc->dev, sg, 1, ddir); | ||
389 | if (!ret) | ||
390 | return -ENOMEM; | ||
391 | |||
392 | dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK); | ||
393 | if (IS_ERR(dmad)) { | ||
394 | ret = PTR_ERR(dmad); | ||
395 | goto err_unmap_buf; | ||
396 | } | ||
397 | |||
398 | writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD, | ||
399 | nfc->regs + NFC_REG_CTL); | ||
400 | writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM); | ||
401 | writel(chunksize, nfc->regs + NFC_REG_CNT); | ||
402 | dmat = dmaengine_submit(dmad); | ||
403 | |||
404 | ret = dma_submit_error(dmat); | ||
405 | if (ret) | ||
406 | goto err_clr_dma_flag; | ||
407 | |||
408 | return 0; | ||
409 | |||
410 | err_clr_dma_flag: | ||
411 | writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, | ||
412 | nfc->regs + NFC_REG_CTL); | ||
413 | |||
414 | err_unmap_buf: | ||
415 | dma_unmap_sg(nfc->dev, sg, 1, ddir); | ||
416 | return ret; | ||
417 | } | ||
418 | |||
419 | static void sunxi_nfc_dma_op_cleanup(struct mtd_info *mtd, | ||
420 | enum dma_data_direction ddir, | ||
421 | struct scatterlist *sg) | ||
422 | { | ||
423 | struct nand_chip *nand = mtd_to_nand(mtd); | ||
424 | struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); | ||
425 | |||
426 | dma_unmap_sg(nfc->dev, sg, 1, ddir); | ||
427 | writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, | ||
428 | nfc->regs + NFC_REG_CTL); | ||
429 | } | ||
430 | |||
368 | static int sunxi_nfc_dev_ready(struct mtd_info *mtd) | 431 | static int sunxi_nfc_dev_ready(struct mtd_info *mtd) |
369 | { | 432 | { |
370 | struct nand_chip *nand = mtd_to_nand(mtd); | 433 | struct nand_chip *nand = mtd_to_nand(mtd); |
@@ -822,17 +885,15 @@ static void sunxi_nfc_hw_ecc_update_stats(struct mtd_info *mtd, | |||
822 | } | 885 | } |
823 | 886 | ||
824 | static int sunxi_nfc_hw_ecc_correct(struct mtd_info *mtd, u8 *data, u8 *oob, | 887 | static int sunxi_nfc_hw_ecc_correct(struct mtd_info *mtd, u8 *data, u8 *oob, |
825 | int step, bool *erased) | 888 | int step, u32 status, bool *erased) |
826 | { | 889 | { |
827 | struct nand_chip *nand = mtd_to_nand(mtd); | 890 | struct nand_chip *nand = mtd_to_nand(mtd); |
828 | struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); | 891 | struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); |
829 | struct nand_ecc_ctrl *ecc = &nand->ecc; | 892 | struct nand_ecc_ctrl *ecc = &nand->ecc; |
830 | u32 status, tmp; | 893 | u32 tmp; |
831 | 894 | ||
832 | *erased = false; | 895 | *erased = false; |
833 | 896 | ||
834 | status = readl(nfc->regs + NFC_REG_ECC_ST); | ||
835 | |||
836 | if (status & NFC_ECC_ERR(step)) | 897 | if (status & NFC_ECC_ERR(step)) |
837 | return -EBADMSG; | 898 | return -EBADMSG; |
838 | 899 | ||
@@ -898,6 +959,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, | |||
898 | *cur_off = oob_off + ecc->bytes + 4; | 959 | *cur_off = oob_off + ecc->bytes + 4; |
899 | 960 | ||
900 | ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob_required ? oob : NULL, 0, | 961 | ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob_required ? oob : NULL, 0, |
962 | readl(nfc->regs + NFC_REG_ECC_ST), | ||
901 | &erased); | 963 | &erased); |
902 | if (erased) | 964 | if (erased) |
903 | return 1; | 965 | return 1; |
@@ -967,6 +1029,128 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, | |||
967 | *cur_off = mtd->oobsize + mtd->writesize; | 1029 | *cur_off = mtd->oobsize + mtd->writesize; |
968 | } | 1030 | } |
969 | 1031 | ||
1032 | static int sunxi_nfc_hw_ecc_read_chunks_dma(struct mtd_info *mtd, uint8_t *buf, | ||
1033 | int oob_required, int page, | ||
1034 | int nchunks) | ||
1035 | { | ||
1036 | struct nand_chip *nand = mtd_to_nand(mtd); | ||
1037 | bool randomized = nand->options & NAND_NEED_SCRAMBLING; | ||
1038 | struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); | ||
1039 | struct nand_ecc_ctrl *ecc = &nand->ecc; | ||
1040 | unsigned int max_bitflips = 0; | ||
1041 | int ret, i, raw_mode = 0; | ||
1042 | struct scatterlist sg; | ||
1043 | u32 status; | ||
1044 | |||
1045 | ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); | ||
1046 | if (ret) | ||
1047 | return ret; | ||
1048 | |||
1049 | ret = sunxi_nfc_dma_op_prepare(mtd, buf, ecc->size, nchunks, | ||
1050 | DMA_FROM_DEVICE, &sg); | ||
1051 | if (ret) | ||
1052 | return ret; | ||
1053 | |||
1054 | sunxi_nfc_hw_ecc_enable(mtd); | ||
1055 | sunxi_nfc_randomizer_config(mtd, page, false); | ||
1056 | sunxi_nfc_randomizer_enable(mtd); | ||
1057 | |||
1058 | writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) | | ||
1059 | NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET); | ||
1060 | |||
1061 | dma_async_issue_pending(nfc->dmac); | ||
1062 | |||
1063 | writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS, | ||
1064 | nfc->regs + NFC_REG_CMD); | ||
1065 | |||
1066 | ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); | ||
1067 | if (ret) | ||
1068 | dmaengine_terminate_all(nfc->dmac); | ||
1069 | |||
1070 | sunxi_nfc_randomizer_disable(mtd); | ||
1071 | sunxi_nfc_hw_ecc_disable(mtd); | ||
1072 | |||
1073 | sunxi_nfc_dma_op_cleanup(mtd, DMA_FROM_DEVICE, &sg); | ||
1074 | |||
1075 | if (ret) | ||
1076 | return ret; | ||
1077 | |||
1078 | status = readl(nfc->regs + NFC_REG_ECC_ST); | ||
1079 | |||
1080 | for (i = 0; i < nchunks; i++) { | ||
1081 | int data_off = i * ecc->size; | ||
1082 | int oob_off = i * (ecc->bytes + 4); | ||
1083 | u8 *data = buf + data_off; | ||
1084 | u8 *oob = nand->oob_poi + oob_off; | ||
1085 | bool erased; | ||
1086 | |||
1087 | ret = sunxi_nfc_hw_ecc_correct(mtd, randomized ? data : NULL, | ||
1088 | oob_required ? oob : NULL, | ||
1089 | i, status, &erased); | ||
1090 | |||
1091 | /* ECC errors are handled in the second loop. */ | ||
1092 | if (ret < 0) | ||
1093 | continue; | ||
1094 | |||
1095 | if (oob_required && !erased) { | ||
1096 | /* TODO: use DMA to retrieve OOB */ | ||
1097 | nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); | ||
1098 | nand->read_buf(mtd, oob, ecc->bytes + 4); | ||
1099 | |||
1100 | sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, i, | ||
1101 | !i, page); | ||
1102 | } | ||
1103 | |||
1104 | if (erased) | ||
1105 | raw_mode = 1; | ||
1106 | |||
1107 | sunxi_nfc_hw_ecc_update_stats(mtd, &max_bitflips, ret); | ||
1108 | } | ||
1109 | |||
1110 | if (status & NFC_ECC_ERR_MSK) { | ||
1111 | for (i = 0; i < nchunks; i++) { | ||
1112 | int data_off = i * ecc->size; | ||
1113 | int oob_off = i * (ecc->bytes + 4); | ||
1114 | u8 *data = buf + data_off; | ||
1115 | u8 *oob = nand->oob_poi + oob_off; | ||
1116 | |||
1117 | if (!(status & NFC_ECC_ERR(i))) | ||
1118 | continue; | ||
1119 | |||
1120 | /* | ||
1121 | * Re-read the data with the randomizer disabled to | ||
1122 | * identify bitflips in erased pages. | ||
1123 | */ | ||
1124 | if (randomized) { | ||
1125 | /* TODO: use DMA to read page in raw mode */ | ||
1126 | nand->cmdfunc(mtd, NAND_CMD_RNDOUT, | ||
1127 | data_off, -1); | ||
1128 | nand->read_buf(mtd, data, ecc->size); | ||
1129 | } | ||
1130 | |||
1131 | /* TODO: use DMA to retrieve OOB */ | ||
1132 | nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); | ||
1133 | nand->read_buf(mtd, oob, ecc->bytes + 4); | ||
1134 | |||
1135 | ret = nand_check_erased_ecc_chunk(data, ecc->size, | ||
1136 | oob, ecc->bytes + 4, | ||
1137 | NULL, 0, | ||
1138 | ecc->strength); | ||
1139 | if (ret >= 0) | ||
1140 | raw_mode = 1; | ||
1141 | |||
1142 | sunxi_nfc_hw_ecc_update_stats(mtd, &max_bitflips, ret); | ||
1143 | } | ||
1144 | } | ||
1145 | |||
1146 | if (oob_required) | ||
1147 | sunxi_nfc_hw_ecc_read_extra_oob(mtd, nand->oob_poi, | ||
1148 | NULL, !raw_mode, | ||
1149 | page); | ||
1150 | |||
1151 | return max_bitflips; | ||
1152 | } | ||
1153 | |||
970 | static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, | 1154 | static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, |
971 | const u8 *data, int data_off, | 1155 | const u8 *data, int data_off, |
972 | const u8 *oob, int oob_off, | 1156 | const u8 *oob, int oob_off, |
@@ -1065,6 +1249,23 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, | |||
1065 | return max_bitflips; | 1249 | return max_bitflips; |
1066 | } | 1250 | } |
1067 | 1251 | ||
1252 | static int sunxi_nfc_hw_ecc_read_page_dma(struct mtd_info *mtd, | ||
1253 | struct nand_chip *chip, u8 *buf, | ||
1254 | int oob_required, int page) | ||
1255 | { | ||
1256 | int ret; | ||
1257 | |||
1258 | ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, oob_required, page, | ||
1259 | chip->ecc.steps); | ||
1260 | if (ret >= 0) | ||
1261 | return ret; | ||
1262 | |||
1263 | /* Fallback to PIO mode */ | ||
1264 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); | ||
1265 | |||
1266 | return sunxi_nfc_hw_ecc_read_page(mtd, chip, buf, oob_required, page); | ||
1267 | } | ||
1268 | |||
1068 | static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd, | 1269 | static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd, |
1069 | struct nand_chip *chip, | 1270 | struct nand_chip *chip, |
1070 | u32 data_offs, u32 readlen, | 1271 | u32 data_offs, u32 readlen, |
@@ -1098,6 +1299,25 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd, | |||
1098 | return max_bitflips; | 1299 | return max_bitflips; |
1099 | } | 1300 | } |
1100 | 1301 | ||
1302 | static int sunxi_nfc_hw_ecc_read_subpage_dma(struct mtd_info *mtd, | ||
1303 | struct nand_chip *chip, | ||
1304 | u32 data_offs, u32 readlen, | ||
1305 | u8 *buf, int page) | ||
1306 | { | ||
1307 | int nchunks = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size); | ||
1308 | int ret; | ||
1309 | |||
1310 | ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, false, page, nchunks); | ||
1311 | if (ret >= 0) | ||
1312 | return ret; | ||
1313 | |||
1314 | /* Fallback to PIO mode */ | ||
1315 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); | ||
1316 | |||
1317 | return sunxi_nfc_hw_ecc_read_subpage(mtd, chip, data_offs, readlen, | ||
1318 | buf, page); | ||
1319 | } | ||
1320 | |||
1101 | static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, | 1321 | static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, |
1102 | struct nand_chip *chip, | 1322 | struct nand_chip *chip, |
1103 | const uint8_t *buf, int oob_required, | 1323 | const uint8_t *buf, int oob_required, |
@@ -1130,6 +1350,69 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, | |||
1130 | return 0; | 1350 | return 0; |
1131 | } | 1351 | } |
1132 | 1352 | ||
1353 | static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd, | ||
1354 | struct nand_chip *chip, | ||
1355 | const u8 *buf, | ||
1356 | int oob_required, | ||
1357 | int page) | ||
1358 | { | ||
1359 | struct nand_chip *nand = mtd_to_nand(mtd); | ||
1360 | struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); | ||
1361 | struct nand_ecc_ctrl *ecc = &nand->ecc; | ||
1362 | struct scatterlist sg; | ||
1363 | int ret, i; | ||
1364 | |||
1365 | ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); | ||
1366 | if (ret) | ||
1367 | return ret; | ||
1368 | |||
1369 | ret = sunxi_nfc_dma_op_prepare(mtd, buf, ecc->size, ecc->steps, | ||
1370 | DMA_TO_DEVICE, &sg); | ||
1371 | if (ret) | ||
1372 | goto pio_fallback; | ||
1373 | |||
1374 | for (i = 0; i < ecc->steps; i++) { | ||
1375 | const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4)); | ||
1376 | |||
1377 | sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, i, !i, page); | ||
1378 | } | ||
1379 | |||
1380 | sunxi_nfc_hw_ecc_enable(mtd); | ||
1381 | sunxi_nfc_randomizer_config(mtd, page, false); | ||
1382 | sunxi_nfc_randomizer_enable(mtd); | ||
1383 | |||
1384 | writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG, | ||
1385 | nfc->regs + NFC_REG_RCMD_SET); | ||
1386 | |||
1387 | dma_async_issue_pending(nfc->dmac); | ||
1388 | |||
1389 | writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | | ||
1390 | NFC_DATA_TRANS | NFC_ACCESS_DIR, | ||
1391 | nfc->regs + NFC_REG_CMD); | ||
1392 | |||
1393 | ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); | ||
1394 | if (ret) | ||
1395 | dmaengine_terminate_all(nfc->dmac); | ||
1396 | |||
1397 | sunxi_nfc_randomizer_disable(mtd); | ||
1398 | sunxi_nfc_hw_ecc_disable(mtd); | ||
1399 | |||
1400 | sunxi_nfc_dma_op_cleanup(mtd, DMA_TO_DEVICE, &sg); | ||
1401 | |||
1402 | if (ret) | ||
1403 | return ret; | ||
1404 | |||
1405 | if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) | ||
1406 | /* TODO: use DMA to transfer extra OOB bytes ? */ | ||
1407 | sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, | ||
1408 | NULL, page); | ||
1409 | |||
1410 | return 0; | ||
1411 | |||
1412 | pio_fallback: | ||
1413 | return sunxi_nfc_hw_ecc_write_page(mtd, chip, buf, oob_required, page); | ||
1414 | } | ||
1415 | |||
1133 | static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, | 1416 | static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, |
1134 | struct nand_chip *chip, | 1417 | struct nand_chip *chip, |
1135 | uint8_t *buf, int oob_required, | 1418 | uint8_t *buf, int oob_required, |
@@ -1550,14 +1833,27 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, | |||
1550 | struct nand_ecc_ctrl *ecc, | 1833 | struct nand_ecc_ctrl *ecc, |
1551 | struct device_node *np) | 1834 | struct device_node *np) |
1552 | { | 1835 | { |
1836 | struct nand_chip *nand = mtd_to_nand(mtd); | ||
1837 | struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); | ||
1838 | struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); | ||
1553 | int ret; | 1839 | int ret; |
1554 | 1840 | ||
1555 | ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np); | 1841 | ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np); |
1556 | if (ret) | 1842 | if (ret) |
1557 | return ret; | 1843 | return ret; |
1558 | 1844 | ||
1559 | ecc->read_page = sunxi_nfc_hw_ecc_read_page; | 1845 | if (nfc->dmac) { |
1560 | ecc->write_page = sunxi_nfc_hw_ecc_write_page; | 1846 | ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma; |
1847 | ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma; | ||
1848 | ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma; | ||
1849 | nand->options |= NAND_USE_BOUNCE_BUFFER; | ||
1850 | } else { | ||
1851 | ecc->read_page = sunxi_nfc_hw_ecc_read_page; | ||
1852 | ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage; | ||
1853 | ecc->write_page = sunxi_nfc_hw_ecc_write_page; | ||
1854 | } | ||
1855 | |||
1856 | /* TODO: support DMA for raw accesses */ | ||
1561 | ecc->read_oob_raw = nand_read_oob_std; | 1857 | ecc->read_oob_raw = nand_read_oob_std; |
1562 | ecc->write_oob_raw = nand_write_oob_std; | 1858 | ecc->write_oob_raw = nand_write_oob_std; |
1563 | ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage; | 1859 | ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage; |
@@ -1881,16 +2177,34 @@ static int sunxi_nfc_probe(struct platform_device *pdev) | |||
1881 | if (ret) | 2177 | if (ret) |
1882 | goto out_mod_clk_unprepare; | 2178 | goto out_mod_clk_unprepare; |
1883 | 2179 | ||
2180 | nfc->dmac = dma_request_slave_channel(dev, "rxtx"); | ||
2181 | if (nfc->dmac) { | ||
2182 | struct dma_slave_config dmac_cfg = { }; | ||
2183 | |||
2184 | dmac_cfg.src_addr = r->start + NFC_REG_IO_DATA; | ||
2185 | dmac_cfg.dst_addr = dmac_cfg.src_addr; | ||
2186 | dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||
2187 | dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width; | ||
2188 | dmac_cfg.src_maxburst = 4; | ||
2189 | dmac_cfg.dst_maxburst = 4; | ||
2190 | dmaengine_slave_config(nfc->dmac, &dmac_cfg); | ||
2191 | } else { | ||
2192 | dev_warn(dev, "failed to request rxtx DMA channel\n"); | ||
2193 | } | ||
2194 | |||
1884 | platform_set_drvdata(pdev, nfc); | 2195 | platform_set_drvdata(pdev, nfc); |
1885 | 2196 | ||
1886 | ret = sunxi_nand_chips_init(dev, nfc); | 2197 | ret = sunxi_nand_chips_init(dev, nfc); |
1887 | if (ret) { | 2198 | if (ret) { |
1888 | dev_err(dev, "failed to init nand chips\n"); | 2199 | dev_err(dev, "failed to init nand chips\n"); |
1889 | goto out_mod_clk_unprepare; | 2200 | goto out_release_dmac; |
1890 | } | 2201 | } |
1891 | 2202 | ||
1892 | return 0; | 2203 | return 0; |
1893 | 2204 | ||
2205 | out_release_dmac: | ||
2206 | if (nfc->dmac) | ||
2207 | dma_release_channel(nfc->dmac); | ||
1894 | out_mod_clk_unprepare: | 2208 | out_mod_clk_unprepare: |
1895 | clk_disable_unprepare(nfc->mod_clk); | 2209 | clk_disable_unprepare(nfc->mod_clk); |
1896 | out_ahb_clk_unprepare: | 2210 | out_ahb_clk_unprepare: |
@@ -1904,6 +2218,8 @@ static int sunxi_nfc_remove(struct platform_device *pdev) | |||
1904 | struct sunxi_nfc *nfc = platform_get_drvdata(pdev); | 2218 | struct sunxi_nfc *nfc = platform_get_drvdata(pdev); |
1905 | 2219 | ||
1906 | sunxi_nand_chips_cleanup(nfc); | 2220 | sunxi_nand_chips_cleanup(nfc); |
2221 | if (nfc->dmac) | ||
2222 | dma_release_channel(nfc->dmac); | ||
1907 | clk_disable_unprepare(nfc->mod_clk); | 2223 | clk_disable_unprepare(nfc->mod_clk); |
1908 | clk_disable_unprepare(nfc->ahb_clk); | 2224 | clk_disable_unprepare(nfc->ahb_clk); |
1909 | 2225 | ||