18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2016, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/clk.h> 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci#include <linux/bitops.h> 88c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 98c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 128c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/of_device.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/dma/qcom_bam_dma.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* NANDc reg offsets */ 198c2ecf20Sopenharmony_ci#define NAND_FLASH_CMD 0x00 208c2ecf20Sopenharmony_ci#define NAND_ADDR0 0x04 218c2ecf20Sopenharmony_ci#define NAND_ADDR1 0x08 228c2ecf20Sopenharmony_ci#define NAND_FLASH_CHIP_SELECT 0x0c 238c2ecf20Sopenharmony_ci#define NAND_EXEC_CMD 0x10 248c2ecf20Sopenharmony_ci#define NAND_FLASH_STATUS 0x14 258c2ecf20Sopenharmony_ci#define NAND_BUFFER_STATUS 0x18 268c2ecf20Sopenharmony_ci#define NAND_DEV0_CFG0 0x20 278c2ecf20Sopenharmony_ci#define NAND_DEV0_CFG1 0x24 288c2ecf20Sopenharmony_ci#define NAND_DEV0_ECC_CFG 0x28 298c2ecf20Sopenharmony_ci#define NAND_DEV1_ECC_CFG 0x2c 308c2ecf20Sopenharmony_ci#define NAND_DEV1_CFG0 0x30 318c2ecf20Sopenharmony_ci#define NAND_DEV1_CFG1 0x34 328c2ecf20Sopenharmony_ci#define NAND_READ_ID 0x40 338c2ecf20Sopenharmony_ci#define NAND_READ_STATUS 0x44 348c2ecf20Sopenharmony_ci#define NAND_DEV_CMD0 0xa0 358c2ecf20Sopenharmony_ci#define NAND_DEV_CMD1 0xa4 368c2ecf20Sopenharmony_ci#define NAND_DEV_CMD2 0xa8 378c2ecf20Sopenharmony_ci#define NAND_DEV_CMD_VLD 0xac 388c2ecf20Sopenharmony_ci#define SFLASHC_BURST_CFG 0xe0 398c2ecf20Sopenharmony_ci#define NAND_ERASED_CW_DETECT_CFG 0xe8 408c2ecf20Sopenharmony_ci#define NAND_ERASED_CW_DETECT_STATUS 0xec 418c2ecf20Sopenharmony_ci#define NAND_EBI2_ECC_BUF_CFG 0xf0 428c2ecf20Sopenharmony_ci#define FLASH_BUF_ACC 0x100 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define NAND_CTRL 0xf00 458c2ecf20Sopenharmony_ci#define NAND_VERSION 0xf08 468c2ecf20Sopenharmony_ci#define NAND_READ_LOCATION_0 0xf20 478c2ecf20Sopenharmony_ci#define NAND_READ_LOCATION_1 0xf24 488c2ecf20Sopenharmony_ci#define NAND_READ_LOCATION_2 0xf28 498c2ecf20Sopenharmony_ci#define NAND_READ_LOCATION_3 0xf2c 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* dummy register offsets, used by write_reg_dma */ 528c2ecf20Sopenharmony_ci#define NAND_DEV_CMD1_RESTORE 0xdead 538c2ecf20Sopenharmony_ci#define NAND_DEV_CMD_VLD_RESTORE 0xbeef 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* NAND_FLASH_CMD bits */ 568c2ecf20Sopenharmony_ci#define PAGE_ACC BIT(4) 578c2ecf20Sopenharmony_ci#define LAST_PAGE BIT(5) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* NAND_FLASH_CHIP_SELECT bits */ 608c2ecf20Sopenharmony_ci#define NAND_DEV_SEL 0 618c2ecf20Sopenharmony_ci#define DM_EN BIT(2) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* NAND_FLASH_STATUS bits */ 648c2ecf20Sopenharmony_ci#define FS_OP_ERR BIT(4) 658c2ecf20Sopenharmony_ci#define FS_READY_BSY_N BIT(5) 668c2ecf20Sopenharmony_ci#define FS_MPU_ERR BIT(8) 678c2ecf20Sopenharmony_ci#define FS_DEVICE_STS_ERR BIT(16) 688c2ecf20Sopenharmony_ci#define FS_DEVICE_WP BIT(23) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* NAND_BUFFER_STATUS bits */ 718c2ecf20Sopenharmony_ci#define BS_UNCORRECTABLE_BIT BIT(8) 728c2ecf20Sopenharmony_ci#define BS_CORRECTABLE_ERR_MSK 0x1f 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* NAND_DEVn_CFG0 bits */ 758c2ecf20Sopenharmony_ci#define DISABLE_STATUS_AFTER_WRITE 4 768c2ecf20Sopenharmony_ci#define CW_PER_PAGE 6 778c2ecf20Sopenharmony_ci#define UD_SIZE_BYTES 9 788c2ecf20Sopenharmony_ci#define ECC_PARITY_SIZE_BYTES_RS 19 798c2ecf20Sopenharmony_ci#define SPARE_SIZE_BYTES 23 808c2ecf20Sopenharmony_ci#define NUM_ADDR_CYCLES 27 818c2ecf20Sopenharmony_ci#define STATUS_BFR_READ 30 828c2ecf20Sopenharmony_ci#define SET_RD_MODE_AFTER_STATUS 31 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* NAND_DEVn_CFG0 bits */ 858c2ecf20Sopenharmony_ci#define DEV0_CFG1_ECC_DISABLE 0 868c2ecf20Sopenharmony_ci#define WIDE_FLASH 1 878c2ecf20Sopenharmony_ci#define NAND_RECOVERY_CYCLES 2 888c2ecf20Sopenharmony_ci#define CS_ACTIVE_BSY 5 898c2ecf20Sopenharmony_ci#define BAD_BLOCK_BYTE_NUM 6 908c2ecf20Sopenharmony_ci#define BAD_BLOCK_IN_SPARE_AREA 16 918c2ecf20Sopenharmony_ci#define WR_RD_BSY_GAP 17 928c2ecf20Sopenharmony_ci#define ENABLE_BCH_ECC 27 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* NAND_DEV0_ECC_CFG bits */ 958c2ecf20Sopenharmony_ci#define ECC_CFG_ECC_DISABLE 0 968c2ecf20Sopenharmony_ci#define ECC_SW_RESET 1 978c2ecf20Sopenharmony_ci#define ECC_MODE 4 988c2ecf20Sopenharmony_ci#define ECC_PARITY_SIZE_BYTES_BCH 8 998c2ecf20Sopenharmony_ci#define ECC_NUM_DATA_BYTES 16 1008c2ecf20Sopenharmony_ci#define ECC_FORCE_CLK_OPEN 30 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* NAND_DEV_CMD1 bits */ 1038c2ecf20Sopenharmony_ci#define READ_ADDR 0 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* NAND_DEV_CMD_VLD bits */ 1068c2ecf20Sopenharmony_ci#define READ_START_VLD BIT(0) 1078c2ecf20Sopenharmony_ci#define READ_STOP_VLD BIT(1) 1088c2ecf20Sopenharmony_ci#define WRITE_START_VLD BIT(2) 1098c2ecf20Sopenharmony_ci#define ERASE_START_VLD BIT(3) 1108c2ecf20Sopenharmony_ci#define SEQ_READ_START_VLD BIT(4) 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* NAND_EBI2_ECC_BUF_CFG bits */ 1138c2ecf20Sopenharmony_ci#define NUM_STEPS 0 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* NAND_ERASED_CW_DETECT_CFG bits */ 1168c2ecf20Sopenharmony_ci#define ERASED_CW_ECC_MASK 1 1178c2ecf20Sopenharmony_ci#define AUTO_DETECT_RES 0 1188c2ecf20Sopenharmony_ci#define MASK_ECC (1 << ERASED_CW_ECC_MASK) 1198c2ecf20Sopenharmony_ci#define RESET_ERASED_DET (1 << AUTO_DETECT_RES) 1208c2ecf20Sopenharmony_ci#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) 1218c2ecf20Sopenharmony_ci#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) 1228c2ecf20Sopenharmony_ci#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* NAND_ERASED_CW_DETECT_STATUS bits */ 1258c2ecf20Sopenharmony_ci#define PAGE_ALL_ERASED BIT(7) 1268c2ecf20Sopenharmony_ci#define CODEWORD_ALL_ERASED BIT(6) 1278c2ecf20Sopenharmony_ci#define PAGE_ERASED BIT(5) 1288c2ecf20Sopenharmony_ci#define CODEWORD_ERASED BIT(4) 1298c2ecf20Sopenharmony_ci#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) 1308c2ecf20Sopenharmony_ci#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* NAND_READ_LOCATION_n bits */ 1338c2ecf20Sopenharmony_ci#define READ_LOCATION_OFFSET 0 1348c2ecf20Sopenharmony_ci#define READ_LOCATION_SIZE 16 1358c2ecf20Sopenharmony_ci#define READ_LOCATION_LAST 31 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* Version Mask */ 1388c2ecf20Sopenharmony_ci#define NAND_VERSION_MAJOR_MASK 0xf0000000 1398c2ecf20Sopenharmony_ci#define NAND_VERSION_MAJOR_SHIFT 28 1408c2ecf20Sopenharmony_ci#define NAND_VERSION_MINOR_MASK 0x0fff0000 1418c2ecf20Sopenharmony_ci#define NAND_VERSION_MINOR_SHIFT 16 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* NAND OP_CMDs */ 1448c2ecf20Sopenharmony_ci#define OP_PAGE_READ 0x2 1458c2ecf20Sopenharmony_ci#define OP_PAGE_READ_WITH_ECC 0x3 1468c2ecf20Sopenharmony_ci#define OP_PAGE_READ_WITH_ECC_SPARE 0x4 1478c2ecf20Sopenharmony_ci#define OP_PROGRAM_PAGE 0x6 1488c2ecf20Sopenharmony_ci#define OP_PAGE_PROGRAM_WITH_ECC 0x7 1498c2ecf20Sopenharmony_ci#define OP_PROGRAM_PAGE_SPARE 0x9 1508c2ecf20Sopenharmony_ci#define OP_BLOCK_ERASE 0xa 1518c2ecf20Sopenharmony_ci#define OP_FETCH_ID 0xb 1528c2ecf20Sopenharmony_ci#define OP_RESET_DEVICE 0xd 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* Default Value for NAND_DEV_CMD_VLD */ 1558c2ecf20Sopenharmony_ci#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \ 1568c2ecf20Sopenharmony_ci ERASE_START_VLD | SEQ_READ_START_VLD) 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* NAND_CTRL bits */ 1598c2ecf20Sopenharmony_ci#define BAM_MODE_EN BIT(0) 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/* 1628c2ecf20Sopenharmony_ci * the NAND controller performs reads/writes with ECC in 516 byte chunks. 1638c2ecf20Sopenharmony_ci * the driver calls the chunks 'step' or 'codeword' interchangeably 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci#define NANDC_STEP_SIZE 512 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* 1688c2ecf20Sopenharmony_ci * the largest page size we support is 8K, this will have 16 steps/codewords 1698c2ecf20Sopenharmony_ci * of 512 bytes each 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ci#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE) 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* we read at most 3 registers per codeword scan */ 1748c2ecf20Sopenharmony_ci#define MAX_REG_RD (3 * MAX_NUM_STEPS) 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/* ECC modes supported by the controller */ 1778c2ecf20Sopenharmony_ci#define ECC_NONE BIT(0) 1788c2ecf20Sopenharmony_ci#define ECC_RS_4BIT BIT(1) 1798c2ecf20Sopenharmony_ci#define ECC_BCH_4BIT BIT(2) 1808c2ecf20Sopenharmony_ci#define ECC_BCH_8BIT BIT(3) 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci#define nandc_set_read_loc(nandc, reg, offset, size, is_last) \ 1838c2ecf20Sopenharmony_cinandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \ 1848c2ecf20Sopenharmony_ci ((offset) << READ_LOCATION_OFFSET) | \ 1858c2ecf20Sopenharmony_ci ((size) << READ_LOCATION_SIZE) | \ 1868c2ecf20Sopenharmony_ci ((is_last) << READ_LOCATION_LAST)) 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* 1898c2ecf20Sopenharmony_ci * Returns the actual register address for all NAND_DEV_ registers 1908c2ecf20Sopenharmony_ci * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD) 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg)) 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* Returns the NAND register physical address */ 1958c2ecf20Sopenharmony_ci#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset)) 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* Returns the dma address for reg read buffer */ 1988c2ecf20Sopenharmony_ci#define reg_buf_dma_addr(chip, vaddr) \ 1998c2ecf20Sopenharmony_ci ((chip)->reg_read_dma + \ 2008c2ecf20Sopenharmony_ci ((uint8_t *)(vaddr) - (uint8_t *)(chip)->reg_read_buf)) 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci#define QPIC_PER_CW_CMD_ELEMENTS 32 2038c2ecf20Sopenharmony_ci#define QPIC_PER_CW_CMD_SGL 32 2048c2ecf20Sopenharmony_ci#define QPIC_PER_CW_DATA_SGL 8 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci#define QPIC_NAND_COMPLETION_TIMEOUT msecs_to_jiffies(2000) 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/* 2098c2ecf20Sopenharmony_ci * Flags used in DMA descriptor preparation helper functions 2108c2ecf20Sopenharmony_ci * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma) 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci/* Don't set the EOT in current tx BAM sgl */ 2138c2ecf20Sopenharmony_ci#define NAND_BAM_NO_EOT BIT(0) 2148c2ecf20Sopenharmony_ci/* Set the NWD flag in current BAM sgl */ 2158c2ecf20Sopenharmony_ci#define NAND_BAM_NWD BIT(1) 2168c2ecf20Sopenharmony_ci/* Finish writing in the current BAM sgl and start writing in another BAM sgl */ 2178c2ecf20Sopenharmony_ci#define NAND_BAM_NEXT_SGL BIT(2) 2188c2ecf20Sopenharmony_ci/* 2198c2ecf20Sopenharmony_ci * Erased codeword status is being used two times in single transfer so this 2208c2ecf20Sopenharmony_ci * flag will determine the current value of erased codeword status register 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_ci#define NAND_ERASED_CW_SET BIT(4) 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/* 2258c2ecf20Sopenharmony_ci * This data type corresponds to the BAM transaction which will be used for all 2268c2ecf20Sopenharmony_ci * NAND transfers. 2278c2ecf20Sopenharmony_ci * @bam_ce - the array of BAM command elements 2288c2ecf20Sopenharmony_ci * @cmd_sgl - sgl for NAND BAM command pipe 2298c2ecf20Sopenharmony_ci * @data_sgl - sgl for NAND BAM consumer/producer pipe 2308c2ecf20Sopenharmony_ci * @bam_ce_pos - the index in bam_ce which is available for next sgl 2318c2ecf20Sopenharmony_ci * @bam_ce_start - the index in bam_ce which marks the start position ce 2328c2ecf20Sopenharmony_ci * for current sgl. It will be used for size calculation 2338c2ecf20Sopenharmony_ci * for current sgl 2348c2ecf20Sopenharmony_ci * @cmd_sgl_pos - current index in command sgl. 2358c2ecf20Sopenharmony_ci * @cmd_sgl_start - start index in command sgl. 2368c2ecf20Sopenharmony_ci * @tx_sgl_pos - current index in data sgl for tx. 2378c2ecf20Sopenharmony_ci * @tx_sgl_start - start index in data sgl for tx. 2388c2ecf20Sopenharmony_ci * @rx_sgl_pos - current index in data sgl for rx. 2398c2ecf20Sopenharmony_ci * @rx_sgl_start - start index in data sgl for rx. 2408c2ecf20Sopenharmony_ci * @wait_second_completion - wait for second DMA desc completion before making 2418c2ecf20Sopenharmony_ci * the NAND transfer completion. 2428c2ecf20Sopenharmony_ci * @txn_done - completion for NAND transfer. 2438c2ecf20Sopenharmony_ci * @last_data_desc - last DMA desc in data channel (tx/rx). 2448c2ecf20Sopenharmony_ci * @last_cmd_desc - last DMA desc in command channel. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_cistruct bam_transaction { 2478c2ecf20Sopenharmony_ci struct bam_cmd_element *bam_ce; 2488c2ecf20Sopenharmony_ci struct scatterlist *cmd_sgl; 2498c2ecf20Sopenharmony_ci struct scatterlist *data_sgl; 2508c2ecf20Sopenharmony_ci u32 bam_ce_pos; 2518c2ecf20Sopenharmony_ci u32 bam_ce_start; 2528c2ecf20Sopenharmony_ci u32 cmd_sgl_pos; 2538c2ecf20Sopenharmony_ci u32 cmd_sgl_start; 2548c2ecf20Sopenharmony_ci u32 tx_sgl_pos; 2558c2ecf20Sopenharmony_ci u32 tx_sgl_start; 2568c2ecf20Sopenharmony_ci u32 rx_sgl_pos; 2578c2ecf20Sopenharmony_ci u32 rx_sgl_start; 2588c2ecf20Sopenharmony_ci bool wait_second_completion; 2598c2ecf20Sopenharmony_ci struct completion txn_done; 2608c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *last_data_desc; 2618c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *last_cmd_desc; 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci/* 2658c2ecf20Sopenharmony_ci * This data type corresponds to the nand dma descriptor 2668c2ecf20Sopenharmony_ci * @list - list for desc_info 2678c2ecf20Sopenharmony_ci * @dir - DMA transfer direction 2688c2ecf20Sopenharmony_ci * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by 2698c2ecf20Sopenharmony_ci * ADM 2708c2ecf20Sopenharmony_ci * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM 2718c2ecf20Sopenharmony_ci * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM 2728c2ecf20Sopenharmony_ci * @dma_desc - low level DMA engine descriptor 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_cistruct desc_info { 2758c2ecf20Sopenharmony_ci struct list_head node; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci enum dma_data_direction dir; 2788c2ecf20Sopenharmony_ci union { 2798c2ecf20Sopenharmony_ci struct scatterlist adm_sgl; 2808c2ecf20Sopenharmony_ci struct { 2818c2ecf20Sopenharmony_ci struct scatterlist *bam_sgl; 2828c2ecf20Sopenharmony_ci int sgl_cnt; 2838c2ecf20Sopenharmony_ci }; 2848c2ecf20Sopenharmony_ci }; 2858c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *dma_desc; 2868c2ecf20Sopenharmony_ci}; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/* 2898c2ecf20Sopenharmony_ci * holds the current register values that we want to write. acts as a contiguous 2908c2ecf20Sopenharmony_ci * chunk of memory which we use to write the controller registers through DMA. 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_cistruct nandc_regs { 2938c2ecf20Sopenharmony_ci __le32 cmd; 2948c2ecf20Sopenharmony_ci __le32 addr0; 2958c2ecf20Sopenharmony_ci __le32 addr1; 2968c2ecf20Sopenharmony_ci __le32 chip_sel; 2978c2ecf20Sopenharmony_ci __le32 exec; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci __le32 cfg0; 3008c2ecf20Sopenharmony_ci __le32 cfg1; 3018c2ecf20Sopenharmony_ci __le32 ecc_bch_cfg; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci __le32 clrflashstatus; 3048c2ecf20Sopenharmony_ci __le32 clrreadstatus; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci __le32 cmd1; 3078c2ecf20Sopenharmony_ci __le32 vld; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci __le32 orig_cmd1; 3108c2ecf20Sopenharmony_ci __le32 orig_vld; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci __le32 ecc_buf_cfg; 3138c2ecf20Sopenharmony_ci __le32 read_location0; 3148c2ecf20Sopenharmony_ci __le32 read_location1; 3158c2ecf20Sopenharmony_ci __le32 read_location2; 3168c2ecf20Sopenharmony_ci __le32 read_location3; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci __le32 erased_cw_detect_cfg_clr; 3198c2ecf20Sopenharmony_ci __le32 erased_cw_detect_cfg_set; 3208c2ecf20Sopenharmony_ci}; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci/* 3238c2ecf20Sopenharmony_ci * NAND controller data struct 3248c2ecf20Sopenharmony_ci * 3258c2ecf20Sopenharmony_ci * @controller: base controller structure 3268c2ecf20Sopenharmony_ci * @host_list: list containing all the chips attached to the 3278c2ecf20Sopenharmony_ci * controller 3288c2ecf20Sopenharmony_ci * @dev: parent device 3298c2ecf20Sopenharmony_ci * @base: MMIO base 3308c2ecf20Sopenharmony_ci * @base_phys: physical base address of controller registers 3318c2ecf20Sopenharmony_ci * @base_dma: dma base address of controller registers 3328c2ecf20Sopenharmony_ci * @core_clk: controller clock 3338c2ecf20Sopenharmony_ci * @aon_clk: another controller clock 3348c2ecf20Sopenharmony_ci * 3358c2ecf20Sopenharmony_ci * @chan: dma channel 3368c2ecf20Sopenharmony_ci * @cmd_crci: ADM DMA CRCI for command flow control 3378c2ecf20Sopenharmony_ci * @data_crci: ADM DMA CRCI for data flow control 3388c2ecf20Sopenharmony_ci * @desc_list: DMA descriptor list (list of desc_infos) 3398c2ecf20Sopenharmony_ci * 3408c2ecf20Sopenharmony_ci * @data_buffer: our local DMA buffer for page read/writes, 3418c2ecf20Sopenharmony_ci * used when we can't use the buffer provided 3428c2ecf20Sopenharmony_ci * by upper layers directly 3438c2ecf20Sopenharmony_ci * @buf_size/count/start: markers for chip->legacy.read_buf/write_buf 3448c2ecf20Sopenharmony_ci * functions 3458c2ecf20Sopenharmony_ci * @reg_read_buf: local buffer for reading back registers via DMA 3468c2ecf20Sopenharmony_ci * @reg_read_dma: contains dma address for register read buffer 3478c2ecf20Sopenharmony_ci * @reg_read_pos: marker for data read in reg_read_buf 3488c2ecf20Sopenharmony_ci * 3498c2ecf20Sopenharmony_ci * @regs: a contiguous chunk of memory for DMA register 3508c2ecf20Sopenharmony_ci * writes. contains the register values to be 3518c2ecf20Sopenharmony_ci * written to controller 3528c2ecf20Sopenharmony_ci * @cmd1/vld: some fixed controller register values 3538c2ecf20Sopenharmony_ci * @props: properties of current NAND controller, 3548c2ecf20Sopenharmony_ci * initialized via DT match data 3558c2ecf20Sopenharmony_ci * @max_cwperpage: maximum QPIC codewords required. calculated 3568c2ecf20Sopenharmony_ci * from all connected NAND devices pagesize 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_cistruct qcom_nand_controller { 3598c2ecf20Sopenharmony_ci struct nand_controller controller; 3608c2ecf20Sopenharmony_ci struct list_head host_list; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci struct device *dev; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci void __iomem *base; 3658c2ecf20Sopenharmony_ci phys_addr_t base_phys; 3668c2ecf20Sopenharmony_ci dma_addr_t base_dma; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci struct clk *core_clk; 3698c2ecf20Sopenharmony_ci struct clk *aon_clk; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci union { 3728c2ecf20Sopenharmony_ci /* will be used only by QPIC for BAM DMA */ 3738c2ecf20Sopenharmony_ci struct { 3748c2ecf20Sopenharmony_ci struct dma_chan *tx_chan; 3758c2ecf20Sopenharmony_ci struct dma_chan *rx_chan; 3768c2ecf20Sopenharmony_ci struct dma_chan *cmd_chan; 3778c2ecf20Sopenharmony_ci }; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* will be used only by EBI2 for ADM DMA */ 3808c2ecf20Sopenharmony_ci struct { 3818c2ecf20Sopenharmony_ci struct dma_chan *chan; 3828c2ecf20Sopenharmony_ci unsigned int cmd_crci; 3838c2ecf20Sopenharmony_ci unsigned int data_crci; 3848c2ecf20Sopenharmony_ci }; 3858c2ecf20Sopenharmony_ci }; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci struct list_head desc_list; 3888c2ecf20Sopenharmony_ci struct bam_transaction *bam_txn; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci u8 *data_buffer; 3918c2ecf20Sopenharmony_ci int buf_size; 3928c2ecf20Sopenharmony_ci int buf_count; 3938c2ecf20Sopenharmony_ci int buf_start; 3948c2ecf20Sopenharmony_ci unsigned int max_cwperpage; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci __le32 *reg_read_buf; 3978c2ecf20Sopenharmony_ci dma_addr_t reg_read_dma; 3988c2ecf20Sopenharmony_ci int reg_read_pos; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci struct nandc_regs *regs; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci u32 cmd1, vld; 4038c2ecf20Sopenharmony_ci const struct qcom_nandc_props *props; 4048c2ecf20Sopenharmony_ci}; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci/* 4078c2ecf20Sopenharmony_ci * NAND chip structure 4088c2ecf20Sopenharmony_ci * 4098c2ecf20Sopenharmony_ci * @chip: base NAND chip structure 4108c2ecf20Sopenharmony_ci * @node: list node to add itself to host_list in 4118c2ecf20Sopenharmony_ci * qcom_nand_controller 4128c2ecf20Sopenharmony_ci * 4138c2ecf20Sopenharmony_ci * @cs: chip select value for this chip 4148c2ecf20Sopenharmony_ci * @cw_size: the number of bytes in a single step/codeword 4158c2ecf20Sopenharmony_ci * of a page, consisting of all data, ecc, spare 4168c2ecf20Sopenharmony_ci * and reserved bytes 4178c2ecf20Sopenharmony_ci * @cw_data: the number of bytes within a codeword protected 4188c2ecf20Sopenharmony_ci * by ECC 4198c2ecf20Sopenharmony_ci * @use_ecc: request the controller to use ECC for the 4208c2ecf20Sopenharmony_ci * upcoming read/write 4218c2ecf20Sopenharmony_ci * @bch_enabled: flag to tell whether BCH ECC mode is used 4228c2ecf20Sopenharmony_ci * @ecc_bytes_hw: ECC bytes used by controller hardware for this 4238c2ecf20Sopenharmony_ci * chip 4248c2ecf20Sopenharmony_ci * @status: value to be returned if NAND_CMD_STATUS command 4258c2ecf20Sopenharmony_ci * is executed 4268c2ecf20Sopenharmony_ci * @last_command: keeps track of last command on this chip. used 4278c2ecf20Sopenharmony_ci * for reading correct status 4288c2ecf20Sopenharmony_ci * 4298c2ecf20Sopenharmony_ci * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for 4308c2ecf20Sopenharmony_ci * ecc/non-ecc mode for the current nand flash 4318c2ecf20Sopenharmony_ci * device 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_cistruct qcom_nand_host { 4348c2ecf20Sopenharmony_ci struct nand_chip chip; 4358c2ecf20Sopenharmony_ci struct list_head node; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci int cs; 4388c2ecf20Sopenharmony_ci int cw_size; 4398c2ecf20Sopenharmony_ci int cw_data; 4408c2ecf20Sopenharmony_ci bool use_ecc; 4418c2ecf20Sopenharmony_ci bool bch_enabled; 4428c2ecf20Sopenharmony_ci int ecc_bytes_hw; 4438c2ecf20Sopenharmony_ci int spare_bytes; 4448c2ecf20Sopenharmony_ci int bbm_size; 4458c2ecf20Sopenharmony_ci u8 status; 4468c2ecf20Sopenharmony_ci int last_command; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci u32 cfg0, cfg1; 4498c2ecf20Sopenharmony_ci u32 cfg0_raw, cfg1_raw; 4508c2ecf20Sopenharmony_ci u32 ecc_buf_cfg; 4518c2ecf20Sopenharmony_ci u32 ecc_bch_cfg; 4528c2ecf20Sopenharmony_ci u32 clrflashstatus; 4538c2ecf20Sopenharmony_ci u32 clrreadstatus; 4548c2ecf20Sopenharmony_ci}; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci/* 4578c2ecf20Sopenharmony_ci * This data type corresponds to the NAND controller properties which varies 4588c2ecf20Sopenharmony_ci * among different NAND controllers. 4598c2ecf20Sopenharmony_ci * @ecc_modes - ecc mode for NAND 4608c2ecf20Sopenharmony_ci * @is_bam - whether NAND controller is using BAM 4618c2ecf20Sopenharmony_ci * @is_qpic - whether NAND CTRL is part of qpic IP 4628c2ecf20Sopenharmony_ci * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset 4638c2ecf20Sopenharmony_ci */ 4648c2ecf20Sopenharmony_cistruct qcom_nandc_props { 4658c2ecf20Sopenharmony_ci u32 ecc_modes; 4668c2ecf20Sopenharmony_ci bool is_bam; 4678c2ecf20Sopenharmony_ci bool is_qpic; 4688c2ecf20Sopenharmony_ci u32 dev_cmd_reg_start; 4698c2ecf20Sopenharmony_ci}; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci/* Frees the BAM transaction memory */ 4728c2ecf20Sopenharmony_cistatic void free_bam_transaction(struct qcom_nand_controller *nandc) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct bam_transaction *bam_txn = nandc->bam_txn; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci devm_kfree(nandc->dev, bam_txn); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci/* Allocates and Initializes the BAM transaction */ 4808c2ecf20Sopenharmony_cistatic struct bam_transaction * 4818c2ecf20Sopenharmony_cialloc_bam_transaction(struct qcom_nand_controller *nandc) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci struct bam_transaction *bam_txn; 4848c2ecf20Sopenharmony_ci size_t bam_txn_size; 4858c2ecf20Sopenharmony_ci unsigned int num_cw = nandc->max_cwperpage; 4868c2ecf20Sopenharmony_ci void *bam_txn_buf; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci bam_txn_size = 4898c2ecf20Sopenharmony_ci sizeof(*bam_txn) + num_cw * 4908c2ecf20Sopenharmony_ci ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) + 4918c2ecf20Sopenharmony_ci (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) + 4928c2ecf20Sopenharmony_ci (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL)); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL); 4958c2ecf20Sopenharmony_ci if (!bam_txn_buf) 4968c2ecf20Sopenharmony_ci return NULL; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci bam_txn = bam_txn_buf; 4998c2ecf20Sopenharmony_ci bam_txn_buf += sizeof(*bam_txn); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci bam_txn->bam_ce = bam_txn_buf; 5028c2ecf20Sopenharmony_ci bam_txn_buf += 5038c2ecf20Sopenharmony_ci sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci bam_txn->cmd_sgl = bam_txn_buf; 5068c2ecf20Sopenharmony_ci bam_txn_buf += 5078c2ecf20Sopenharmony_ci sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci bam_txn->data_sgl = bam_txn_buf; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci init_completion(&bam_txn->txn_done); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci return bam_txn; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci/* Clears the BAM transaction indexes */ 5178c2ecf20Sopenharmony_cistatic void clear_bam_transaction(struct qcom_nand_controller *nandc) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct bam_transaction *bam_txn = nandc->bam_txn; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (!nandc->props->is_bam) 5228c2ecf20Sopenharmony_ci return; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci bam_txn->bam_ce_pos = 0; 5258c2ecf20Sopenharmony_ci bam_txn->bam_ce_start = 0; 5268c2ecf20Sopenharmony_ci bam_txn->cmd_sgl_pos = 0; 5278c2ecf20Sopenharmony_ci bam_txn->cmd_sgl_start = 0; 5288c2ecf20Sopenharmony_ci bam_txn->tx_sgl_pos = 0; 5298c2ecf20Sopenharmony_ci bam_txn->tx_sgl_start = 0; 5308c2ecf20Sopenharmony_ci bam_txn->rx_sgl_pos = 0; 5318c2ecf20Sopenharmony_ci bam_txn->rx_sgl_start = 0; 5328c2ecf20Sopenharmony_ci bam_txn->last_data_desc = NULL; 5338c2ecf20Sopenharmony_ci bam_txn->wait_second_completion = false; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * 5368c2ecf20Sopenharmony_ci QPIC_PER_CW_CMD_SGL); 5378c2ecf20Sopenharmony_ci sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage * 5388c2ecf20Sopenharmony_ci QPIC_PER_CW_DATA_SGL); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci reinit_completion(&bam_txn->txn_done); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci/* Callback for DMA descriptor completion */ 5448c2ecf20Sopenharmony_cistatic void qpic_bam_dma_done(void *data) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci struct bam_transaction *bam_txn = data; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* 5498c2ecf20Sopenharmony_ci * In case of data transfer with NAND, 2 callbacks will be generated. 5508c2ecf20Sopenharmony_ci * One for command channel and another one for data channel. 5518c2ecf20Sopenharmony_ci * If current transaction has data descriptors 5528c2ecf20Sopenharmony_ci * (i.e. wait_second_completion is true), then set this to false 5538c2ecf20Sopenharmony_ci * and wait for second DMA descriptor completion. 5548c2ecf20Sopenharmony_ci */ 5558c2ecf20Sopenharmony_ci if (bam_txn->wait_second_completion) 5568c2ecf20Sopenharmony_ci bam_txn->wait_second_completion = false; 5578c2ecf20Sopenharmony_ci else 5588c2ecf20Sopenharmony_ci complete(&bam_txn->txn_done); 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci return container_of(chip, struct qcom_nand_host, chip); 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic inline struct qcom_nand_controller * 5678c2ecf20Sopenharmony_ciget_qcom_nand_controller(struct nand_chip *chip) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci return container_of(chip->controller, struct qcom_nand_controller, 5708c2ecf20Sopenharmony_ci controller); 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic inline u32 nandc_read(struct qcom_nand_controller *nandc, int offset) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci return ioread32(nandc->base + offset); 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic inline void nandc_write(struct qcom_nand_controller *nandc, int offset, 5798c2ecf20Sopenharmony_ci u32 val) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci iowrite32(val, nandc->base + offset); 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic inline void nandc_read_buffer_sync(struct qcom_nand_controller *nandc, 5858c2ecf20Sopenharmony_ci bool is_cpu) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci if (!nandc->props->is_bam) 5888c2ecf20Sopenharmony_ci return; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (is_cpu) 5918c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma, 5928c2ecf20Sopenharmony_ci MAX_REG_RD * 5938c2ecf20Sopenharmony_ci sizeof(*nandc->reg_read_buf), 5948c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 5958c2ecf20Sopenharmony_ci else 5968c2ecf20Sopenharmony_ci dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma, 5978c2ecf20Sopenharmony_ci MAX_REG_RD * 5988c2ecf20Sopenharmony_ci sizeof(*nandc->reg_read_buf), 5998c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci switch (offset) { 6058c2ecf20Sopenharmony_ci case NAND_FLASH_CMD: 6068c2ecf20Sopenharmony_ci return ®s->cmd; 6078c2ecf20Sopenharmony_ci case NAND_ADDR0: 6088c2ecf20Sopenharmony_ci return ®s->addr0; 6098c2ecf20Sopenharmony_ci case NAND_ADDR1: 6108c2ecf20Sopenharmony_ci return ®s->addr1; 6118c2ecf20Sopenharmony_ci case NAND_FLASH_CHIP_SELECT: 6128c2ecf20Sopenharmony_ci return ®s->chip_sel; 6138c2ecf20Sopenharmony_ci case NAND_EXEC_CMD: 6148c2ecf20Sopenharmony_ci return ®s->exec; 6158c2ecf20Sopenharmony_ci case NAND_FLASH_STATUS: 6168c2ecf20Sopenharmony_ci return ®s->clrflashstatus; 6178c2ecf20Sopenharmony_ci case NAND_DEV0_CFG0: 6188c2ecf20Sopenharmony_ci return ®s->cfg0; 6198c2ecf20Sopenharmony_ci case NAND_DEV0_CFG1: 6208c2ecf20Sopenharmony_ci return ®s->cfg1; 6218c2ecf20Sopenharmony_ci case NAND_DEV0_ECC_CFG: 6228c2ecf20Sopenharmony_ci return ®s->ecc_bch_cfg; 6238c2ecf20Sopenharmony_ci case NAND_READ_STATUS: 6248c2ecf20Sopenharmony_ci return ®s->clrreadstatus; 6258c2ecf20Sopenharmony_ci case NAND_DEV_CMD1: 6268c2ecf20Sopenharmony_ci return ®s->cmd1; 6278c2ecf20Sopenharmony_ci case NAND_DEV_CMD1_RESTORE: 6288c2ecf20Sopenharmony_ci return ®s->orig_cmd1; 6298c2ecf20Sopenharmony_ci case NAND_DEV_CMD_VLD: 6308c2ecf20Sopenharmony_ci return ®s->vld; 6318c2ecf20Sopenharmony_ci case NAND_DEV_CMD_VLD_RESTORE: 6328c2ecf20Sopenharmony_ci return ®s->orig_vld; 6338c2ecf20Sopenharmony_ci case NAND_EBI2_ECC_BUF_CFG: 6348c2ecf20Sopenharmony_ci return ®s->ecc_buf_cfg; 6358c2ecf20Sopenharmony_ci case NAND_READ_LOCATION_0: 6368c2ecf20Sopenharmony_ci return ®s->read_location0; 6378c2ecf20Sopenharmony_ci case NAND_READ_LOCATION_1: 6388c2ecf20Sopenharmony_ci return ®s->read_location1; 6398c2ecf20Sopenharmony_ci case NAND_READ_LOCATION_2: 6408c2ecf20Sopenharmony_ci return ®s->read_location2; 6418c2ecf20Sopenharmony_ci case NAND_READ_LOCATION_3: 6428c2ecf20Sopenharmony_ci return ®s->read_location3; 6438c2ecf20Sopenharmony_ci default: 6448c2ecf20Sopenharmony_ci return NULL; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic void nandc_set_reg(struct qcom_nand_controller *nandc, int offset, 6498c2ecf20Sopenharmony_ci u32 val) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci struct nandc_regs *regs = nandc->regs; 6528c2ecf20Sopenharmony_ci __le32 *reg; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci reg = offset_to_nandc_reg(regs, offset); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (reg) 6578c2ecf20Sopenharmony_ci *reg = cpu_to_le32(val); 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci/* helper to configure address register values */ 6618c2ecf20Sopenharmony_cistatic void set_address(struct qcom_nand_host *host, u16 column, int page) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->chip; 6648c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci if (chip->options & NAND_BUSWIDTH_16) 6678c2ecf20Sopenharmony_ci column >>= 1; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_ADDR0, page << 16 | column); 6708c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_ADDR1, page >> 16 & 0xff); 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci/* 6748c2ecf20Sopenharmony_ci * update_rw_regs: set up read/write register values, these will be 6758c2ecf20Sopenharmony_ci * written to the NAND controller registers via DMA 6768c2ecf20Sopenharmony_ci * 6778c2ecf20Sopenharmony_ci * @num_cw: number of steps for the read/write operation 6788c2ecf20Sopenharmony_ci * @read: read or write operation 6798c2ecf20Sopenharmony_ci */ 6808c2ecf20Sopenharmony_cistatic void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->chip; 6838c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 6848c2ecf20Sopenharmony_ci u32 cmd, cfg0, cfg1, ecc_bch_cfg; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (read) { 6878c2ecf20Sopenharmony_ci if (host->use_ecc) 6888c2ecf20Sopenharmony_ci cmd = OP_PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE; 6898c2ecf20Sopenharmony_ci else 6908c2ecf20Sopenharmony_ci cmd = OP_PAGE_READ | PAGE_ACC | LAST_PAGE; 6918c2ecf20Sopenharmony_ci } else { 6928c2ecf20Sopenharmony_ci cmd = OP_PROGRAM_PAGE | PAGE_ACC | LAST_PAGE; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci if (host->use_ecc) { 6968c2ecf20Sopenharmony_ci cfg0 = (host->cfg0 & ~(7U << CW_PER_PAGE)) | 6978c2ecf20Sopenharmony_ci (num_cw - 1) << CW_PER_PAGE; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci cfg1 = host->cfg1; 7008c2ecf20Sopenharmony_ci ecc_bch_cfg = host->ecc_bch_cfg; 7018c2ecf20Sopenharmony_ci } else { 7028c2ecf20Sopenharmony_ci cfg0 = (host->cfg0_raw & ~(7U << CW_PER_PAGE)) | 7038c2ecf20Sopenharmony_ci (num_cw - 1) << CW_PER_PAGE; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci cfg1 = host->cfg1_raw; 7068c2ecf20Sopenharmony_ci ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_FLASH_CMD, cmd); 7108c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_DEV0_CFG0, cfg0); 7118c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_DEV0_CFG1, cfg1); 7128c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_DEV0_ECC_CFG, ecc_bch_cfg); 7138c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg); 7148c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); 7158c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); 7168c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_EXEC_CMD, 1); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci if (read) 7198c2ecf20Sopenharmony_ci nandc_set_read_loc(nandc, 0, 0, host->use_ecc ? 7208c2ecf20Sopenharmony_ci host->cw_data : host->cw_size, 1); 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci/* 7248c2ecf20Sopenharmony_ci * Maps the scatter gather list for DMA transfer and forms the DMA descriptor 7258c2ecf20Sopenharmony_ci * for BAM. This descriptor will be added in the NAND DMA descriptor queue 7268c2ecf20Sopenharmony_ci * which will be submitted to DMA engine. 7278c2ecf20Sopenharmony_ci */ 7288c2ecf20Sopenharmony_cistatic int prepare_bam_async_desc(struct qcom_nand_controller *nandc, 7298c2ecf20Sopenharmony_ci struct dma_chan *chan, 7308c2ecf20Sopenharmony_ci unsigned long flags) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci struct desc_info *desc; 7338c2ecf20Sopenharmony_ci struct scatterlist *sgl; 7348c2ecf20Sopenharmony_ci unsigned int sgl_cnt; 7358c2ecf20Sopenharmony_ci int ret; 7368c2ecf20Sopenharmony_ci struct bam_transaction *bam_txn = nandc->bam_txn; 7378c2ecf20Sopenharmony_ci enum dma_transfer_direction dir_eng; 7388c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *dma_desc; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci desc = kzalloc(sizeof(*desc), GFP_KERNEL); 7418c2ecf20Sopenharmony_ci if (!desc) 7428c2ecf20Sopenharmony_ci return -ENOMEM; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (chan == nandc->cmd_chan) { 7458c2ecf20Sopenharmony_ci sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start]; 7468c2ecf20Sopenharmony_ci sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start; 7478c2ecf20Sopenharmony_ci bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos; 7488c2ecf20Sopenharmony_ci dir_eng = DMA_MEM_TO_DEV; 7498c2ecf20Sopenharmony_ci desc->dir = DMA_TO_DEVICE; 7508c2ecf20Sopenharmony_ci } else if (chan == nandc->tx_chan) { 7518c2ecf20Sopenharmony_ci sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start]; 7528c2ecf20Sopenharmony_ci sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start; 7538c2ecf20Sopenharmony_ci bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos; 7548c2ecf20Sopenharmony_ci dir_eng = DMA_MEM_TO_DEV; 7558c2ecf20Sopenharmony_ci desc->dir = DMA_TO_DEVICE; 7568c2ecf20Sopenharmony_ci } else { 7578c2ecf20Sopenharmony_ci sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start]; 7588c2ecf20Sopenharmony_ci sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start; 7598c2ecf20Sopenharmony_ci bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos; 7608c2ecf20Sopenharmony_ci dir_eng = DMA_DEV_TO_MEM; 7618c2ecf20Sopenharmony_ci desc->dir = DMA_FROM_DEVICE; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci sg_mark_end(sgl + sgl_cnt - 1); 7658c2ecf20Sopenharmony_ci ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir); 7668c2ecf20Sopenharmony_ci if (ret == 0) { 7678c2ecf20Sopenharmony_ci dev_err(nandc->dev, "failure in mapping desc\n"); 7688c2ecf20Sopenharmony_ci kfree(desc); 7698c2ecf20Sopenharmony_ci return -ENOMEM; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci desc->sgl_cnt = sgl_cnt; 7738c2ecf20Sopenharmony_ci desc->bam_sgl = sgl; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng, 7768c2ecf20Sopenharmony_ci flags); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if (!dma_desc) { 7798c2ecf20Sopenharmony_ci dev_err(nandc->dev, "failure in prep desc\n"); 7808c2ecf20Sopenharmony_ci dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir); 7818c2ecf20Sopenharmony_ci kfree(desc); 7828c2ecf20Sopenharmony_ci return -EINVAL; 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci desc->dma_desc = dma_desc; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* update last data/command descriptor */ 7888c2ecf20Sopenharmony_ci if (chan == nandc->cmd_chan) 7898c2ecf20Sopenharmony_ci bam_txn->last_cmd_desc = dma_desc; 7908c2ecf20Sopenharmony_ci else 7918c2ecf20Sopenharmony_ci bam_txn->last_data_desc = dma_desc; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci list_add_tail(&desc->node, &nandc->desc_list); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci return 0; 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci/* 7998c2ecf20Sopenharmony_ci * Prepares the command descriptor for BAM DMA which will be used for NAND 8008c2ecf20Sopenharmony_ci * register reads and writes. The command descriptor requires the command 8018c2ecf20Sopenharmony_ci * to be formed in command element type so this function uses the command 8028c2ecf20Sopenharmony_ci * element from bam transaction ce array and fills the same with required 8038c2ecf20Sopenharmony_ci * data. A single SGL can contain multiple command elements so 8048c2ecf20Sopenharmony_ci * NAND_BAM_NEXT_SGL will be used for starting the separate SGL 8058c2ecf20Sopenharmony_ci * after the current command element. 8068c2ecf20Sopenharmony_ci */ 8078c2ecf20Sopenharmony_cistatic int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, 8088c2ecf20Sopenharmony_ci int reg_off, const void *vaddr, 8098c2ecf20Sopenharmony_ci int size, unsigned int flags) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci int bam_ce_size; 8128c2ecf20Sopenharmony_ci int i, ret; 8138c2ecf20Sopenharmony_ci struct bam_cmd_element *bam_ce_buffer; 8148c2ecf20Sopenharmony_ci struct bam_transaction *bam_txn = nandc->bam_txn; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos]; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* fill the command desc */ 8198c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 8208c2ecf20Sopenharmony_ci if (read) 8218c2ecf20Sopenharmony_ci bam_prep_ce(&bam_ce_buffer[i], 8228c2ecf20Sopenharmony_ci nandc_reg_phys(nandc, reg_off + 4 * i), 8238c2ecf20Sopenharmony_ci BAM_READ_COMMAND, 8248c2ecf20Sopenharmony_ci reg_buf_dma_addr(nandc, 8258c2ecf20Sopenharmony_ci (__le32 *)vaddr + i)); 8268c2ecf20Sopenharmony_ci else 8278c2ecf20Sopenharmony_ci bam_prep_ce_le32(&bam_ce_buffer[i], 8288c2ecf20Sopenharmony_ci nandc_reg_phys(nandc, reg_off + 4 * i), 8298c2ecf20Sopenharmony_ci BAM_WRITE_COMMAND, 8308c2ecf20Sopenharmony_ci *((__le32 *)vaddr + i)); 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci bam_txn->bam_ce_pos += size; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci /* use the separate sgl after this command */ 8368c2ecf20Sopenharmony_ci if (flags & NAND_BAM_NEXT_SGL) { 8378c2ecf20Sopenharmony_ci bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start]; 8388c2ecf20Sopenharmony_ci bam_ce_size = (bam_txn->bam_ce_pos - 8398c2ecf20Sopenharmony_ci bam_txn->bam_ce_start) * 8408c2ecf20Sopenharmony_ci sizeof(struct bam_cmd_element); 8418c2ecf20Sopenharmony_ci sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos], 8428c2ecf20Sopenharmony_ci bam_ce_buffer, bam_ce_size); 8438c2ecf20Sopenharmony_ci bam_txn->cmd_sgl_pos++; 8448c2ecf20Sopenharmony_ci bam_txn->bam_ce_start = bam_txn->bam_ce_pos; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (flags & NAND_BAM_NWD) { 8478c2ecf20Sopenharmony_ci ret = prepare_bam_async_desc(nandc, nandc->cmd_chan, 8488c2ecf20Sopenharmony_ci DMA_PREP_FENCE | 8498c2ecf20Sopenharmony_ci DMA_PREP_CMD); 8508c2ecf20Sopenharmony_ci if (ret) 8518c2ecf20Sopenharmony_ci return ret; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci return 0; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci/* 8598c2ecf20Sopenharmony_ci * Prepares the data descriptor for BAM DMA which will be used for NAND 8608c2ecf20Sopenharmony_ci * data reads and writes. 8618c2ecf20Sopenharmony_ci */ 8628c2ecf20Sopenharmony_cistatic int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, 8638c2ecf20Sopenharmony_ci const void *vaddr, 8648c2ecf20Sopenharmony_ci int size, unsigned int flags) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci int ret; 8678c2ecf20Sopenharmony_ci struct bam_transaction *bam_txn = nandc->bam_txn; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (read) { 8708c2ecf20Sopenharmony_ci sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos], 8718c2ecf20Sopenharmony_ci vaddr, size); 8728c2ecf20Sopenharmony_ci bam_txn->rx_sgl_pos++; 8738c2ecf20Sopenharmony_ci } else { 8748c2ecf20Sopenharmony_ci sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos], 8758c2ecf20Sopenharmony_ci vaddr, size); 8768c2ecf20Sopenharmony_ci bam_txn->tx_sgl_pos++; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci /* 8798c2ecf20Sopenharmony_ci * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag 8808c2ecf20Sopenharmony_ci * is not set, form the DMA descriptor 8818c2ecf20Sopenharmony_ci */ 8828c2ecf20Sopenharmony_ci if (!(flags & NAND_BAM_NO_EOT)) { 8838c2ecf20Sopenharmony_ci ret = prepare_bam_async_desc(nandc, nandc->tx_chan, 8848c2ecf20Sopenharmony_ci DMA_PREP_INTERRUPT); 8858c2ecf20Sopenharmony_ci if (ret) 8868c2ecf20Sopenharmony_ci return ret; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci return 0; 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cistatic int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, 8948c2ecf20Sopenharmony_ci int reg_off, const void *vaddr, int size, 8958c2ecf20Sopenharmony_ci bool flow_control) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci struct desc_info *desc; 8988c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *dma_desc; 8998c2ecf20Sopenharmony_ci struct scatterlist *sgl; 9008c2ecf20Sopenharmony_ci struct dma_slave_config slave_conf; 9018c2ecf20Sopenharmony_ci enum dma_transfer_direction dir_eng; 9028c2ecf20Sopenharmony_ci int ret; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci desc = kzalloc(sizeof(*desc), GFP_KERNEL); 9058c2ecf20Sopenharmony_ci if (!desc) 9068c2ecf20Sopenharmony_ci return -ENOMEM; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci sgl = &desc->adm_sgl; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci sg_init_one(sgl, vaddr, size); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (read) { 9138c2ecf20Sopenharmony_ci dir_eng = DMA_DEV_TO_MEM; 9148c2ecf20Sopenharmony_ci desc->dir = DMA_FROM_DEVICE; 9158c2ecf20Sopenharmony_ci } else { 9168c2ecf20Sopenharmony_ci dir_eng = DMA_MEM_TO_DEV; 9178c2ecf20Sopenharmony_ci desc->dir = DMA_TO_DEVICE; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci ret = dma_map_sg(nandc->dev, sgl, 1, desc->dir); 9218c2ecf20Sopenharmony_ci if (ret == 0) { 9228c2ecf20Sopenharmony_ci ret = -ENOMEM; 9238c2ecf20Sopenharmony_ci goto err; 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci memset(&slave_conf, 0x00, sizeof(slave_conf)); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci slave_conf.device_fc = flow_control; 9298c2ecf20Sopenharmony_ci if (read) { 9308c2ecf20Sopenharmony_ci slave_conf.src_maxburst = 16; 9318c2ecf20Sopenharmony_ci slave_conf.src_addr = nandc->base_dma + reg_off; 9328c2ecf20Sopenharmony_ci slave_conf.slave_id = nandc->data_crci; 9338c2ecf20Sopenharmony_ci } else { 9348c2ecf20Sopenharmony_ci slave_conf.dst_maxburst = 16; 9358c2ecf20Sopenharmony_ci slave_conf.dst_addr = nandc->base_dma + reg_off; 9368c2ecf20Sopenharmony_ci slave_conf.slave_id = nandc->cmd_crci; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci ret = dmaengine_slave_config(nandc->chan, &slave_conf); 9408c2ecf20Sopenharmony_ci if (ret) { 9418c2ecf20Sopenharmony_ci dev_err(nandc->dev, "failed to configure dma channel\n"); 9428c2ecf20Sopenharmony_ci goto err; 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci dma_desc = dmaengine_prep_slave_sg(nandc->chan, sgl, 1, dir_eng, 0); 9468c2ecf20Sopenharmony_ci if (!dma_desc) { 9478c2ecf20Sopenharmony_ci dev_err(nandc->dev, "failed to prepare desc\n"); 9488c2ecf20Sopenharmony_ci ret = -EINVAL; 9498c2ecf20Sopenharmony_ci goto err; 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci desc->dma_desc = dma_desc; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci list_add_tail(&desc->node, &nandc->desc_list); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci return 0; 9578c2ecf20Sopenharmony_cierr: 9588c2ecf20Sopenharmony_ci kfree(desc); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci return ret; 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci/* 9648c2ecf20Sopenharmony_ci * read_reg_dma: prepares a descriptor to read a given number of 9658c2ecf20Sopenharmony_ci * contiguous registers to the reg_read_buf pointer 9668c2ecf20Sopenharmony_ci * 9678c2ecf20Sopenharmony_ci * @first: offset of the first register in the contiguous block 9688c2ecf20Sopenharmony_ci * @num_regs: number of registers to read 9698c2ecf20Sopenharmony_ci * @flags: flags to control DMA descriptor preparation 9708c2ecf20Sopenharmony_ci */ 9718c2ecf20Sopenharmony_cistatic int read_reg_dma(struct qcom_nand_controller *nandc, int first, 9728c2ecf20Sopenharmony_ci int num_regs, unsigned int flags) 9738c2ecf20Sopenharmony_ci{ 9748c2ecf20Sopenharmony_ci bool flow_control = false; 9758c2ecf20Sopenharmony_ci void *vaddr; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci vaddr = nandc->reg_read_buf + nandc->reg_read_pos; 9788c2ecf20Sopenharmony_ci nandc->reg_read_pos += num_regs; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1) 9818c2ecf20Sopenharmony_ci first = dev_cmd_reg_addr(nandc, first); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci if (nandc->props->is_bam) 9848c2ecf20Sopenharmony_ci return prep_bam_dma_desc_cmd(nandc, true, first, vaddr, 9858c2ecf20Sopenharmony_ci num_regs, flags); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) 9888c2ecf20Sopenharmony_ci flow_control = true; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci return prep_adm_dma_desc(nandc, true, first, vaddr, 9918c2ecf20Sopenharmony_ci num_regs * sizeof(u32), flow_control); 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci/* 9958c2ecf20Sopenharmony_ci * write_reg_dma: prepares a descriptor to write a given number of 9968c2ecf20Sopenharmony_ci * contiguous registers 9978c2ecf20Sopenharmony_ci * 9988c2ecf20Sopenharmony_ci * @first: offset of the first register in the contiguous block 9998c2ecf20Sopenharmony_ci * @num_regs: number of registers to write 10008c2ecf20Sopenharmony_ci * @flags: flags to control DMA descriptor preparation 10018c2ecf20Sopenharmony_ci */ 10028c2ecf20Sopenharmony_cistatic int write_reg_dma(struct qcom_nand_controller *nandc, int first, 10038c2ecf20Sopenharmony_ci int num_regs, unsigned int flags) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci bool flow_control = false; 10068c2ecf20Sopenharmony_ci struct nandc_regs *regs = nandc->regs; 10078c2ecf20Sopenharmony_ci void *vaddr; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci vaddr = offset_to_nandc_reg(regs, first); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci if (first == NAND_ERASED_CW_DETECT_CFG) { 10128c2ecf20Sopenharmony_ci if (flags & NAND_ERASED_CW_SET) 10138c2ecf20Sopenharmony_ci vaddr = ®s->erased_cw_detect_cfg_set; 10148c2ecf20Sopenharmony_ci else 10158c2ecf20Sopenharmony_ci vaddr = ®s->erased_cw_detect_cfg_clr; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci if (first == NAND_EXEC_CMD) 10198c2ecf20Sopenharmony_ci flags |= NAND_BAM_NWD; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1) 10228c2ecf20Sopenharmony_ci first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD) 10258c2ecf20Sopenharmony_ci first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci if (nandc->props->is_bam) 10288c2ecf20Sopenharmony_ci return prep_bam_dma_desc_cmd(nandc, false, first, vaddr, 10298c2ecf20Sopenharmony_ci num_regs, flags); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci if (first == NAND_FLASH_CMD) 10328c2ecf20Sopenharmony_ci flow_control = true; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci return prep_adm_dma_desc(nandc, false, first, vaddr, 10358c2ecf20Sopenharmony_ci num_regs * sizeof(u32), flow_control); 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci/* 10398c2ecf20Sopenharmony_ci * read_data_dma: prepares a DMA descriptor to transfer data from the 10408c2ecf20Sopenharmony_ci * controller's internal buffer to the buffer 'vaddr' 10418c2ecf20Sopenharmony_ci * 10428c2ecf20Sopenharmony_ci * @reg_off: offset within the controller's data buffer 10438c2ecf20Sopenharmony_ci * @vaddr: virtual address of the buffer we want to write to 10448c2ecf20Sopenharmony_ci * @size: DMA transaction size in bytes 10458c2ecf20Sopenharmony_ci * @flags: flags to control DMA descriptor preparation 10468c2ecf20Sopenharmony_ci */ 10478c2ecf20Sopenharmony_cistatic int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, 10488c2ecf20Sopenharmony_ci const u8 *vaddr, int size, unsigned int flags) 10498c2ecf20Sopenharmony_ci{ 10508c2ecf20Sopenharmony_ci if (nandc->props->is_bam) 10518c2ecf20Sopenharmony_ci return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci/* 10578c2ecf20Sopenharmony_ci * write_data_dma: prepares a DMA descriptor to transfer data from 10588c2ecf20Sopenharmony_ci * 'vaddr' to the controller's internal buffer 10598c2ecf20Sopenharmony_ci * 10608c2ecf20Sopenharmony_ci * @reg_off: offset within the controller's data buffer 10618c2ecf20Sopenharmony_ci * @vaddr: virtual address of the buffer we want to read from 10628c2ecf20Sopenharmony_ci * @size: DMA transaction size in bytes 10638c2ecf20Sopenharmony_ci * @flags: flags to control DMA descriptor preparation 10648c2ecf20Sopenharmony_ci */ 10658c2ecf20Sopenharmony_cistatic int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, 10668c2ecf20Sopenharmony_ci const u8 *vaddr, int size, unsigned int flags) 10678c2ecf20Sopenharmony_ci{ 10688c2ecf20Sopenharmony_ci if (nandc->props->is_bam) 10698c2ecf20Sopenharmony_ci return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); 10728c2ecf20Sopenharmony_ci} 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci/* 10758c2ecf20Sopenharmony_ci * Helper to prepare DMA descriptors for configuring registers 10768c2ecf20Sopenharmony_ci * before reading a NAND page. 10778c2ecf20Sopenharmony_ci */ 10788c2ecf20Sopenharmony_cistatic void config_nand_page_read(struct qcom_nand_controller *nandc) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_ADDR0, 2, 0); 10818c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); 10828c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0); 10838c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0); 10848c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 10858c2ecf20Sopenharmony_ci NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); 10868c2ecf20Sopenharmony_ci} 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci/* 10898c2ecf20Sopenharmony_ci * Helper to prepare DMA descriptors for configuring registers 10908c2ecf20Sopenharmony_ci * before reading each codeword in NAND page. 10918c2ecf20Sopenharmony_ci */ 10928c2ecf20Sopenharmony_cistatic void 10938c2ecf20Sopenharmony_ciconfig_nand_cw_read(struct qcom_nand_controller *nandc, bool use_ecc) 10948c2ecf20Sopenharmony_ci{ 10958c2ecf20Sopenharmony_ci if (nandc->props->is_bam) 10968c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_READ_LOCATION_0, 4, 10978c2ecf20Sopenharmony_ci NAND_BAM_NEXT_SGL); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); 11008c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (use_ecc) { 11038c2ecf20Sopenharmony_ci read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); 11048c2ecf20Sopenharmony_ci read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, 11058c2ecf20Sopenharmony_ci NAND_BAM_NEXT_SGL); 11068c2ecf20Sopenharmony_ci } else { 11078c2ecf20Sopenharmony_ci read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci} 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci/* 11128c2ecf20Sopenharmony_ci * Helper to prepare dma descriptors to configure registers needed for reading a 11138c2ecf20Sopenharmony_ci * single codeword in page 11148c2ecf20Sopenharmony_ci */ 11158c2ecf20Sopenharmony_cistatic void 11168c2ecf20Sopenharmony_ciconfig_nand_single_cw_page_read(struct qcom_nand_controller *nandc, 11178c2ecf20Sopenharmony_ci bool use_ecc) 11188c2ecf20Sopenharmony_ci{ 11198c2ecf20Sopenharmony_ci config_nand_page_read(nandc); 11208c2ecf20Sopenharmony_ci config_nand_cw_read(nandc, use_ecc); 11218c2ecf20Sopenharmony_ci} 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci/* 11248c2ecf20Sopenharmony_ci * Helper to prepare DMA descriptors used to configure registers needed for 11258c2ecf20Sopenharmony_ci * before writing a NAND page. 11268c2ecf20Sopenharmony_ci */ 11278c2ecf20Sopenharmony_cistatic void config_nand_page_write(struct qcom_nand_controller *nandc) 11288c2ecf20Sopenharmony_ci{ 11298c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_ADDR0, 2, 0); 11308c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); 11318c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 11328c2ecf20Sopenharmony_ci NAND_BAM_NEXT_SGL); 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci/* 11368c2ecf20Sopenharmony_ci * Helper to prepare DMA descriptors for configuring registers 11378c2ecf20Sopenharmony_ci * before writing each codeword in NAND page. 11388c2ecf20Sopenharmony_ci */ 11398c2ecf20Sopenharmony_cistatic void config_nand_cw_write(struct qcom_nand_controller *nandc) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); 11428c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); 11478c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci/* 11518c2ecf20Sopenharmony_ci * the following functions are used within chip->legacy.cmdfunc() to 11528c2ecf20Sopenharmony_ci * perform different NAND_CMD_* commands 11538c2ecf20Sopenharmony_ci */ 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci/* sets up descriptors for NAND_CMD_PARAM */ 11568c2ecf20Sopenharmony_cistatic int nandc_param(struct qcom_nand_host *host) 11578c2ecf20Sopenharmony_ci{ 11588c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->chip; 11598c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci /* 11628c2ecf20Sopenharmony_ci * NAND_CMD_PARAM is called before we know much about the FLASH chip 11638c2ecf20Sopenharmony_ci * in use. we configure the controller to perform a raw read of 512 11648c2ecf20Sopenharmony_ci * bytes to read onfi params 11658c2ecf20Sopenharmony_ci */ 11668c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_FLASH_CMD, OP_PAGE_READ | PAGE_ACC | LAST_PAGE); 11678c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_ADDR0, 0); 11688c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_ADDR1, 0); 11698c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_DEV0_CFG0, 0 << CW_PER_PAGE 11708c2ecf20Sopenharmony_ci | 512 << UD_SIZE_BYTES 11718c2ecf20Sopenharmony_ci | 5 << NUM_ADDR_CYCLES 11728c2ecf20Sopenharmony_ci | 0 << SPARE_SIZE_BYTES); 11738c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES 11748c2ecf20Sopenharmony_ci | 0 << CS_ACTIVE_BSY 11758c2ecf20Sopenharmony_ci | 17 << BAD_BLOCK_BYTE_NUM 11768c2ecf20Sopenharmony_ci | 1 << BAD_BLOCK_IN_SPARE_AREA 11778c2ecf20Sopenharmony_ci | 2 << WR_RD_BSY_GAP 11788c2ecf20Sopenharmony_ci | 0 << WIDE_FLASH 11798c2ecf20Sopenharmony_ci | 1 << DEV0_CFG1_ECC_DISABLE); 11808c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci /* configure CMD1 and VLD for ONFI param probing */ 11838c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_DEV_CMD_VLD, 11848c2ecf20Sopenharmony_ci (nandc->vld & ~READ_START_VLD)); 11858c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_DEV_CMD1, 11868c2ecf20Sopenharmony_ci (nandc->cmd1 & ~(0xFF << READ_ADDR)) 11878c2ecf20Sopenharmony_ci | NAND_CMD_PARAM << READ_ADDR); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_EXEC_CMD, 1); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1); 11928c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); 11938c2ecf20Sopenharmony_ci nandc_set_read_loc(nandc, 0, 0, 512, 1); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0); 11968c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci nandc->buf_count = 512; 11998c2ecf20Sopenharmony_ci memset(nandc->data_buffer, 0xff, nandc->buf_count); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci config_nand_single_cw_page_read(nandc, false); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, 12048c2ecf20Sopenharmony_ci nandc->buf_count, 0); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci /* restore CMD1 and VLD regs */ 12078c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0); 12088c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci return 0; 12118c2ecf20Sopenharmony_ci} 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci/* sets up descriptors for NAND_CMD_ERASE1 */ 12148c2ecf20Sopenharmony_cistatic int erase_block(struct qcom_nand_host *host, int page_addr) 12158c2ecf20Sopenharmony_ci{ 12168c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->chip; 12178c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_FLASH_CMD, 12208c2ecf20Sopenharmony_ci OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE); 12218c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_ADDR0, page_addr); 12228c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_ADDR1, 0); 12238c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_DEV0_CFG0, 12248c2ecf20Sopenharmony_ci host->cfg0_raw & ~(7 << CW_PER_PAGE)); 12258c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_DEV0_CFG1, host->cfg1_raw); 12268c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_EXEC_CMD, 1); 12278c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); 12288c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); 12318c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); 12328c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); 12378c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci return 0; 12408c2ecf20Sopenharmony_ci} 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci/* sets up descriptors for NAND_CMD_READID */ 12438c2ecf20Sopenharmony_cistatic int read_id(struct qcom_nand_host *host, int column) 12448c2ecf20Sopenharmony_ci{ 12458c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->chip; 12468c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci if (column == -1) 12498c2ecf20Sopenharmony_ci return 0; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_FLASH_CMD, OP_FETCH_ID); 12528c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_ADDR0, column); 12538c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_ADDR1, 0); 12548c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, 12558c2ecf20Sopenharmony_ci nandc->props->is_bam ? 0 : DM_EN); 12568c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_EXEC_CMD, 1); 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); 12598c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci return 0; 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci/* sets up descriptors for NAND_CMD_RESET */ 12678c2ecf20Sopenharmony_cistatic int reset(struct qcom_nand_host *host) 12688c2ecf20Sopenharmony_ci{ 12698c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->chip; 12708c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_FLASH_CMD, OP_RESET_DEVICE); 12738c2ecf20Sopenharmony_ci nandc_set_reg(nandc, NAND_EXEC_CMD, 1); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); 12768c2ecf20Sopenharmony_ci write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci return 0; 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci/* helpers to submit/free our list of dma descriptors */ 12848c2ecf20Sopenharmony_cistatic int submit_descs(struct qcom_nand_controller *nandc) 12858c2ecf20Sopenharmony_ci{ 12868c2ecf20Sopenharmony_ci struct desc_info *desc; 12878c2ecf20Sopenharmony_ci dma_cookie_t cookie = 0; 12888c2ecf20Sopenharmony_ci struct bam_transaction *bam_txn = nandc->bam_txn; 12898c2ecf20Sopenharmony_ci int r; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci if (nandc->props->is_bam) { 12928c2ecf20Sopenharmony_ci if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { 12938c2ecf20Sopenharmony_ci r = prepare_bam_async_desc(nandc, nandc->rx_chan, 0); 12948c2ecf20Sopenharmony_ci if (r) 12958c2ecf20Sopenharmony_ci return r; 12968c2ecf20Sopenharmony_ci } 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) { 12998c2ecf20Sopenharmony_ci r = prepare_bam_async_desc(nandc, nandc->tx_chan, 13008c2ecf20Sopenharmony_ci DMA_PREP_INTERRUPT); 13018c2ecf20Sopenharmony_ci if (r) 13028c2ecf20Sopenharmony_ci return r; 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) { 13068c2ecf20Sopenharmony_ci r = prepare_bam_async_desc(nandc, nandc->cmd_chan, 13078c2ecf20Sopenharmony_ci DMA_PREP_CMD); 13088c2ecf20Sopenharmony_ci if (r) 13098c2ecf20Sopenharmony_ci return r; 13108c2ecf20Sopenharmony_ci } 13118c2ecf20Sopenharmony_ci } 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci list_for_each_entry(desc, &nandc->desc_list, node) 13148c2ecf20Sopenharmony_ci cookie = dmaengine_submit(desc->dma_desc); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci if (nandc->props->is_bam) { 13178c2ecf20Sopenharmony_ci bam_txn->last_cmd_desc->callback = qpic_bam_dma_done; 13188c2ecf20Sopenharmony_ci bam_txn->last_cmd_desc->callback_param = bam_txn; 13198c2ecf20Sopenharmony_ci if (bam_txn->last_data_desc) { 13208c2ecf20Sopenharmony_ci bam_txn->last_data_desc->callback = qpic_bam_dma_done; 13218c2ecf20Sopenharmony_ci bam_txn->last_data_desc->callback_param = bam_txn; 13228c2ecf20Sopenharmony_ci bam_txn->wait_second_completion = true; 13238c2ecf20Sopenharmony_ci } 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci dma_async_issue_pending(nandc->tx_chan); 13268c2ecf20Sopenharmony_ci dma_async_issue_pending(nandc->rx_chan); 13278c2ecf20Sopenharmony_ci dma_async_issue_pending(nandc->cmd_chan); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&bam_txn->txn_done, 13308c2ecf20Sopenharmony_ci QPIC_NAND_COMPLETION_TIMEOUT)) 13318c2ecf20Sopenharmony_ci return -ETIMEDOUT; 13328c2ecf20Sopenharmony_ci } else { 13338c2ecf20Sopenharmony_ci if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) 13348c2ecf20Sopenharmony_ci return -ETIMEDOUT; 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci return 0; 13388c2ecf20Sopenharmony_ci} 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_cistatic void free_descs(struct qcom_nand_controller *nandc) 13418c2ecf20Sopenharmony_ci{ 13428c2ecf20Sopenharmony_ci struct desc_info *desc, *n; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { 13458c2ecf20Sopenharmony_ci list_del(&desc->node); 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci if (nandc->props->is_bam) 13488c2ecf20Sopenharmony_ci dma_unmap_sg(nandc->dev, desc->bam_sgl, 13498c2ecf20Sopenharmony_ci desc->sgl_cnt, desc->dir); 13508c2ecf20Sopenharmony_ci else 13518c2ecf20Sopenharmony_ci dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1, 13528c2ecf20Sopenharmony_ci desc->dir); 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci kfree(desc); 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci/* reset the register read buffer for next NAND operation */ 13598c2ecf20Sopenharmony_cistatic void clear_read_regs(struct qcom_nand_controller *nandc) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci nandc->reg_read_pos = 0; 13628c2ecf20Sopenharmony_ci nandc_read_buffer_sync(nandc, false); 13638c2ecf20Sopenharmony_ci} 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_cistatic void pre_command(struct qcom_nand_host *host, int command) 13668c2ecf20Sopenharmony_ci{ 13678c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->chip; 13688c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci nandc->buf_count = 0; 13718c2ecf20Sopenharmony_ci nandc->buf_start = 0; 13728c2ecf20Sopenharmony_ci host->use_ecc = false; 13738c2ecf20Sopenharmony_ci host->last_command = command; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci clear_read_regs(nandc); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci if (command == NAND_CMD_RESET || command == NAND_CMD_READID || 13788c2ecf20Sopenharmony_ci command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) 13798c2ecf20Sopenharmony_ci clear_bam_transaction(nandc); 13808c2ecf20Sopenharmony_ci} 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci/* 13838c2ecf20Sopenharmony_ci * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our 13848c2ecf20Sopenharmony_ci * privately maintained status byte, this status byte can be read after 13858c2ecf20Sopenharmony_ci * NAND_CMD_STATUS is called 13868c2ecf20Sopenharmony_ci */ 13878c2ecf20Sopenharmony_cistatic void parse_erase_write_errors(struct qcom_nand_host *host, int command) 13888c2ecf20Sopenharmony_ci{ 13898c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->chip; 13908c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 13918c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 13928c2ecf20Sopenharmony_ci int num_cw; 13938c2ecf20Sopenharmony_ci int i; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1; 13968c2ecf20Sopenharmony_ci nandc_read_buffer_sync(nandc, true); 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci for (i = 0; i < num_cw; i++) { 13998c2ecf20Sopenharmony_ci u32 flash_status = le32_to_cpu(nandc->reg_read_buf[i]); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci if (flash_status & FS_MPU_ERR) 14028c2ecf20Sopenharmony_ci host->status &= ~NAND_STATUS_WP; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && 14058c2ecf20Sopenharmony_ci (flash_status & 14068c2ecf20Sopenharmony_ci FS_DEVICE_STS_ERR))) 14078c2ecf20Sopenharmony_ci host->status |= NAND_STATUS_FAIL; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci} 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_cistatic void post_command(struct qcom_nand_host *host, int command) 14128c2ecf20Sopenharmony_ci{ 14138c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->chip; 14148c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci switch (command) { 14178c2ecf20Sopenharmony_ci case NAND_CMD_READID: 14188c2ecf20Sopenharmony_ci nandc_read_buffer_sync(nandc, true); 14198c2ecf20Sopenharmony_ci memcpy(nandc->data_buffer, nandc->reg_read_buf, 14208c2ecf20Sopenharmony_ci nandc->buf_count); 14218c2ecf20Sopenharmony_ci break; 14228c2ecf20Sopenharmony_ci case NAND_CMD_PAGEPROG: 14238c2ecf20Sopenharmony_ci case NAND_CMD_ERASE1: 14248c2ecf20Sopenharmony_ci parse_erase_write_errors(host, command); 14258c2ecf20Sopenharmony_ci break; 14268c2ecf20Sopenharmony_ci default: 14278c2ecf20Sopenharmony_ci break; 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci} 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci/* 14328c2ecf20Sopenharmony_ci * Implements chip->legacy.cmdfunc. It's only used for a limited set of 14338c2ecf20Sopenharmony_ci * commands. The rest of the commands wouldn't be called by upper layers. 14348c2ecf20Sopenharmony_ci * For example, NAND_CMD_READOOB would never be called because we have our own 14358c2ecf20Sopenharmony_ci * versions of read_oob ops for nand_ecc_ctrl. 14368c2ecf20Sopenharmony_ci */ 14378c2ecf20Sopenharmony_cistatic void qcom_nandc_command(struct nand_chip *chip, unsigned int command, 14388c2ecf20Sopenharmony_ci int column, int page_addr) 14398c2ecf20Sopenharmony_ci{ 14408c2ecf20Sopenharmony_ci struct qcom_nand_host *host = to_qcom_nand_host(chip); 14418c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 14428c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 14438c2ecf20Sopenharmony_ci bool wait = false; 14448c2ecf20Sopenharmony_ci int ret = 0; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci pre_command(host, command); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci switch (command) { 14498c2ecf20Sopenharmony_ci case NAND_CMD_RESET: 14508c2ecf20Sopenharmony_ci ret = reset(host); 14518c2ecf20Sopenharmony_ci wait = true; 14528c2ecf20Sopenharmony_ci break; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci case NAND_CMD_READID: 14558c2ecf20Sopenharmony_ci nandc->buf_count = 4; 14568c2ecf20Sopenharmony_ci ret = read_id(host, column); 14578c2ecf20Sopenharmony_ci wait = true; 14588c2ecf20Sopenharmony_ci break; 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci case NAND_CMD_PARAM: 14618c2ecf20Sopenharmony_ci ret = nandc_param(host); 14628c2ecf20Sopenharmony_ci wait = true; 14638c2ecf20Sopenharmony_ci break; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci case NAND_CMD_ERASE1: 14668c2ecf20Sopenharmony_ci ret = erase_block(host, page_addr); 14678c2ecf20Sopenharmony_ci wait = true; 14688c2ecf20Sopenharmony_ci break; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci case NAND_CMD_READ0: 14718c2ecf20Sopenharmony_ci /* we read the entire page for now */ 14728c2ecf20Sopenharmony_ci WARN_ON(column != 0); 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci host->use_ecc = true; 14758c2ecf20Sopenharmony_ci set_address(host, 0, page_addr); 14768c2ecf20Sopenharmony_ci update_rw_regs(host, ecc->steps, true); 14778c2ecf20Sopenharmony_ci break; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci case NAND_CMD_SEQIN: 14808c2ecf20Sopenharmony_ci WARN_ON(column != 0); 14818c2ecf20Sopenharmony_ci set_address(host, 0, page_addr); 14828c2ecf20Sopenharmony_ci break; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci case NAND_CMD_PAGEPROG: 14858c2ecf20Sopenharmony_ci case NAND_CMD_STATUS: 14868c2ecf20Sopenharmony_ci case NAND_CMD_NONE: 14878c2ecf20Sopenharmony_ci default: 14888c2ecf20Sopenharmony_ci break; 14898c2ecf20Sopenharmony_ci } 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci if (ret) { 14928c2ecf20Sopenharmony_ci dev_err(nandc->dev, "failure executing command %d\n", 14938c2ecf20Sopenharmony_ci command); 14948c2ecf20Sopenharmony_ci free_descs(nandc); 14958c2ecf20Sopenharmony_ci return; 14968c2ecf20Sopenharmony_ci } 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci if (wait) { 14998c2ecf20Sopenharmony_ci ret = submit_descs(nandc); 15008c2ecf20Sopenharmony_ci if (ret) 15018c2ecf20Sopenharmony_ci dev_err(nandc->dev, 15028c2ecf20Sopenharmony_ci "failure submitting descs for command %d\n", 15038c2ecf20Sopenharmony_ci command); 15048c2ecf20Sopenharmony_ci } 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci free_descs(nandc); 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci post_command(host, command); 15098c2ecf20Sopenharmony_ci} 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci/* 15128c2ecf20Sopenharmony_ci * when using BCH ECC, the HW flags an error in NAND_FLASH_STATUS if it read 15138c2ecf20Sopenharmony_ci * an erased CW, and reports an erased CW in NAND_ERASED_CW_DETECT_STATUS. 15148c2ecf20Sopenharmony_ci * 15158c2ecf20Sopenharmony_ci * when using RS ECC, the HW reports the same erros when reading an erased CW, 15168c2ecf20Sopenharmony_ci * but it notifies that it is an erased CW by placing special characters at 15178c2ecf20Sopenharmony_ci * certain offsets in the buffer. 15188c2ecf20Sopenharmony_ci * 15198c2ecf20Sopenharmony_ci * verify if the page is erased or not, and fix up the page for RS ECC by 15208c2ecf20Sopenharmony_ci * replacing the special characters with 0xff. 15218c2ecf20Sopenharmony_ci */ 15228c2ecf20Sopenharmony_cistatic bool erased_chunk_check_and_fixup(u8 *data_buf, int data_len) 15238c2ecf20Sopenharmony_ci{ 15248c2ecf20Sopenharmony_ci u8 empty1, empty2; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci /* 15278c2ecf20Sopenharmony_ci * an erased page flags an error in NAND_FLASH_STATUS, check if the page 15288c2ecf20Sopenharmony_ci * is erased by looking for 0x54s at offsets 3 and 175 from the 15298c2ecf20Sopenharmony_ci * beginning of each codeword 15308c2ecf20Sopenharmony_ci */ 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci empty1 = data_buf[3]; 15338c2ecf20Sopenharmony_ci empty2 = data_buf[175]; 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci /* 15368c2ecf20Sopenharmony_ci * if the erased codework markers, if they exist override them with 15378c2ecf20Sopenharmony_ci * 0xffs 15388c2ecf20Sopenharmony_ci */ 15398c2ecf20Sopenharmony_ci if ((empty1 == 0x54 && empty2 == 0xff) || 15408c2ecf20Sopenharmony_ci (empty1 == 0xff && empty2 == 0x54)) { 15418c2ecf20Sopenharmony_ci data_buf[3] = 0xff; 15428c2ecf20Sopenharmony_ci data_buf[175] = 0xff; 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci /* 15468c2ecf20Sopenharmony_ci * check if the entire chunk contains 0xffs or not. if it doesn't, then 15478c2ecf20Sopenharmony_ci * restore the original values at the special offsets 15488c2ecf20Sopenharmony_ci */ 15498c2ecf20Sopenharmony_ci if (memchr_inv(data_buf, 0xff, data_len)) { 15508c2ecf20Sopenharmony_ci data_buf[3] = empty1; 15518c2ecf20Sopenharmony_ci data_buf[175] = empty2; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci return false; 15548c2ecf20Sopenharmony_ci } 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci return true; 15578c2ecf20Sopenharmony_ci} 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_cistruct read_stats { 15608c2ecf20Sopenharmony_ci __le32 flash; 15618c2ecf20Sopenharmony_ci __le32 buffer; 15628c2ecf20Sopenharmony_ci __le32 erased_cw; 15638c2ecf20Sopenharmony_ci}; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci/* reads back FLASH_STATUS register set by the controller */ 15668c2ecf20Sopenharmony_cistatic int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) 15678c2ecf20Sopenharmony_ci{ 15688c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->chip; 15698c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 15708c2ecf20Sopenharmony_ci int i; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci nandc_read_buffer_sync(nandc, true); 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci for (i = 0; i < cw_cnt; i++) { 15758c2ecf20Sopenharmony_ci u32 flash = le32_to_cpu(nandc->reg_read_buf[i]); 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci if (flash & (FS_OP_ERR | FS_MPU_ERR)) 15788c2ecf20Sopenharmony_ci return -EIO; 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci return 0; 15828c2ecf20Sopenharmony_ci} 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci/* performs raw read for one codeword */ 15858c2ecf20Sopenharmony_cistatic int 15868c2ecf20Sopenharmony_ciqcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, 15878c2ecf20Sopenharmony_ci u8 *data_buf, u8 *oob_buf, int page, int cw) 15888c2ecf20Sopenharmony_ci{ 15898c2ecf20Sopenharmony_ci struct qcom_nand_host *host = to_qcom_nand_host(chip); 15908c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 15918c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 15928c2ecf20Sopenharmony_ci int data_size1, data_size2, oob_size1, oob_size2; 15938c2ecf20Sopenharmony_ci int ret, reg_off = FLASH_BUF_ACC, read_loc = 0; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci nand_read_page_op(chip, page, 0, NULL, 0); 15968c2ecf20Sopenharmony_ci host->use_ecc = false; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci clear_bam_transaction(nandc); 15998c2ecf20Sopenharmony_ci set_address(host, host->cw_size * cw, page); 16008c2ecf20Sopenharmony_ci update_rw_regs(host, 1, true); 16018c2ecf20Sopenharmony_ci config_nand_page_read(nandc); 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); 16048c2ecf20Sopenharmony_ci oob_size1 = host->bbm_size; 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci if (cw == (ecc->steps - 1)) { 16078c2ecf20Sopenharmony_ci data_size2 = ecc->size - data_size1 - 16088c2ecf20Sopenharmony_ci ((ecc->steps - 1) * 4); 16098c2ecf20Sopenharmony_ci oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw + 16108c2ecf20Sopenharmony_ci host->spare_bytes; 16118c2ecf20Sopenharmony_ci } else { 16128c2ecf20Sopenharmony_ci data_size2 = host->cw_data - data_size1; 16138c2ecf20Sopenharmony_ci oob_size2 = host->ecc_bytes_hw + host->spare_bytes; 16148c2ecf20Sopenharmony_ci } 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci if (nandc->props->is_bam) { 16178c2ecf20Sopenharmony_ci nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0); 16188c2ecf20Sopenharmony_ci read_loc += data_size1; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0); 16218c2ecf20Sopenharmony_ci read_loc += oob_size1; 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0); 16248c2ecf20Sopenharmony_ci read_loc += data_size2; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1); 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci config_nand_cw_read(nandc, false); 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci read_data_dma(nandc, reg_off, data_buf, data_size1, 0); 16328c2ecf20Sopenharmony_ci reg_off += data_size1; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0); 16358c2ecf20Sopenharmony_ci reg_off += oob_size1; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci read_data_dma(nandc, reg_off, data_buf + data_size1, data_size2, 0); 16388c2ecf20Sopenharmony_ci reg_off += data_size2; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci read_data_dma(nandc, reg_off, oob_buf + oob_size1, oob_size2, 0); 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci ret = submit_descs(nandc); 16438c2ecf20Sopenharmony_ci free_descs(nandc); 16448c2ecf20Sopenharmony_ci if (ret) { 16458c2ecf20Sopenharmony_ci dev_err(nandc->dev, "failure to read raw cw %d\n", cw); 16468c2ecf20Sopenharmony_ci return ret; 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci return check_flash_errors(host, 1); 16508c2ecf20Sopenharmony_ci} 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci/* 16538c2ecf20Sopenharmony_ci * Bitflips can happen in erased codewords also so this function counts the 16548c2ecf20Sopenharmony_ci * number of 0 in each CW for which ECC engine returns the uncorrectable 16558c2ecf20Sopenharmony_ci * error. The page will be assumed as erased if this count is less than or 16568c2ecf20Sopenharmony_ci * equal to the ecc->strength for each CW. 16578c2ecf20Sopenharmony_ci * 16588c2ecf20Sopenharmony_ci * 1. Both DATA and OOB need to be checked for number of 0. The 16598c2ecf20Sopenharmony_ci * top-level API can be called with only data buf or OOB buf so use 16608c2ecf20Sopenharmony_ci * chip->data_buf if data buf is null and chip->oob_poi if oob buf 16618c2ecf20Sopenharmony_ci * is null for copying the raw bytes. 16628c2ecf20Sopenharmony_ci * 2. Perform raw read for all the CW which has uncorrectable errors. 16638c2ecf20Sopenharmony_ci * 3. For each CW, check the number of 0 in cw_data and usable OOB bytes. 16648c2ecf20Sopenharmony_ci * The BBM and spare bytes bit flip won’t affect the ECC so don’t check 16658c2ecf20Sopenharmony_ci * the number of bitflips in this area. 16668c2ecf20Sopenharmony_ci */ 16678c2ecf20Sopenharmony_cistatic int 16688c2ecf20Sopenharmony_cicheck_for_erased_page(struct qcom_nand_host *host, u8 *data_buf, 16698c2ecf20Sopenharmony_ci u8 *oob_buf, unsigned long uncorrectable_cws, 16708c2ecf20Sopenharmony_ci int page, unsigned int max_bitflips) 16718c2ecf20Sopenharmony_ci{ 16728c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->chip; 16738c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 16748c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 16758c2ecf20Sopenharmony_ci u8 *cw_data_buf, *cw_oob_buf; 16768c2ecf20Sopenharmony_ci int cw, data_size, oob_size, ret = 0; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci if (!data_buf) 16798c2ecf20Sopenharmony_ci data_buf = nand_get_data_buf(chip); 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci if (!oob_buf) { 16828c2ecf20Sopenharmony_ci nand_get_data_buf(chip); 16838c2ecf20Sopenharmony_ci oob_buf = chip->oob_poi; 16848c2ecf20Sopenharmony_ci } 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) { 16878c2ecf20Sopenharmony_ci if (cw == (ecc->steps - 1)) { 16888c2ecf20Sopenharmony_ci data_size = ecc->size - ((ecc->steps - 1) * 4); 16898c2ecf20Sopenharmony_ci oob_size = (ecc->steps * 4) + host->ecc_bytes_hw; 16908c2ecf20Sopenharmony_ci } else { 16918c2ecf20Sopenharmony_ci data_size = host->cw_data; 16928c2ecf20Sopenharmony_ci oob_size = host->ecc_bytes_hw; 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci /* determine starting buffer address for current CW */ 16968c2ecf20Sopenharmony_ci cw_data_buf = data_buf + (cw * host->cw_data); 16978c2ecf20Sopenharmony_ci cw_oob_buf = oob_buf + (cw * ecc->bytes); 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci ret = qcom_nandc_read_cw_raw(mtd, chip, cw_data_buf, 17008c2ecf20Sopenharmony_ci cw_oob_buf, page, cw); 17018c2ecf20Sopenharmony_ci if (ret) 17028c2ecf20Sopenharmony_ci return ret; 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci /* 17058c2ecf20Sopenharmony_ci * make sure it isn't an erased page reported 17068c2ecf20Sopenharmony_ci * as not-erased by HW because of a few bitflips 17078c2ecf20Sopenharmony_ci */ 17088c2ecf20Sopenharmony_ci ret = nand_check_erased_ecc_chunk(cw_data_buf, data_size, 17098c2ecf20Sopenharmony_ci cw_oob_buf + host->bbm_size, 17108c2ecf20Sopenharmony_ci oob_size, NULL, 17118c2ecf20Sopenharmony_ci 0, ecc->strength); 17128c2ecf20Sopenharmony_ci if (ret < 0) { 17138c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 17148c2ecf20Sopenharmony_ci } else { 17158c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += ret; 17168c2ecf20Sopenharmony_ci max_bitflips = max_t(unsigned int, max_bitflips, ret); 17178c2ecf20Sopenharmony_ci } 17188c2ecf20Sopenharmony_ci } 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci return max_bitflips; 17218c2ecf20Sopenharmony_ci} 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci/* 17248c2ecf20Sopenharmony_ci * reads back status registers set by the controller to notify page read 17258c2ecf20Sopenharmony_ci * errors. this is equivalent to what 'ecc->correct()' would do. 17268c2ecf20Sopenharmony_ci */ 17278c2ecf20Sopenharmony_cistatic int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, 17288c2ecf20Sopenharmony_ci u8 *oob_buf, int page) 17298c2ecf20Sopenharmony_ci{ 17308c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->chip; 17318c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 17328c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 17338c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 17348c2ecf20Sopenharmony_ci unsigned int max_bitflips = 0, uncorrectable_cws = 0; 17358c2ecf20Sopenharmony_ci struct read_stats *buf; 17368c2ecf20Sopenharmony_ci bool flash_op_err = false, erased; 17378c2ecf20Sopenharmony_ci int i; 17388c2ecf20Sopenharmony_ci u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci buf = (struct read_stats *)nandc->reg_read_buf; 17418c2ecf20Sopenharmony_ci nandc_read_buffer_sync(nandc, true); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci for (i = 0; i < ecc->steps; i++, buf++) { 17448c2ecf20Sopenharmony_ci u32 flash, buffer, erased_cw; 17458c2ecf20Sopenharmony_ci int data_len, oob_len; 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci if (i == (ecc->steps - 1)) { 17488c2ecf20Sopenharmony_ci data_len = ecc->size - ((ecc->steps - 1) << 2); 17498c2ecf20Sopenharmony_ci oob_len = ecc->steps << 2; 17508c2ecf20Sopenharmony_ci } else { 17518c2ecf20Sopenharmony_ci data_len = host->cw_data; 17528c2ecf20Sopenharmony_ci oob_len = 0; 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci flash = le32_to_cpu(buf->flash); 17568c2ecf20Sopenharmony_ci buffer = le32_to_cpu(buf->buffer); 17578c2ecf20Sopenharmony_ci erased_cw = le32_to_cpu(buf->erased_cw); 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci /* 17608c2ecf20Sopenharmony_ci * Check ECC failure for each codeword. ECC failure can 17618c2ecf20Sopenharmony_ci * happen in either of the following conditions 17628c2ecf20Sopenharmony_ci * 1. If number of bitflips are greater than ECC engine 17638c2ecf20Sopenharmony_ci * capability. 17648c2ecf20Sopenharmony_ci * 2. If this codeword contains all 0xff for which erased 17658c2ecf20Sopenharmony_ci * codeword detection check will be done. 17668c2ecf20Sopenharmony_ci */ 17678c2ecf20Sopenharmony_ci if ((flash & FS_OP_ERR) && (buffer & BS_UNCORRECTABLE_BIT)) { 17688c2ecf20Sopenharmony_ci /* 17698c2ecf20Sopenharmony_ci * For BCH ECC, ignore erased codeword errors, if 17708c2ecf20Sopenharmony_ci * ERASED_CW bits are set. 17718c2ecf20Sopenharmony_ci */ 17728c2ecf20Sopenharmony_ci if (host->bch_enabled) { 17738c2ecf20Sopenharmony_ci erased = (erased_cw & ERASED_CW) == ERASED_CW ? 17748c2ecf20Sopenharmony_ci true : false; 17758c2ecf20Sopenharmony_ci /* 17768c2ecf20Sopenharmony_ci * For RS ECC, HW reports the erased CW by placing 17778c2ecf20Sopenharmony_ci * special characters at certain offsets in the buffer. 17788c2ecf20Sopenharmony_ci * These special characters will be valid only if 17798c2ecf20Sopenharmony_ci * complete page is read i.e. data_buf is not NULL. 17808c2ecf20Sopenharmony_ci */ 17818c2ecf20Sopenharmony_ci } else if (data_buf) { 17828c2ecf20Sopenharmony_ci erased = erased_chunk_check_and_fixup(data_buf, 17838c2ecf20Sopenharmony_ci data_len); 17848c2ecf20Sopenharmony_ci } else { 17858c2ecf20Sopenharmony_ci erased = false; 17868c2ecf20Sopenharmony_ci } 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci if (!erased) 17898c2ecf20Sopenharmony_ci uncorrectable_cws |= BIT(i); 17908c2ecf20Sopenharmony_ci /* 17918c2ecf20Sopenharmony_ci * Check if MPU or any other operational error (timeout, 17928c2ecf20Sopenharmony_ci * device failure, etc.) happened for this codeword and 17938c2ecf20Sopenharmony_ci * make flash_op_err true. If flash_op_err is set, then 17948c2ecf20Sopenharmony_ci * EIO will be returned for page read. 17958c2ecf20Sopenharmony_ci */ 17968c2ecf20Sopenharmony_ci } else if (flash & (FS_OP_ERR | FS_MPU_ERR)) { 17978c2ecf20Sopenharmony_ci flash_op_err = true; 17988c2ecf20Sopenharmony_ci /* 17998c2ecf20Sopenharmony_ci * No ECC or operational errors happened. Check the number of 18008c2ecf20Sopenharmony_ci * bits corrected and update the ecc_stats.corrected. 18018c2ecf20Sopenharmony_ci */ 18028c2ecf20Sopenharmony_ci } else { 18038c2ecf20Sopenharmony_ci unsigned int stat; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci stat = buffer & BS_CORRECTABLE_ERR_MSK; 18068c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += stat; 18078c2ecf20Sopenharmony_ci max_bitflips = max(max_bitflips, stat); 18088c2ecf20Sopenharmony_ci } 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci if (data_buf) 18118c2ecf20Sopenharmony_ci data_buf += data_len; 18128c2ecf20Sopenharmony_ci if (oob_buf) 18138c2ecf20Sopenharmony_ci oob_buf += oob_len + ecc->bytes; 18148c2ecf20Sopenharmony_ci } 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci if (flash_op_err) 18178c2ecf20Sopenharmony_ci return -EIO; 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci if (!uncorrectable_cws) 18208c2ecf20Sopenharmony_ci return max_bitflips; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci return check_for_erased_page(host, data_buf_start, oob_buf_start, 18238c2ecf20Sopenharmony_ci uncorrectable_cws, page, 18248c2ecf20Sopenharmony_ci max_bitflips); 18258c2ecf20Sopenharmony_ci} 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci/* 18288c2ecf20Sopenharmony_ci * helper to perform the actual page read operation, used by ecc->read_page(), 18298c2ecf20Sopenharmony_ci * ecc->read_oob() 18308c2ecf20Sopenharmony_ci */ 18318c2ecf20Sopenharmony_cistatic int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, 18328c2ecf20Sopenharmony_ci u8 *oob_buf, int page) 18338c2ecf20Sopenharmony_ci{ 18348c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->chip; 18358c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 18368c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 18378c2ecf20Sopenharmony_ci u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; 18388c2ecf20Sopenharmony_ci int i, ret; 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci config_nand_page_read(nandc); 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci /* queue cmd descs for each codeword */ 18438c2ecf20Sopenharmony_ci for (i = 0; i < ecc->steps; i++) { 18448c2ecf20Sopenharmony_ci int data_size, oob_size; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci if (i == (ecc->steps - 1)) { 18478c2ecf20Sopenharmony_ci data_size = ecc->size - ((ecc->steps - 1) << 2); 18488c2ecf20Sopenharmony_ci oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + 18498c2ecf20Sopenharmony_ci host->spare_bytes; 18508c2ecf20Sopenharmony_ci } else { 18518c2ecf20Sopenharmony_ci data_size = host->cw_data; 18528c2ecf20Sopenharmony_ci oob_size = host->ecc_bytes_hw + host->spare_bytes; 18538c2ecf20Sopenharmony_ci } 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci if (nandc->props->is_bam) { 18568c2ecf20Sopenharmony_ci if (data_buf && oob_buf) { 18578c2ecf20Sopenharmony_ci nandc_set_read_loc(nandc, 0, 0, data_size, 0); 18588c2ecf20Sopenharmony_ci nandc_set_read_loc(nandc, 1, data_size, 18598c2ecf20Sopenharmony_ci oob_size, 1); 18608c2ecf20Sopenharmony_ci } else if (data_buf) { 18618c2ecf20Sopenharmony_ci nandc_set_read_loc(nandc, 0, 0, data_size, 1); 18628c2ecf20Sopenharmony_ci } else { 18638c2ecf20Sopenharmony_ci nandc_set_read_loc(nandc, 0, data_size, 18648c2ecf20Sopenharmony_ci oob_size, 1); 18658c2ecf20Sopenharmony_ci } 18668c2ecf20Sopenharmony_ci } 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci config_nand_cw_read(nandc, true); 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci if (data_buf) 18718c2ecf20Sopenharmony_ci read_data_dma(nandc, FLASH_BUF_ACC, data_buf, 18728c2ecf20Sopenharmony_ci data_size, 0); 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci /* 18758c2ecf20Sopenharmony_ci * when ecc is enabled, the controller doesn't read the real 18768c2ecf20Sopenharmony_ci * or dummy bad block markers in each chunk. To maintain a 18778c2ecf20Sopenharmony_ci * consistent layout across RAW and ECC reads, we just 18788c2ecf20Sopenharmony_ci * leave the real/dummy BBM offsets empty (i.e, filled with 18798c2ecf20Sopenharmony_ci * 0xffs) 18808c2ecf20Sopenharmony_ci */ 18818c2ecf20Sopenharmony_ci if (oob_buf) { 18828c2ecf20Sopenharmony_ci int j; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci for (j = 0; j < host->bbm_size; j++) 18858c2ecf20Sopenharmony_ci *oob_buf++ = 0xff; 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci read_data_dma(nandc, FLASH_BUF_ACC + data_size, 18888c2ecf20Sopenharmony_ci oob_buf, oob_size, 0); 18898c2ecf20Sopenharmony_ci } 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci if (data_buf) 18928c2ecf20Sopenharmony_ci data_buf += data_size; 18938c2ecf20Sopenharmony_ci if (oob_buf) 18948c2ecf20Sopenharmony_ci oob_buf += oob_size; 18958c2ecf20Sopenharmony_ci } 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci ret = submit_descs(nandc); 18988c2ecf20Sopenharmony_ci free_descs(nandc); 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci if (ret) { 19018c2ecf20Sopenharmony_ci dev_err(nandc->dev, "failure to read page/oob\n"); 19028c2ecf20Sopenharmony_ci return ret; 19038c2ecf20Sopenharmony_ci } 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci return parse_read_errors(host, data_buf_start, oob_buf_start, page); 19068c2ecf20Sopenharmony_ci} 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci/* 19098c2ecf20Sopenharmony_ci * a helper that copies the last step/codeword of a page (containing free oob) 19108c2ecf20Sopenharmony_ci * into our local buffer 19118c2ecf20Sopenharmony_ci */ 19128c2ecf20Sopenharmony_cistatic int copy_last_cw(struct qcom_nand_host *host, int page) 19138c2ecf20Sopenharmony_ci{ 19148c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->chip; 19158c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 19168c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 19178c2ecf20Sopenharmony_ci int size; 19188c2ecf20Sopenharmony_ci int ret; 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci clear_read_regs(nandc); 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci size = host->use_ecc ? host->cw_data : host->cw_size; 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci /* prepare a clean read buffer */ 19258c2ecf20Sopenharmony_ci memset(nandc->data_buffer, 0xff, size); 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci set_address(host, host->cw_size * (ecc->steps - 1), page); 19288c2ecf20Sopenharmony_ci update_rw_regs(host, 1, true); 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci config_nand_single_cw_page_read(nandc, host->use_ecc); 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci ret = submit_descs(nandc); 19358c2ecf20Sopenharmony_ci if (ret) 19368c2ecf20Sopenharmony_ci dev_err(nandc->dev, "failed to copy last codeword\n"); 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci free_descs(nandc); 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci return ret; 19418c2ecf20Sopenharmony_ci} 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci/* implements ecc->read_page() */ 19448c2ecf20Sopenharmony_cistatic int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf, 19458c2ecf20Sopenharmony_ci int oob_required, int page) 19468c2ecf20Sopenharmony_ci{ 19478c2ecf20Sopenharmony_ci struct qcom_nand_host *host = to_qcom_nand_host(chip); 19488c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 19498c2ecf20Sopenharmony_ci u8 *data_buf, *oob_buf = NULL; 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci nand_read_page_op(chip, page, 0, NULL, 0); 19528c2ecf20Sopenharmony_ci data_buf = buf; 19538c2ecf20Sopenharmony_ci oob_buf = oob_required ? chip->oob_poi : NULL; 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci clear_bam_transaction(nandc); 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci return read_page_ecc(host, data_buf, oob_buf, page); 19588c2ecf20Sopenharmony_ci} 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci/* implements ecc->read_page_raw() */ 19618c2ecf20Sopenharmony_cistatic int qcom_nandc_read_page_raw(struct nand_chip *chip, uint8_t *buf, 19628c2ecf20Sopenharmony_ci int oob_required, int page) 19638c2ecf20Sopenharmony_ci{ 19648c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 19658c2ecf20Sopenharmony_ci struct qcom_nand_host *host = to_qcom_nand_host(chip); 19668c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 19678c2ecf20Sopenharmony_ci int cw, ret; 19688c2ecf20Sopenharmony_ci u8 *data_buf = buf, *oob_buf = chip->oob_poi; 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci for (cw = 0; cw < ecc->steps; cw++) { 19718c2ecf20Sopenharmony_ci ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf, 19728c2ecf20Sopenharmony_ci page, cw); 19738c2ecf20Sopenharmony_ci if (ret) 19748c2ecf20Sopenharmony_ci return ret; 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci data_buf += host->cw_data; 19778c2ecf20Sopenharmony_ci oob_buf += ecc->bytes; 19788c2ecf20Sopenharmony_ci } 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci return 0; 19818c2ecf20Sopenharmony_ci} 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci/* implements ecc->read_oob() */ 19848c2ecf20Sopenharmony_cistatic int qcom_nandc_read_oob(struct nand_chip *chip, int page) 19858c2ecf20Sopenharmony_ci{ 19868c2ecf20Sopenharmony_ci struct qcom_nand_host *host = to_qcom_nand_host(chip); 19878c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 19888c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci clear_read_regs(nandc); 19918c2ecf20Sopenharmony_ci clear_bam_transaction(nandc); 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci host->use_ecc = true; 19948c2ecf20Sopenharmony_ci set_address(host, 0, page); 19958c2ecf20Sopenharmony_ci update_rw_regs(host, ecc->steps, true); 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci return read_page_ecc(host, NULL, chip->oob_poi, page); 19988c2ecf20Sopenharmony_ci} 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci/* implements ecc->write_page() */ 20018c2ecf20Sopenharmony_cistatic int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf, 20028c2ecf20Sopenharmony_ci int oob_required, int page) 20038c2ecf20Sopenharmony_ci{ 20048c2ecf20Sopenharmony_ci struct qcom_nand_host *host = to_qcom_nand_host(chip); 20058c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 20068c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 20078c2ecf20Sopenharmony_ci u8 *data_buf, *oob_buf; 20088c2ecf20Sopenharmony_ci int i, ret; 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci nand_prog_page_begin_op(chip, page, 0, NULL, 0); 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci clear_read_regs(nandc); 20138c2ecf20Sopenharmony_ci clear_bam_transaction(nandc); 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci data_buf = (u8 *)buf; 20168c2ecf20Sopenharmony_ci oob_buf = chip->oob_poi; 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci host->use_ecc = true; 20198c2ecf20Sopenharmony_ci update_rw_regs(host, ecc->steps, false); 20208c2ecf20Sopenharmony_ci config_nand_page_write(nandc); 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci for (i = 0; i < ecc->steps; i++) { 20238c2ecf20Sopenharmony_ci int data_size, oob_size; 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci if (i == (ecc->steps - 1)) { 20268c2ecf20Sopenharmony_ci data_size = ecc->size - ((ecc->steps - 1) << 2); 20278c2ecf20Sopenharmony_ci oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + 20288c2ecf20Sopenharmony_ci host->spare_bytes; 20298c2ecf20Sopenharmony_ci } else { 20308c2ecf20Sopenharmony_ci data_size = host->cw_data; 20318c2ecf20Sopenharmony_ci oob_size = ecc->bytes; 20328c2ecf20Sopenharmony_ci } 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size, 20368c2ecf20Sopenharmony_ci i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0); 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci /* 20398c2ecf20Sopenharmony_ci * when ECC is enabled, we don't really need to write anything 20408c2ecf20Sopenharmony_ci * to oob for the first n - 1 codewords since these oob regions 20418c2ecf20Sopenharmony_ci * just contain ECC bytes that's written by the controller 20428c2ecf20Sopenharmony_ci * itself. For the last codeword, we skip the bbm positions and 20438c2ecf20Sopenharmony_ci * write to the free oob area. 20448c2ecf20Sopenharmony_ci */ 20458c2ecf20Sopenharmony_ci if (i == (ecc->steps - 1)) { 20468c2ecf20Sopenharmony_ci oob_buf += host->bbm_size; 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci write_data_dma(nandc, FLASH_BUF_ACC + data_size, 20498c2ecf20Sopenharmony_ci oob_buf, oob_size, 0); 20508c2ecf20Sopenharmony_ci } 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci config_nand_cw_write(nandc); 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci data_buf += data_size; 20558c2ecf20Sopenharmony_ci oob_buf += oob_size; 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci ret = submit_descs(nandc); 20598c2ecf20Sopenharmony_ci if (ret) 20608c2ecf20Sopenharmony_ci dev_err(nandc->dev, "failure to write page\n"); 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci free_descs(nandc); 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci if (!ret) 20658c2ecf20Sopenharmony_ci ret = nand_prog_page_end_op(chip); 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci return ret; 20688c2ecf20Sopenharmony_ci} 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci/* implements ecc->write_page_raw() */ 20718c2ecf20Sopenharmony_cistatic int qcom_nandc_write_page_raw(struct nand_chip *chip, 20728c2ecf20Sopenharmony_ci const uint8_t *buf, int oob_required, 20738c2ecf20Sopenharmony_ci int page) 20748c2ecf20Sopenharmony_ci{ 20758c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 20768c2ecf20Sopenharmony_ci struct qcom_nand_host *host = to_qcom_nand_host(chip); 20778c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 20788c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 20798c2ecf20Sopenharmony_ci u8 *data_buf, *oob_buf; 20808c2ecf20Sopenharmony_ci int i, ret; 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci nand_prog_page_begin_op(chip, page, 0, NULL, 0); 20838c2ecf20Sopenharmony_ci clear_read_regs(nandc); 20848c2ecf20Sopenharmony_ci clear_bam_transaction(nandc); 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci data_buf = (u8 *)buf; 20878c2ecf20Sopenharmony_ci oob_buf = chip->oob_poi; 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_ci host->use_ecc = false; 20908c2ecf20Sopenharmony_ci update_rw_regs(host, ecc->steps, false); 20918c2ecf20Sopenharmony_ci config_nand_page_write(nandc); 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci for (i = 0; i < ecc->steps; i++) { 20948c2ecf20Sopenharmony_ci int data_size1, data_size2, oob_size1, oob_size2; 20958c2ecf20Sopenharmony_ci int reg_off = FLASH_BUF_ACC; 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_ci data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); 20988c2ecf20Sopenharmony_ci oob_size1 = host->bbm_size; 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_ci if (i == (ecc->steps - 1)) { 21018c2ecf20Sopenharmony_ci data_size2 = ecc->size - data_size1 - 21028c2ecf20Sopenharmony_ci ((ecc->steps - 1) << 2); 21038c2ecf20Sopenharmony_ci oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw + 21048c2ecf20Sopenharmony_ci host->spare_bytes; 21058c2ecf20Sopenharmony_ci } else { 21068c2ecf20Sopenharmony_ci data_size2 = host->cw_data - data_size1; 21078c2ecf20Sopenharmony_ci oob_size2 = host->ecc_bytes_hw + host->spare_bytes; 21088c2ecf20Sopenharmony_ci } 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci write_data_dma(nandc, reg_off, data_buf, data_size1, 21118c2ecf20Sopenharmony_ci NAND_BAM_NO_EOT); 21128c2ecf20Sopenharmony_ci reg_off += data_size1; 21138c2ecf20Sopenharmony_ci data_buf += data_size1; 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci write_data_dma(nandc, reg_off, oob_buf, oob_size1, 21168c2ecf20Sopenharmony_ci NAND_BAM_NO_EOT); 21178c2ecf20Sopenharmony_ci reg_off += oob_size1; 21188c2ecf20Sopenharmony_ci oob_buf += oob_size1; 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci write_data_dma(nandc, reg_off, data_buf, data_size2, 21218c2ecf20Sopenharmony_ci NAND_BAM_NO_EOT); 21228c2ecf20Sopenharmony_ci reg_off += data_size2; 21238c2ecf20Sopenharmony_ci data_buf += data_size2; 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); 21268c2ecf20Sopenharmony_ci oob_buf += oob_size2; 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci config_nand_cw_write(nandc); 21298c2ecf20Sopenharmony_ci } 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci ret = submit_descs(nandc); 21328c2ecf20Sopenharmony_ci if (ret) 21338c2ecf20Sopenharmony_ci dev_err(nandc->dev, "failure to write raw page\n"); 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci free_descs(nandc); 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci if (!ret) 21388c2ecf20Sopenharmony_ci ret = nand_prog_page_end_op(chip); 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_ci return ret; 21418c2ecf20Sopenharmony_ci} 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci/* 21448c2ecf20Sopenharmony_ci * implements ecc->write_oob() 21458c2ecf20Sopenharmony_ci * 21468c2ecf20Sopenharmony_ci * the NAND controller cannot write only data or only OOB within a codeword 21478c2ecf20Sopenharmony_ci * since ECC is calculated for the combined codeword. So update the OOB from 21488c2ecf20Sopenharmony_ci * chip->oob_poi, and pad the data area with OxFF before writing. 21498c2ecf20Sopenharmony_ci */ 21508c2ecf20Sopenharmony_cistatic int qcom_nandc_write_oob(struct nand_chip *chip, int page) 21518c2ecf20Sopenharmony_ci{ 21528c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 21538c2ecf20Sopenharmony_ci struct qcom_nand_host *host = to_qcom_nand_host(chip); 21548c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 21558c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 21568c2ecf20Sopenharmony_ci u8 *oob = chip->oob_poi; 21578c2ecf20Sopenharmony_ci int data_size, oob_size; 21588c2ecf20Sopenharmony_ci int ret; 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci host->use_ecc = true; 21618c2ecf20Sopenharmony_ci clear_bam_transaction(nandc); 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci /* calculate the data and oob size for the last codeword/step */ 21648c2ecf20Sopenharmony_ci data_size = ecc->size - ((ecc->steps - 1) << 2); 21658c2ecf20Sopenharmony_ci oob_size = mtd->oobavail; 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci memset(nandc->data_buffer, 0xff, host->cw_data); 21688c2ecf20Sopenharmony_ci /* override new oob content to last codeword */ 21698c2ecf20Sopenharmony_ci mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob, 21708c2ecf20Sopenharmony_ci 0, mtd->oobavail); 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci set_address(host, host->cw_size * (ecc->steps - 1), page); 21738c2ecf20Sopenharmony_ci update_rw_regs(host, 1, false); 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci config_nand_page_write(nandc); 21768c2ecf20Sopenharmony_ci write_data_dma(nandc, FLASH_BUF_ACC, 21778c2ecf20Sopenharmony_ci nandc->data_buffer, data_size + oob_size, 0); 21788c2ecf20Sopenharmony_ci config_nand_cw_write(nandc); 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci ret = submit_descs(nandc); 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ci free_descs(nandc); 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci if (ret) { 21858c2ecf20Sopenharmony_ci dev_err(nandc->dev, "failure to write oob\n"); 21868c2ecf20Sopenharmony_ci return -EIO; 21878c2ecf20Sopenharmony_ci } 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci return nand_prog_page_end_op(chip); 21908c2ecf20Sopenharmony_ci} 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_cistatic int qcom_nandc_block_bad(struct nand_chip *chip, loff_t ofs) 21938c2ecf20Sopenharmony_ci{ 21948c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 21958c2ecf20Sopenharmony_ci struct qcom_nand_host *host = to_qcom_nand_host(chip); 21968c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 21978c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 21988c2ecf20Sopenharmony_ci int page, ret, bbpos, bad = 0; 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci page = (int)(ofs >> chip->page_shift) & chip->pagemask; 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci /* 22038c2ecf20Sopenharmony_ci * configure registers for a raw sub page read, the address is set to 22048c2ecf20Sopenharmony_ci * the beginning of the last codeword, we don't care about reading ecc 22058c2ecf20Sopenharmony_ci * portion of oob. we just want the first few bytes from this codeword 22068c2ecf20Sopenharmony_ci * that contains the BBM 22078c2ecf20Sopenharmony_ci */ 22088c2ecf20Sopenharmony_ci host->use_ecc = false; 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_ci clear_bam_transaction(nandc); 22118c2ecf20Sopenharmony_ci ret = copy_last_cw(host, page); 22128c2ecf20Sopenharmony_ci if (ret) 22138c2ecf20Sopenharmony_ci goto err; 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci if (check_flash_errors(host, 1)) { 22168c2ecf20Sopenharmony_ci dev_warn(nandc->dev, "error when trying to read BBM\n"); 22178c2ecf20Sopenharmony_ci goto err; 22188c2ecf20Sopenharmony_ci } 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci bbpos = mtd->writesize - host->cw_size * (ecc->steps - 1); 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci bad = nandc->data_buffer[bbpos] != 0xff; 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci if (chip->options & NAND_BUSWIDTH_16) 22258c2ecf20Sopenharmony_ci bad = bad || (nandc->data_buffer[bbpos + 1] != 0xff); 22268c2ecf20Sopenharmony_cierr: 22278c2ecf20Sopenharmony_ci return bad; 22288c2ecf20Sopenharmony_ci} 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_cistatic int qcom_nandc_block_markbad(struct nand_chip *chip, loff_t ofs) 22318c2ecf20Sopenharmony_ci{ 22328c2ecf20Sopenharmony_ci struct qcom_nand_host *host = to_qcom_nand_host(chip); 22338c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 22348c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 22358c2ecf20Sopenharmony_ci int page, ret; 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci clear_read_regs(nandc); 22388c2ecf20Sopenharmony_ci clear_bam_transaction(nandc); 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci /* 22418c2ecf20Sopenharmony_ci * to mark the BBM as bad, we flash the entire last codeword with 0s. 22428c2ecf20Sopenharmony_ci * we don't care about the rest of the content in the codeword since 22438c2ecf20Sopenharmony_ci * we aren't going to use this block again 22448c2ecf20Sopenharmony_ci */ 22458c2ecf20Sopenharmony_ci memset(nandc->data_buffer, 0x00, host->cw_size); 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci page = (int)(ofs >> chip->page_shift) & chip->pagemask; 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci /* prepare write */ 22508c2ecf20Sopenharmony_ci host->use_ecc = false; 22518c2ecf20Sopenharmony_ci set_address(host, host->cw_size * (ecc->steps - 1), page); 22528c2ecf20Sopenharmony_ci update_rw_regs(host, 1, false); 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci config_nand_page_write(nandc); 22558c2ecf20Sopenharmony_ci write_data_dma(nandc, FLASH_BUF_ACC, 22568c2ecf20Sopenharmony_ci nandc->data_buffer, host->cw_size, 0); 22578c2ecf20Sopenharmony_ci config_nand_cw_write(nandc); 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci ret = submit_descs(nandc); 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci free_descs(nandc); 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci if (ret) { 22648c2ecf20Sopenharmony_ci dev_err(nandc->dev, "failure to update BBM\n"); 22658c2ecf20Sopenharmony_ci return -EIO; 22668c2ecf20Sopenharmony_ci } 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci return nand_prog_page_end_op(chip); 22698c2ecf20Sopenharmony_ci} 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci/* 22728c2ecf20Sopenharmony_ci * the three functions below implement chip->legacy.read_byte(), 22738c2ecf20Sopenharmony_ci * chip->legacy.read_buf() and chip->legacy.write_buf() respectively. these 22748c2ecf20Sopenharmony_ci * aren't used for reading/writing page data, they are used for smaller data 22758c2ecf20Sopenharmony_ci * like reading id, status etc 22768c2ecf20Sopenharmony_ci */ 22778c2ecf20Sopenharmony_cistatic uint8_t qcom_nandc_read_byte(struct nand_chip *chip) 22788c2ecf20Sopenharmony_ci{ 22798c2ecf20Sopenharmony_ci struct qcom_nand_host *host = to_qcom_nand_host(chip); 22808c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 22818c2ecf20Sopenharmony_ci u8 *buf = nandc->data_buffer; 22828c2ecf20Sopenharmony_ci u8 ret = 0x0; 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci if (host->last_command == NAND_CMD_STATUS) { 22858c2ecf20Sopenharmony_ci ret = host->status; 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ci host->status = NAND_STATUS_READY | NAND_STATUS_WP; 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ci return ret; 22908c2ecf20Sopenharmony_ci } 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci if (nandc->buf_start < nandc->buf_count) 22938c2ecf20Sopenharmony_ci ret = buf[nandc->buf_start++]; 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci return ret; 22968c2ecf20Sopenharmony_ci} 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_cistatic void qcom_nandc_read_buf(struct nand_chip *chip, uint8_t *buf, int len) 22998c2ecf20Sopenharmony_ci{ 23008c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 23018c2ecf20Sopenharmony_ci int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start); 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci memcpy(buf, nandc->data_buffer + nandc->buf_start, real_len); 23048c2ecf20Sopenharmony_ci nandc->buf_start += real_len; 23058c2ecf20Sopenharmony_ci} 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_cistatic void qcom_nandc_write_buf(struct nand_chip *chip, const uint8_t *buf, 23088c2ecf20Sopenharmony_ci int len) 23098c2ecf20Sopenharmony_ci{ 23108c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 23118c2ecf20Sopenharmony_ci int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start); 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci memcpy(nandc->data_buffer + nandc->buf_start, buf, real_len); 23148c2ecf20Sopenharmony_ci 23158c2ecf20Sopenharmony_ci nandc->buf_start += real_len; 23168c2ecf20Sopenharmony_ci} 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_ci/* we support only one external chip for now */ 23198c2ecf20Sopenharmony_cistatic void qcom_nandc_select_chip(struct nand_chip *chip, int chipnr) 23208c2ecf20Sopenharmony_ci{ 23218c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci if (chipnr <= 0) 23248c2ecf20Sopenharmony_ci return; 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ci dev_warn(nandc->dev, "invalid chip select\n"); 23278c2ecf20Sopenharmony_ci} 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci/* 23308c2ecf20Sopenharmony_ci * NAND controller page layout info 23318c2ecf20Sopenharmony_ci * 23328c2ecf20Sopenharmony_ci * Layout with ECC enabled: 23338c2ecf20Sopenharmony_ci * 23348c2ecf20Sopenharmony_ci * |----------------------| |---------------------------------| 23358c2ecf20Sopenharmony_ci * | xx.......yy| | *********xx.......yy| 23368c2ecf20Sopenharmony_ci * | DATA xx..ECC..yy| | DATA **SPARE**xx..ECC..yy| 23378c2ecf20Sopenharmony_ci * | (516) xx.......yy| | (516-n*4) **(n*4)**xx.......yy| 23388c2ecf20Sopenharmony_ci * | xx.......yy| | *********xx.......yy| 23398c2ecf20Sopenharmony_ci * |----------------------| |---------------------------------| 23408c2ecf20Sopenharmony_ci * codeword 1,2..n-1 codeword n 23418c2ecf20Sopenharmony_ci * <---(528/532 Bytes)--> <-------(528/532 Bytes)---------> 23428c2ecf20Sopenharmony_ci * 23438c2ecf20Sopenharmony_ci * n = Number of codewords in the page 23448c2ecf20Sopenharmony_ci * . = ECC bytes 23458c2ecf20Sopenharmony_ci * * = Spare/free bytes 23468c2ecf20Sopenharmony_ci * x = Unused byte(s) 23478c2ecf20Sopenharmony_ci * y = Reserved byte(s) 23488c2ecf20Sopenharmony_ci * 23498c2ecf20Sopenharmony_ci * 2K page: n = 4, spare = 16 bytes 23508c2ecf20Sopenharmony_ci * 4K page: n = 8, spare = 32 bytes 23518c2ecf20Sopenharmony_ci * 8K page: n = 16, spare = 64 bytes 23528c2ecf20Sopenharmony_ci * 23538c2ecf20Sopenharmony_ci * the qcom nand controller operates at a sub page/codeword level. each 23548c2ecf20Sopenharmony_ci * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively. 23558c2ecf20Sopenharmony_ci * the number of ECC bytes vary based on the ECC strength and the bus width. 23568c2ecf20Sopenharmony_ci * 23578c2ecf20Sopenharmony_ci * the first n - 1 codewords contains 516 bytes of user data, the remaining 23588c2ecf20Sopenharmony_ci * 12/16 bytes consist of ECC and reserved data. The nth codeword contains 23598c2ecf20Sopenharmony_ci * both user data and spare(oobavail) bytes that sum up to 516 bytes. 23608c2ecf20Sopenharmony_ci * 23618c2ecf20Sopenharmony_ci * When we access a page with ECC enabled, the reserved bytes(s) are not 23628c2ecf20Sopenharmony_ci * accessible at all. When reading, we fill up these unreadable positions 23638c2ecf20Sopenharmony_ci * with 0xffs. When writing, the controller skips writing the inaccessible 23648c2ecf20Sopenharmony_ci * bytes. 23658c2ecf20Sopenharmony_ci * 23668c2ecf20Sopenharmony_ci * Layout with ECC disabled: 23678c2ecf20Sopenharmony_ci * 23688c2ecf20Sopenharmony_ci * |------------------------------| |---------------------------------------| 23698c2ecf20Sopenharmony_ci * | yy xx.......| | bb *********xx.......| 23708c2ecf20Sopenharmony_ci * | DATA1 yy DATA2 xx..ECC..| | DATA1 bb DATA2 **SPARE**xx..ECC..| 23718c2ecf20Sopenharmony_ci * | (size1) yy (size2) xx.......| | (size1) bb (size2) **(n*4)**xx.......| 23728c2ecf20Sopenharmony_ci * | yy xx.......| | bb *********xx.......| 23738c2ecf20Sopenharmony_ci * |------------------------------| |---------------------------------------| 23748c2ecf20Sopenharmony_ci * codeword 1,2..n-1 codeword n 23758c2ecf20Sopenharmony_ci * <-------(528/532 Bytes)------> <-----------(528/532 Bytes)-----------> 23768c2ecf20Sopenharmony_ci * 23778c2ecf20Sopenharmony_ci * n = Number of codewords in the page 23788c2ecf20Sopenharmony_ci * . = ECC bytes 23798c2ecf20Sopenharmony_ci * * = Spare/free bytes 23808c2ecf20Sopenharmony_ci * x = Unused byte(s) 23818c2ecf20Sopenharmony_ci * y = Dummy Bad Bock byte(s) 23828c2ecf20Sopenharmony_ci * b = Real Bad Block byte(s) 23838c2ecf20Sopenharmony_ci * size1/size2 = function of codeword size and 'n' 23848c2ecf20Sopenharmony_ci * 23858c2ecf20Sopenharmony_ci * when the ECC block is disabled, one reserved byte (or two for 16 bit bus 23868c2ecf20Sopenharmony_ci * width) is now accessible. For the first n - 1 codewords, these are dummy Bad 23878c2ecf20Sopenharmony_ci * Block Markers. In the last codeword, this position contains the real BBM 23888c2ecf20Sopenharmony_ci * 23898c2ecf20Sopenharmony_ci * In order to have a consistent layout between RAW and ECC modes, we assume 23908c2ecf20Sopenharmony_ci * the following OOB layout arrangement: 23918c2ecf20Sopenharmony_ci * 23928c2ecf20Sopenharmony_ci * |-----------| |--------------------| 23938c2ecf20Sopenharmony_ci * |yyxx.......| |bb*********xx.......| 23948c2ecf20Sopenharmony_ci * |yyxx..ECC..| |bb*FREEOOB*xx..ECC..| 23958c2ecf20Sopenharmony_ci * |yyxx.......| |bb*********xx.......| 23968c2ecf20Sopenharmony_ci * |yyxx.......| |bb*********xx.......| 23978c2ecf20Sopenharmony_ci * |-----------| |--------------------| 23988c2ecf20Sopenharmony_ci * first n - 1 nth OOB region 23998c2ecf20Sopenharmony_ci * OOB regions 24008c2ecf20Sopenharmony_ci * 24018c2ecf20Sopenharmony_ci * n = Number of codewords in the page 24028c2ecf20Sopenharmony_ci * . = ECC bytes 24038c2ecf20Sopenharmony_ci * * = FREE OOB bytes 24048c2ecf20Sopenharmony_ci * y = Dummy bad block byte(s) (inaccessible when ECC enabled) 24058c2ecf20Sopenharmony_ci * x = Unused byte(s) 24068c2ecf20Sopenharmony_ci * b = Real bad block byte(s) (inaccessible when ECC enabled) 24078c2ecf20Sopenharmony_ci * 24088c2ecf20Sopenharmony_ci * This layout is read as is when ECC is disabled. When ECC is enabled, the 24098c2ecf20Sopenharmony_ci * inaccessible Bad Block byte(s) are ignored when we write to a page/oob, 24108c2ecf20Sopenharmony_ci * and assumed as 0xffs when we read a page/oob. The ECC, unused and 24118c2ecf20Sopenharmony_ci * dummy/real bad block bytes are grouped as ecc bytes (i.e, ecc->bytes is 24128c2ecf20Sopenharmony_ci * the sum of the three). 24138c2ecf20Sopenharmony_ci */ 24148c2ecf20Sopenharmony_cistatic int qcom_nand_ooblayout_ecc(struct mtd_info *mtd, int section, 24158c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 24168c2ecf20Sopenharmony_ci{ 24178c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 24188c2ecf20Sopenharmony_ci struct qcom_nand_host *host = to_qcom_nand_host(chip); 24198c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci if (section > 1) 24228c2ecf20Sopenharmony_ci return -ERANGE; 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_ci if (!section) { 24258c2ecf20Sopenharmony_ci oobregion->length = (ecc->bytes * (ecc->steps - 1)) + 24268c2ecf20Sopenharmony_ci host->bbm_size; 24278c2ecf20Sopenharmony_ci oobregion->offset = 0; 24288c2ecf20Sopenharmony_ci } else { 24298c2ecf20Sopenharmony_ci oobregion->length = host->ecc_bytes_hw + host->spare_bytes; 24308c2ecf20Sopenharmony_ci oobregion->offset = mtd->oobsize - oobregion->length; 24318c2ecf20Sopenharmony_ci } 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci return 0; 24348c2ecf20Sopenharmony_ci} 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_cistatic int qcom_nand_ooblayout_free(struct mtd_info *mtd, int section, 24378c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 24388c2ecf20Sopenharmony_ci{ 24398c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 24408c2ecf20Sopenharmony_ci struct qcom_nand_host *host = to_qcom_nand_host(chip); 24418c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ci if (section) 24448c2ecf20Sopenharmony_ci return -ERANGE; 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci oobregion->length = ecc->steps * 4; 24478c2ecf20Sopenharmony_ci oobregion->offset = ((ecc->steps - 1) * ecc->bytes) + host->bbm_size; 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_ci return 0; 24508c2ecf20Sopenharmony_ci} 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops qcom_nand_ooblayout_ops = { 24538c2ecf20Sopenharmony_ci .ecc = qcom_nand_ooblayout_ecc, 24548c2ecf20Sopenharmony_ci .free = qcom_nand_ooblayout_free, 24558c2ecf20Sopenharmony_ci}; 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_cistatic int 24588c2ecf20Sopenharmony_ciqcom_nandc_calc_ecc_bytes(int step_size, int strength) 24598c2ecf20Sopenharmony_ci{ 24608c2ecf20Sopenharmony_ci return strength == 4 ? 12 : 16; 24618c2ecf20Sopenharmony_ci} 24628c2ecf20Sopenharmony_ciNAND_ECC_CAPS_SINGLE(qcom_nandc_ecc_caps, qcom_nandc_calc_ecc_bytes, 24638c2ecf20Sopenharmony_ci NANDC_STEP_SIZE, 4, 8); 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_cistatic int qcom_nand_attach_chip(struct nand_chip *chip) 24668c2ecf20Sopenharmony_ci{ 24678c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 24688c2ecf20Sopenharmony_ci struct qcom_nand_host *host = to_qcom_nand_host(chip); 24698c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 24708c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); 24718c2ecf20Sopenharmony_ci int cwperpage, bad_block_byte, ret; 24728c2ecf20Sopenharmony_ci bool wide_bus; 24738c2ecf20Sopenharmony_ci int ecc_mode = 1; 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_ci /* controller only supports 512 bytes data steps */ 24768c2ecf20Sopenharmony_ci ecc->size = NANDC_STEP_SIZE; 24778c2ecf20Sopenharmony_ci wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; 24788c2ecf20Sopenharmony_ci cwperpage = mtd->writesize / NANDC_STEP_SIZE; 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_ci /* 24818c2ecf20Sopenharmony_ci * Each CW has 4 available OOB bytes which will be protected with ECC 24828c2ecf20Sopenharmony_ci * so remaining bytes can be used for ECC. 24838c2ecf20Sopenharmony_ci */ 24848c2ecf20Sopenharmony_ci ret = nand_ecc_choose_conf(chip, &qcom_nandc_ecc_caps, 24858c2ecf20Sopenharmony_ci mtd->oobsize - (cwperpage * 4)); 24868c2ecf20Sopenharmony_ci if (ret) { 24878c2ecf20Sopenharmony_ci dev_err(nandc->dev, "No valid ECC settings possible\n"); 24888c2ecf20Sopenharmony_ci return ret; 24898c2ecf20Sopenharmony_ci } 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci if (ecc->strength >= 8) { 24928c2ecf20Sopenharmony_ci /* 8 bit ECC defaults to BCH ECC on all platforms */ 24938c2ecf20Sopenharmony_ci host->bch_enabled = true; 24948c2ecf20Sopenharmony_ci ecc_mode = 1; 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_ci if (wide_bus) { 24978c2ecf20Sopenharmony_ci host->ecc_bytes_hw = 14; 24988c2ecf20Sopenharmony_ci host->spare_bytes = 0; 24998c2ecf20Sopenharmony_ci host->bbm_size = 2; 25008c2ecf20Sopenharmony_ci } else { 25018c2ecf20Sopenharmony_ci host->ecc_bytes_hw = 13; 25028c2ecf20Sopenharmony_ci host->spare_bytes = 2; 25038c2ecf20Sopenharmony_ci host->bbm_size = 1; 25048c2ecf20Sopenharmony_ci } 25058c2ecf20Sopenharmony_ci } else { 25068c2ecf20Sopenharmony_ci /* 25078c2ecf20Sopenharmony_ci * if the controller supports BCH for 4 bit ECC, the controller 25088c2ecf20Sopenharmony_ci * uses lesser bytes for ECC. If RS is used, the ECC bytes is 25098c2ecf20Sopenharmony_ci * always 10 bytes 25108c2ecf20Sopenharmony_ci */ 25118c2ecf20Sopenharmony_ci if (nandc->props->ecc_modes & ECC_BCH_4BIT) { 25128c2ecf20Sopenharmony_ci /* BCH */ 25138c2ecf20Sopenharmony_ci host->bch_enabled = true; 25148c2ecf20Sopenharmony_ci ecc_mode = 0; 25158c2ecf20Sopenharmony_ci 25168c2ecf20Sopenharmony_ci if (wide_bus) { 25178c2ecf20Sopenharmony_ci host->ecc_bytes_hw = 8; 25188c2ecf20Sopenharmony_ci host->spare_bytes = 2; 25198c2ecf20Sopenharmony_ci host->bbm_size = 2; 25208c2ecf20Sopenharmony_ci } else { 25218c2ecf20Sopenharmony_ci host->ecc_bytes_hw = 7; 25228c2ecf20Sopenharmony_ci host->spare_bytes = 4; 25238c2ecf20Sopenharmony_ci host->bbm_size = 1; 25248c2ecf20Sopenharmony_ci } 25258c2ecf20Sopenharmony_ci } else { 25268c2ecf20Sopenharmony_ci /* RS */ 25278c2ecf20Sopenharmony_ci host->ecc_bytes_hw = 10; 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci if (wide_bus) { 25308c2ecf20Sopenharmony_ci host->spare_bytes = 0; 25318c2ecf20Sopenharmony_ci host->bbm_size = 2; 25328c2ecf20Sopenharmony_ci } else { 25338c2ecf20Sopenharmony_ci host->spare_bytes = 1; 25348c2ecf20Sopenharmony_ci host->bbm_size = 1; 25358c2ecf20Sopenharmony_ci } 25368c2ecf20Sopenharmony_ci } 25378c2ecf20Sopenharmony_ci } 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_ci /* 25408c2ecf20Sopenharmony_ci * we consider ecc->bytes as the sum of all the non-data content in a 25418c2ecf20Sopenharmony_ci * step. It gives us a clean representation of the oob area (even if 25428c2ecf20Sopenharmony_ci * all the bytes aren't used for ECC).It is always 16 bytes for 8 bit 25438c2ecf20Sopenharmony_ci * ECC and 12 bytes for 4 bit ECC 25448c2ecf20Sopenharmony_ci */ 25458c2ecf20Sopenharmony_ci ecc->bytes = host->ecc_bytes_hw + host->spare_bytes + host->bbm_size; 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci ecc->read_page = qcom_nandc_read_page; 25488c2ecf20Sopenharmony_ci ecc->read_page_raw = qcom_nandc_read_page_raw; 25498c2ecf20Sopenharmony_ci ecc->read_oob = qcom_nandc_read_oob; 25508c2ecf20Sopenharmony_ci ecc->write_page = qcom_nandc_write_page; 25518c2ecf20Sopenharmony_ci ecc->write_page_raw = qcom_nandc_write_page_raw; 25528c2ecf20Sopenharmony_ci ecc->write_oob = qcom_nandc_write_oob; 25538c2ecf20Sopenharmony_ci 25548c2ecf20Sopenharmony_ci ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops); 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage, 25598c2ecf20Sopenharmony_ci cwperpage); 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci /* 25628c2ecf20Sopenharmony_ci * DATA_UD_BYTES varies based on whether the read/write command protects 25638c2ecf20Sopenharmony_ci * spare data with ECC too. We protect spare data by default, so we set 25648c2ecf20Sopenharmony_ci * it to main + spare data, which are 512 and 4 bytes respectively. 25658c2ecf20Sopenharmony_ci */ 25668c2ecf20Sopenharmony_ci host->cw_data = 516; 25678c2ecf20Sopenharmony_ci 25688c2ecf20Sopenharmony_ci /* 25698c2ecf20Sopenharmony_ci * total bytes in a step, either 528 bytes for 4 bit ECC, or 532 bytes 25708c2ecf20Sopenharmony_ci * for 8 bit ECC 25718c2ecf20Sopenharmony_ci */ 25728c2ecf20Sopenharmony_ci host->cw_size = host->cw_data + ecc->bytes; 25738c2ecf20Sopenharmony_ci bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1; 25748c2ecf20Sopenharmony_ci 25758c2ecf20Sopenharmony_ci host->cfg0 = (cwperpage - 1) << CW_PER_PAGE 25768c2ecf20Sopenharmony_ci | host->cw_data << UD_SIZE_BYTES 25778c2ecf20Sopenharmony_ci | 0 << DISABLE_STATUS_AFTER_WRITE 25788c2ecf20Sopenharmony_ci | 5 << NUM_ADDR_CYCLES 25798c2ecf20Sopenharmony_ci | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_RS 25808c2ecf20Sopenharmony_ci | 0 << STATUS_BFR_READ 25818c2ecf20Sopenharmony_ci | 1 << SET_RD_MODE_AFTER_STATUS 25828c2ecf20Sopenharmony_ci | host->spare_bytes << SPARE_SIZE_BYTES; 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_ci host->cfg1 = 7 << NAND_RECOVERY_CYCLES 25858c2ecf20Sopenharmony_ci | 0 << CS_ACTIVE_BSY 25868c2ecf20Sopenharmony_ci | bad_block_byte << BAD_BLOCK_BYTE_NUM 25878c2ecf20Sopenharmony_ci | 0 << BAD_BLOCK_IN_SPARE_AREA 25888c2ecf20Sopenharmony_ci | 2 << WR_RD_BSY_GAP 25898c2ecf20Sopenharmony_ci | wide_bus << WIDE_FLASH 25908c2ecf20Sopenharmony_ci | host->bch_enabled << ENABLE_BCH_ECC; 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci host->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE 25938c2ecf20Sopenharmony_ci | host->cw_size << UD_SIZE_BYTES 25948c2ecf20Sopenharmony_ci | 5 << NUM_ADDR_CYCLES 25958c2ecf20Sopenharmony_ci | 0 << SPARE_SIZE_BYTES; 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci host->cfg1_raw = 7 << NAND_RECOVERY_CYCLES 25988c2ecf20Sopenharmony_ci | 0 << CS_ACTIVE_BSY 25998c2ecf20Sopenharmony_ci | 17 << BAD_BLOCK_BYTE_NUM 26008c2ecf20Sopenharmony_ci | 1 << BAD_BLOCK_IN_SPARE_AREA 26018c2ecf20Sopenharmony_ci | 2 << WR_RD_BSY_GAP 26028c2ecf20Sopenharmony_ci | wide_bus << WIDE_FLASH 26038c2ecf20Sopenharmony_ci | 1 << DEV0_CFG1_ECC_DISABLE; 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE 26068c2ecf20Sopenharmony_ci | 0 << ECC_SW_RESET 26078c2ecf20Sopenharmony_ci | host->cw_data << ECC_NUM_DATA_BYTES 26088c2ecf20Sopenharmony_ci | 1 << ECC_FORCE_CLK_OPEN 26098c2ecf20Sopenharmony_ci | ecc_mode << ECC_MODE 26108c2ecf20Sopenharmony_ci | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH; 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci host->ecc_buf_cfg = 0x203 << NUM_STEPS; 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci host->clrflashstatus = FS_READY_BSY_N; 26158c2ecf20Sopenharmony_ci host->clrreadstatus = 0xc0; 26168c2ecf20Sopenharmony_ci nandc->regs->erased_cw_detect_cfg_clr = 26178c2ecf20Sopenharmony_ci cpu_to_le32(CLR_ERASED_PAGE_DET); 26188c2ecf20Sopenharmony_ci nandc->regs->erased_cw_detect_cfg_set = 26198c2ecf20Sopenharmony_ci cpu_to_le32(SET_ERASED_PAGE_DET); 26208c2ecf20Sopenharmony_ci 26218c2ecf20Sopenharmony_ci dev_dbg(nandc->dev, 26228c2ecf20Sopenharmony_ci "cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n", 26238c2ecf20Sopenharmony_ci host->cfg0, host->cfg1, host->ecc_buf_cfg, host->ecc_bch_cfg, 26248c2ecf20Sopenharmony_ci host->cw_size, host->cw_data, ecc->strength, ecc->bytes, 26258c2ecf20Sopenharmony_ci cwperpage); 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_ci return 0; 26288c2ecf20Sopenharmony_ci} 26298c2ecf20Sopenharmony_ci 26308c2ecf20Sopenharmony_cistatic const struct nand_controller_ops qcom_nandc_ops = { 26318c2ecf20Sopenharmony_ci .attach_chip = qcom_nand_attach_chip, 26328c2ecf20Sopenharmony_ci}; 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_cistatic void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) 26358c2ecf20Sopenharmony_ci{ 26368c2ecf20Sopenharmony_ci if (nandc->props->is_bam) { 26378c2ecf20Sopenharmony_ci if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) 26388c2ecf20Sopenharmony_ci dma_unmap_single(nandc->dev, nandc->reg_read_dma, 26398c2ecf20Sopenharmony_ci MAX_REG_RD * 26408c2ecf20Sopenharmony_ci sizeof(*nandc->reg_read_buf), 26418c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ci if (nandc->tx_chan) 26448c2ecf20Sopenharmony_ci dma_release_channel(nandc->tx_chan); 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci if (nandc->rx_chan) 26478c2ecf20Sopenharmony_ci dma_release_channel(nandc->rx_chan); 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_ci if (nandc->cmd_chan) 26508c2ecf20Sopenharmony_ci dma_release_channel(nandc->cmd_chan); 26518c2ecf20Sopenharmony_ci } else { 26528c2ecf20Sopenharmony_ci if (nandc->chan) 26538c2ecf20Sopenharmony_ci dma_release_channel(nandc->chan); 26548c2ecf20Sopenharmony_ci } 26558c2ecf20Sopenharmony_ci} 26568c2ecf20Sopenharmony_ci 26578c2ecf20Sopenharmony_cistatic int qcom_nandc_alloc(struct qcom_nand_controller *nandc) 26588c2ecf20Sopenharmony_ci{ 26598c2ecf20Sopenharmony_ci int ret; 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_ci ret = dma_set_coherent_mask(nandc->dev, DMA_BIT_MASK(32)); 26628c2ecf20Sopenharmony_ci if (ret) { 26638c2ecf20Sopenharmony_ci dev_err(nandc->dev, "failed to set DMA mask\n"); 26648c2ecf20Sopenharmony_ci return ret; 26658c2ecf20Sopenharmony_ci } 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_ci /* 26688c2ecf20Sopenharmony_ci * we use the internal buffer for reading ONFI params, reading small 26698c2ecf20Sopenharmony_ci * data like ID and status, and preforming read-copy-write operations 26708c2ecf20Sopenharmony_ci * when writing to a codeword partially. 532 is the maximum possible 26718c2ecf20Sopenharmony_ci * size of a codeword for our nand controller 26728c2ecf20Sopenharmony_ci */ 26738c2ecf20Sopenharmony_ci nandc->buf_size = 532; 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_ci nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size, 26768c2ecf20Sopenharmony_ci GFP_KERNEL); 26778c2ecf20Sopenharmony_ci if (!nandc->data_buffer) 26788c2ecf20Sopenharmony_ci return -ENOMEM; 26798c2ecf20Sopenharmony_ci 26808c2ecf20Sopenharmony_ci nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs), 26818c2ecf20Sopenharmony_ci GFP_KERNEL); 26828c2ecf20Sopenharmony_ci if (!nandc->regs) 26838c2ecf20Sopenharmony_ci return -ENOMEM; 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ci nandc->reg_read_buf = devm_kcalloc(nandc->dev, 26868c2ecf20Sopenharmony_ci MAX_REG_RD, sizeof(*nandc->reg_read_buf), 26878c2ecf20Sopenharmony_ci GFP_KERNEL); 26888c2ecf20Sopenharmony_ci if (!nandc->reg_read_buf) 26898c2ecf20Sopenharmony_ci return -ENOMEM; 26908c2ecf20Sopenharmony_ci 26918c2ecf20Sopenharmony_ci if (nandc->props->is_bam) { 26928c2ecf20Sopenharmony_ci nandc->reg_read_dma = 26938c2ecf20Sopenharmony_ci dma_map_single(nandc->dev, nandc->reg_read_buf, 26948c2ecf20Sopenharmony_ci MAX_REG_RD * 26958c2ecf20Sopenharmony_ci sizeof(*nandc->reg_read_buf), 26968c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 26978c2ecf20Sopenharmony_ci if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) { 26988c2ecf20Sopenharmony_ci dev_err(nandc->dev, "failed to DMA MAP reg buffer\n"); 26998c2ecf20Sopenharmony_ci return -EIO; 27008c2ecf20Sopenharmony_ci } 27018c2ecf20Sopenharmony_ci 27028c2ecf20Sopenharmony_ci nandc->tx_chan = dma_request_chan(nandc->dev, "tx"); 27038c2ecf20Sopenharmony_ci if (IS_ERR(nandc->tx_chan)) { 27048c2ecf20Sopenharmony_ci ret = PTR_ERR(nandc->tx_chan); 27058c2ecf20Sopenharmony_ci nandc->tx_chan = NULL; 27068c2ecf20Sopenharmony_ci dev_err_probe(nandc->dev, ret, 27078c2ecf20Sopenharmony_ci "tx DMA channel request failed\n"); 27088c2ecf20Sopenharmony_ci goto unalloc; 27098c2ecf20Sopenharmony_ci } 27108c2ecf20Sopenharmony_ci 27118c2ecf20Sopenharmony_ci nandc->rx_chan = dma_request_chan(nandc->dev, "rx"); 27128c2ecf20Sopenharmony_ci if (IS_ERR(nandc->rx_chan)) { 27138c2ecf20Sopenharmony_ci ret = PTR_ERR(nandc->rx_chan); 27148c2ecf20Sopenharmony_ci nandc->rx_chan = NULL; 27158c2ecf20Sopenharmony_ci dev_err_probe(nandc->dev, ret, 27168c2ecf20Sopenharmony_ci "rx DMA channel request failed\n"); 27178c2ecf20Sopenharmony_ci goto unalloc; 27188c2ecf20Sopenharmony_ci } 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci nandc->cmd_chan = dma_request_chan(nandc->dev, "cmd"); 27218c2ecf20Sopenharmony_ci if (IS_ERR(nandc->cmd_chan)) { 27228c2ecf20Sopenharmony_ci ret = PTR_ERR(nandc->cmd_chan); 27238c2ecf20Sopenharmony_ci nandc->cmd_chan = NULL; 27248c2ecf20Sopenharmony_ci dev_err_probe(nandc->dev, ret, 27258c2ecf20Sopenharmony_ci "cmd DMA channel request failed\n"); 27268c2ecf20Sopenharmony_ci goto unalloc; 27278c2ecf20Sopenharmony_ci } 27288c2ecf20Sopenharmony_ci 27298c2ecf20Sopenharmony_ci /* 27308c2ecf20Sopenharmony_ci * Initially allocate BAM transaction to read ONFI param page. 27318c2ecf20Sopenharmony_ci * After detecting all the devices, this BAM transaction will 27328c2ecf20Sopenharmony_ci * be freed and the next BAM tranasction will be allocated with 27338c2ecf20Sopenharmony_ci * maximum codeword size 27348c2ecf20Sopenharmony_ci */ 27358c2ecf20Sopenharmony_ci nandc->max_cwperpage = 1; 27368c2ecf20Sopenharmony_ci nandc->bam_txn = alloc_bam_transaction(nandc); 27378c2ecf20Sopenharmony_ci if (!nandc->bam_txn) { 27388c2ecf20Sopenharmony_ci dev_err(nandc->dev, 27398c2ecf20Sopenharmony_ci "failed to allocate bam transaction\n"); 27408c2ecf20Sopenharmony_ci ret = -ENOMEM; 27418c2ecf20Sopenharmony_ci goto unalloc; 27428c2ecf20Sopenharmony_ci } 27438c2ecf20Sopenharmony_ci } else { 27448c2ecf20Sopenharmony_ci nandc->chan = dma_request_chan(nandc->dev, "rxtx"); 27458c2ecf20Sopenharmony_ci if (IS_ERR(nandc->chan)) { 27468c2ecf20Sopenharmony_ci ret = PTR_ERR(nandc->chan); 27478c2ecf20Sopenharmony_ci nandc->chan = NULL; 27488c2ecf20Sopenharmony_ci dev_err_probe(nandc->dev, ret, 27498c2ecf20Sopenharmony_ci "rxtx DMA channel request failed\n"); 27508c2ecf20Sopenharmony_ci return ret; 27518c2ecf20Sopenharmony_ci } 27528c2ecf20Sopenharmony_ci } 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&nandc->desc_list); 27558c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&nandc->host_list); 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci nand_controller_init(&nandc->controller); 27588c2ecf20Sopenharmony_ci nandc->controller.ops = &qcom_nandc_ops; 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_ci return 0; 27618c2ecf20Sopenharmony_ciunalloc: 27628c2ecf20Sopenharmony_ci qcom_nandc_unalloc(nandc); 27638c2ecf20Sopenharmony_ci return ret; 27648c2ecf20Sopenharmony_ci} 27658c2ecf20Sopenharmony_ci 27668c2ecf20Sopenharmony_ci/* one time setup of a few nand controller registers */ 27678c2ecf20Sopenharmony_cistatic int qcom_nandc_setup(struct qcom_nand_controller *nandc) 27688c2ecf20Sopenharmony_ci{ 27698c2ecf20Sopenharmony_ci u32 nand_ctrl; 27708c2ecf20Sopenharmony_ci 27718c2ecf20Sopenharmony_ci /* kill onenand */ 27728c2ecf20Sopenharmony_ci if (!nandc->props->is_qpic) 27738c2ecf20Sopenharmony_ci nandc_write(nandc, SFLASHC_BURST_CFG, 0); 27748c2ecf20Sopenharmony_ci nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD), 27758c2ecf20Sopenharmony_ci NAND_DEV_CMD_VLD_VAL); 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci /* enable ADM or BAM DMA */ 27788c2ecf20Sopenharmony_ci if (nandc->props->is_bam) { 27798c2ecf20Sopenharmony_ci nand_ctrl = nandc_read(nandc, NAND_CTRL); 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_ci /* 27828c2ecf20Sopenharmony_ci *NAND_CTRL is an operational registers, and CPU 27838c2ecf20Sopenharmony_ci * access to operational registers are read only 27848c2ecf20Sopenharmony_ci * in BAM mode. So update the NAND_CTRL register 27858c2ecf20Sopenharmony_ci * only if it is not in BAM mode. In most cases BAM 27868c2ecf20Sopenharmony_ci * mode will be enabled in bootloader 27878c2ecf20Sopenharmony_ci */ 27888c2ecf20Sopenharmony_ci if (!(nand_ctrl & BAM_MODE_EN)) 27898c2ecf20Sopenharmony_ci nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN); 27908c2ecf20Sopenharmony_ci } else { 27918c2ecf20Sopenharmony_ci nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN); 27928c2ecf20Sopenharmony_ci } 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_ci /* save the original values of these registers */ 27958c2ecf20Sopenharmony_ci nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1)); 27968c2ecf20Sopenharmony_ci nandc->vld = NAND_DEV_CMD_VLD_VAL; 27978c2ecf20Sopenharmony_ci 27988c2ecf20Sopenharmony_ci return 0; 27998c2ecf20Sopenharmony_ci} 28008c2ecf20Sopenharmony_ci 28018c2ecf20Sopenharmony_cistatic int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc, 28028c2ecf20Sopenharmony_ci struct qcom_nand_host *host, 28038c2ecf20Sopenharmony_ci struct device_node *dn) 28048c2ecf20Sopenharmony_ci{ 28058c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->chip; 28068c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 28078c2ecf20Sopenharmony_ci struct device *dev = nandc->dev; 28088c2ecf20Sopenharmony_ci int ret; 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci ret = of_property_read_u32(dn, "reg", &host->cs); 28118c2ecf20Sopenharmony_ci if (ret) { 28128c2ecf20Sopenharmony_ci dev_err(dev, "can't get chip-select\n"); 28138c2ecf20Sopenharmony_ci return -ENXIO; 28148c2ecf20Sopenharmony_ci } 28158c2ecf20Sopenharmony_ci 28168c2ecf20Sopenharmony_ci nand_set_flash_node(chip, dn); 28178c2ecf20Sopenharmony_ci mtd->name = devm_kasprintf(dev, GFP_KERNEL, "qcom_nand.%d", host->cs); 28188c2ecf20Sopenharmony_ci if (!mtd->name) 28198c2ecf20Sopenharmony_ci return -ENOMEM; 28208c2ecf20Sopenharmony_ci 28218c2ecf20Sopenharmony_ci mtd->owner = THIS_MODULE; 28228c2ecf20Sopenharmony_ci mtd->dev.parent = dev; 28238c2ecf20Sopenharmony_ci 28248c2ecf20Sopenharmony_ci chip->legacy.cmdfunc = qcom_nandc_command; 28258c2ecf20Sopenharmony_ci chip->legacy.select_chip = qcom_nandc_select_chip; 28268c2ecf20Sopenharmony_ci chip->legacy.read_byte = qcom_nandc_read_byte; 28278c2ecf20Sopenharmony_ci chip->legacy.read_buf = qcom_nandc_read_buf; 28288c2ecf20Sopenharmony_ci chip->legacy.write_buf = qcom_nandc_write_buf; 28298c2ecf20Sopenharmony_ci chip->legacy.set_features = nand_get_set_features_notsupp; 28308c2ecf20Sopenharmony_ci chip->legacy.get_features = nand_get_set_features_notsupp; 28318c2ecf20Sopenharmony_ci 28328c2ecf20Sopenharmony_ci /* 28338c2ecf20Sopenharmony_ci * the bad block marker is readable only when we read the last codeword 28348c2ecf20Sopenharmony_ci * of a page with ECC disabled. currently, the nand_base and nand_bbt 28358c2ecf20Sopenharmony_ci * helpers don't allow us to read BB from a nand chip with ECC 28368c2ecf20Sopenharmony_ci * disabled (MTD_OPS_PLACE_OOB is set by default). use the block_bad 28378c2ecf20Sopenharmony_ci * and block_markbad helpers until we permanently switch to using 28388c2ecf20Sopenharmony_ci * MTD_OPS_RAW for all drivers (with the help of badblockbits) 28398c2ecf20Sopenharmony_ci */ 28408c2ecf20Sopenharmony_ci chip->legacy.block_bad = qcom_nandc_block_bad; 28418c2ecf20Sopenharmony_ci chip->legacy.block_markbad = qcom_nandc_block_markbad; 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci chip->controller = &nandc->controller; 28448c2ecf20Sopenharmony_ci chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USES_DMA | 28458c2ecf20Sopenharmony_ci NAND_SKIP_BBTSCAN; 28468c2ecf20Sopenharmony_ci 28478c2ecf20Sopenharmony_ci /* set up initial status value */ 28488c2ecf20Sopenharmony_ci host->status = NAND_STATUS_READY | NAND_STATUS_WP; 28498c2ecf20Sopenharmony_ci 28508c2ecf20Sopenharmony_ci ret = nand_scan(chip, 1); 28518c2ecf20Sopenharmony_ci if (ret) 28528c2ecf20Sopenharmony_ci return ret; 28538c2ecf20Sopenharmony_ci 28548c2ecf20Sopenharmony_ci if (nandc->props->is_bam) { 28558c2ecf20Sopenharmony_ci free_bam_transaction(nandc); 28568c2ecf20Sopenharmony_ci nandc->bam_txn = alloc_bam_transaction(nandc); 28578c2ecf20Sopenharmony_ci if (!nandc->bam_txn) { 28588c2ecf20Sopenharmony_ci dev_err(nandc->dev, 28598c2ecf20Sopenharmony_ci "failed to allocate bam transaction\n"); 28608c2ecf20Sopenharmony_ci return -ENOMEM; 28618c2ecf20Sopenharmony_ci } 28628c2ecf20Sopenharmony_ci } 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci ret = mtd_device_register(mtd, NULL, 0); 28658c2ecf20Sopenharmony_ci if (ret) 28668c2ecf20Sopenharmony_ci nand_cleanup(chip); 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_ci return ret; 28698c2ecf20Sopenharmony_ci} 28708c2ecf20Sopenharmony_ci 28718c2ecf20Sopenharmony_cistatic int qcom_probe_nand_devices(struct qcom_nand_controller *nandc) 28728c2ecf20Sopenharmony_ci{ 28738c2ecf20Sopenharmony_ci struct device *dev = nandc->dev; 28748c2ecf20Sopenharmony_ci struct device_node *dn = dev->of_node, *child; 28758c2ecf20Sopenharmony_ci struct qcom_nand_host *host; 28768c2ecf20Sopenharmony_ci int ret = -ENODEV; 28778c2ecf20Sopenharmony_ci 28788c2ecf20Sopenharmony_ci for_each_available_child_of_node(dn, child) { 28798c2ecf20Sopenharmony_ci host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); 28808c2ecf20Sopenharmony_ci if (!host) { 28818c2ecf20Sopenharmony_ci of_node_put(child); 28828c2ecf20Sopenharmony_ci return -ENOMEM; 28838c2ecf20Sopenharmony_ci } 28848c2ecf20Sopenharmony_ci 28858c2ecf20Sopenharmony_ci ret = qcom_nand_host_init_and_register(nandc, host, child); 28868c2ecf20Sopenharmony_ci if (ret) { 28878c2ecf20Sopenharmony_ci devm_kfree(dev, host); 28888c2ecf20Sopenharmony_ci continue; 28898c2ecf20Sopenharmony_ci } 28908c2ecf20Sopenharmony_ci 28918c2ecf20Sopenharmony_ci list_add_tail(&host->node, &nandc->host_list); 28928c2ecf20Sopenharmony_ci } 28938c2ecf20Sopenharmony_ci 28948c2ecf20Sopenharmony_ci return ret; 28958c2ecf20Sopenharmony_ci} 28968c2ecf20Sopenharmony_ci 28978c2ecf20Sopenharmony_ci/* parse custom DT properties here */ 28988c2ecf20Sopenharmony_cistatic int qcom_nandc_parse_dt(struct platform_device *pdev) 28998c2ecf20Sopenharmony_ci{ 29008c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = platform_get_drvdata(pdev); 29018c2ecf20Sopenharmony_ci struct device_node *np = nandc->dev->of_node; 29028c2ecf20Sopenharmony_ci int ret; 29038c2ecf20Sopenharmony_ci 29048c2ecf20Sopenharmony_ci if (!nandc->props->is_bam) { 29058c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "qcom,cmd-crci", 29068c2ecf20Sopenharmony_ci &nandc->cmd_crci); 29078c2ecf20Sopenharmony_ci if (ret) { 29088c2ecf20Sopenharmony_ci dev_err(nandc->dev, "command CRCI unspecified\n"); 29098c2ecf20Sopenharmony_ci return ret; 29108c2ecf20Sopenharmony_ci } 29118c2ecf20Sopenharmony_ci 29128c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "qcom,data-crci", 29138c2ecf20Sopenharmony_ci &nandc->data_crci); 29148c2ecf20Sopenharmony_ci if (ret) { 29158c2ecf20Sopenharmony_ci dev_err(nandc->dev, "data CRCI unspecified\n"); 29168c2ecf20Sopenharmony_ci return ret; 29178c2ecf20Sopenharmony_ci } 29188c2ecf20Sopenharmony_ci } 29198c2ecf20Sopenharmony_ci 29208c2ecf20Sopenharmony_ci return 0; 29218c2ecf20Sopenharmony_ci} 29228c2ecf20Sopenharmony_ci 29238c2ecf20Sopenharmony_cistatic int qcom_nandc_probe(struct platform_device *pdev) 29248c2ecf20Sopenharmony_ci{ 29258c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc; 29268c2ecf20Sopenharmony_ci const void *dev_data; 29278c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 29288c2ecf20Sopenharmony_ci struct resource *res; 29298c2ecf20Sopenharmony_ci int ret; 29308c2ecf20Sopenharmony_ci 29318c2ecf20Sopenharmony_ci nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL); 29328c2ecf20Sopenharmony_ci if (!nandc) 29338c2ecf20Sopenharmony_ci return -ENOMEM; 29348c2ecf20Sopenharmony_ci 29358c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, nandc); 29368c2ecf20Sopenharmony_ci nandc->dev = dev; 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_ci dev_data = of_device_get_match_data(dev); 29398c2ecf20Sopenharmony_ci if (!dev_data) { 29408c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get device data\n"); 29418c2ecf20Sopenharmony_ci return -ENODEV; 29428c2ecf20Sopenharmony_ci } 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ci nandc->props = dev_data; 29458c2ecf20Sopenharmony_ci 29468c2ecf20Sopenharmony_ci nandc->core_clk = devm_clk_get(dev, "core"); 29478c2ecf20Sopenharmony_ci if (IS_ERR(nandc->core_clk)) 29488c2ecf20Sopenharmony_ci return PTR_ERR(nandc->core_clk); 29498c2ecf20Sopenharmony_ci 29508c2ecf20Sopenharmony_ci nandc->aon_clk = devm_clk_get(dev, "aon"); 29518c2ecf20Sopenharmony_ci if (IS_ERR(nandc->aon_clk)) 29528c2ecf20Sopenharmony_ci return PTR_ERR(nandc->aon_clk); 29538c2ecf20Sopenharmony_ci 29548c2ecf20Sopenharmony_ci ret = qcom_nandc_parse_dt(pdev); 29558c2ecf20Sopenharmony_ci if (ret) 29568c2ecf20Sopenharmony_ci return ret; 29578c2ecf20Sopenharmony_ci 29588c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 29598c2ecf20Sopenharmony_ci nandc->base = devm_ioremap_resource(dev, res); 29608c2ecf20Sopenharmony_ci if (IS_ERR(nandc->base)) 29618c2ecf20Sopenharmony_ci return PTR_ERR(nandc->base); 29628c2ecf20Sopenharmony_ci 29638c2ecf20Sopenharmony_ci nandc->base_phys = res->start; 29648c2ecf20Sopenharmony_ci nandc->base_dma = dma_map_resource(dev, res->start, 29658c2ecf20Sopenharmony_ci resource_size(res), 29668c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL, 0); 29678c2ecf20Sopenharmony_ci if (!nandc->base_dma) 29688c2ecf20Sopenharmony_ci return -ENXIO; 29698c2ecf20Sopenharmony_ci 29708c2ecf20Sopenharmony_ci ret = clk_prepare_enable(nandc->core_clk); 29718c2ecf20Sopenharmony_ci if (ret) 29728c2ecf20Sopenharmony_ci goto err_core_clk; 29738c2ecf20Sopenharmony_ci 29748c2ecf20Sopenharmony_ci ret = clk_prepare_enable(nandc->aon_clk); 29758c2ecf20Sopenharmony_ci if (ret) 29768c2ecf20Sopenharmony_ci goto err_aon_clk; 29778c2ecf20Sopenharmony_ci 29788c2ecf20Sopenharmony_ci ret = qcom_nandc_alloc(nandc); 29798c2ecf20Sopenharmony_ci if (ret) 29808c2ecf20Sopenharmony_ci goto err_nandc_alloc; 29818c2ecf20Sopenharmony_ci 29828c2ecf20Sopenharmony_ci ret = qcom_nandc_setup(nandc); 29838c2ecf20Sopenharmony_ci if (ret) 29848c2ecf20Sopenharmony_ci goto err_setup; 29858c2ecf20Sopenharmony_ci 29868c2ecf20Sopenharmony_ci ret = qcom_probe_nand_devices(nandc); 29878c2ecf20Sopenharmony_ci if (ret) 29888c2ecf20Sopenharmony_ci goto err_setup; 29898c2ecf20Sopenharmony_ci 29908c2ecf20Sopenharmony_ci return 0; 29918c2ecf20Sopenharmony_ci 29928c2ecf20Sopenharmony_cierr_setup: 29938c2ecf20Sopenharmony_ci qcom_nandc_unalloc(nandc); 29948c2ecf20Sopenharmony_cierr_nandc_alloc: 29958c2ecf20Sopenharmony_ci clk_disable_unprepare(nandc->aon_clk); 29968c2ecf20Sopenharmony_cierr_aon_clk: 29978c2ecf20Sopenharmony_ci clk_disable_unprepare(nandc->core_clk); 29988c2ecf20Sopenharmony_cierr_core_clk: 29998c2ecf20Sopenharmony_ci dma_unmap_resource(dev, nandc->base_dma, resource_size(res), 30008c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL, 0); 30018c2ecf20Sopenharmony_ci return ret; 30028c2ecf20Sopenharmony_ci} 30038c2ecf20Sopenharmony_ci 30048c2ecf20Sopenharmony_cistatic int qcom_nandc_remove(struct platform_device *pdev) 30058c2ecf20Sopenharmony_ci{ 30068c2ecf20Sopenharmony_ci struct qcom_nand_controller *nandc = platform_get_drvdata(pdev); 30078c2ecf20Sopenharmony_ci struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 30088c2ecf20Sopenharmony_ci struct qcom_nand_host *host; 30098c2ecf20Sopenharmony_ci struct nand_chip *chip; 30108c2ecf20Sopenharmony_ci int ret; 30118c2ecf20Sopenharmony_ci 30128c2ecf20Sopenharmony_ci list_for_each_entry(host, &nandc->host_list, node) { 30138c2ecf20Sopenharmony_ci chip = &host->chip; 30148c2ecf20Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 30158c2ecf20Sopenharmony_ci WARN_ON(ret); 30168c2ecf20Sopenharmony_ci nand_cleanup(chip); 30178c2ecf20Sopenharmony_ci } 30188c2ecf20Sopenharmony_ci 30198c2ecf20Sopenharmony_ci qcom_nandc_unalloc(nandc); 30208c2ecf20Sopenharmony_ci 30218c2ecf20Sopenharmony_ci clk_disable_unprepare(nandc->aon_clk); 30228c2ecf20Sopenharmony_ci clk_disable_unprepare(nandc->core_clk); 30238c2ecf20Sopenharmony_ci 30248c2ecf20Sopenharmony_ci dma_unmap_resource(&pdev->dev, nandc->base_dma, resource_size(res), 30258c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL, 0); 30268c2ecf20Sopenharmony_ci 30278c2ecf20Sopenharmony_ci return 0; 30288c2ecf20Sopenharmony_ci} 30298c2ecf20Sopenharmony_ci 30308c2ecf20Sopenharmony_cistatic const struct qcom_nandc_props ipq806x_nandc_props = { 30318c2ecf20Sopenharmony_ci .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT), 30328c2ecf20Sopenharmony_ci .is_bam = false, 30338c2ecf20Sopenharmony_ci .dev_cmd_reg_start = 0x0, 30348c2ecf20Sopenharmony_ci}; 30358c2ecf20Sopenharmony_ci 30368c2ecf20Sopenharmony_cistatic const struct qcom_nandc_props ipq4019_nandc_props = { 30378c2ecf20Sopenharmony_ci .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), 30388c2ecf20Sopenharmony_ci .is_bam = true, 30398c2ecf20Sopenharmony_ci .is_qpic = true, 30408c2ecf20Sopenharmony_ci .dev_cmd_reg_start = 0x0, 30418c2ecf20Sopenharmony_ci}; 30428c2ecf20Sopenharmony_ci 30438c2ecf20Sopenharmony_cistatic const struct qcom_nandc_props ipq8074_nandc_props = { 30448c2ecf20Sopenharmony_ci .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), 30458c2ecf20Sopenharmony_ci .is_bam = true, 30468c2ecf20Sopenharmony_ci .is_qpic = true, 30478c2ecf20Sopenharmony_ci .dev_cmd_reg_start = 0x7000, 30488c2ecf20Sopenharmony_ci}; 30498c2ecf20Sopenharmony_ci 30508c2ecf20Sopenharmony_ci/* 30518c2ecf20Sopenharmony_ci * data will hold a struct pointer containing more differences once we support 30528c2ecf20Sopenharmony_ci * more controller variants 30538c2ecf20Sopenharmony_ci */ 30548c2ecf20Sopenharmony_cistatic const struct of_device_id qcom_nandc_of_match[] = { 30558c2ecf20Sopenharmony_ci { 30568c2ecf20Sopenharmony_ci .compatible = "qcom,ipq806x-nand", 30578c2ecf20Sopenharmony_ci .data = &ipq806x_nandc_props, 30588c2ecf20Sopenharmony_ci }, 30598c2ecf20Sopenharmony_ci { 30608c2ecf20Sopenharmony_ci .compatible = "qcom,ipq4019-nand", 30618c2ecf20Sopenharmony_ci .data = &ipq4019_nandc_props, 30628c2ecf20Sopenharmony_ci }, 30638c2ecf20Sopenharmony_ci { 30648c2ecf20Sopenharmony_ci .compatible = "qcom,ipq8074-nand", 30658c2ecf20Sopenharmony_ci .data = &ipq8074_nandc_props, 30668c2ecf20Sopenharmony_ci }, 30678c2ecf20Sopenharmony_ci {} 30688c2ecf20Sopenharmony_ci}; 30698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_nandc_of_match); 30708c2ecf20Sopenharmony_ci 30718c2ecf20Sopenharmony_cistatic struct platform_driver qcom_nandc_driver = { 30728c2ecf20Sopenharmony_ci .driver = { 30738c2ecf20Sopenharmony_ci .name = "qcom-nandc", 30748c2ecf20Sopenharmony_ci .of_match_table = qcom_nandc_of_match, 30758c2ecf20Sopenharmony_ci }, 30768c2ecf20Sopenharmony_ci .probe = qcom_nandc_probe, 30778c2ecf20Sopenharmony_ci .remove = qcom_nandc_remove, 30788c2ecf20Sopenharmony_ci}; 30798c2ecf20Sopenharmony_cimodule_platform_driver(qcom_nandc_driver); 30808c2ecf20Sopenharmony_ci 30818c2ecf20Sopenharmony_ciMODULE_AUTHOR("Archit Taneja <architt@codeaurora.org>"); 30828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm NAND Controller driver"); 30838c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3084