1*7304d190SMd Sadre Alam /* 2*7304d190SMd Sadre Alam * SPDX-License-Identifier: GPL-2.0 3*7304d190SMd Sadre Alam * 4*7304d190SMd Sadre Alam * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. 5*7304d190SMd Sadre Alam * 6*7304d190SMd Sadre Alam * Authors: 7*7304d190SMd Sadre Alam * Md Sadre Alam <quic_mdalam@quicinc.com> 8*7304d190SMd Sadre Alam * Sricharan R <quic_srichara@quicinc.com> 9*7304d190SMd Sadre Alam * Varadarajan Narayanan <quic_varada@quicinc.com> 10*7304d190SMd Sadre Alam */ 11*7304d190SMd Sadre Alam #include <linux/bitops.h> 12*7304d190SMd Sadre Alam #include <linux/clk.h> 13*7304d190SMd Sadre Alam #include <linux/delay.h> 14*7304d190SMd Sadre Alam #include <linux/dmaengine.h> 15*7304d190SMd Sadre Alam #include <linux/dma-mapping.h> 16*7304d190SMd Sadre Alam #include <linux/dma/qcom_adm.h> 17*7304d190SMd Sadre Alam #include <linux/dma/qcom_bam_dma.h> 18*7304d190SMd Sadre Alam #include <linux/module.h> 19*7304d190SMd Sadre Alam #include <linux/of.h> 20*7304d190SMd Sadre Alam #include <linux/platform_device.h> 21*7304d190SMd Sadre Alam #include <linux/slab.h> 22*7304d190SMd Sadre Alam #include <linux/mtd/nand-qpic-common.h> 23*7304d190SMd Sadre Alam #include <linux/mtd/spinand.h> 24*7304d190SMd Sadre Alam #include <linux/bitfield.h> 25*7304d190SMd Sadre Alam 26*7304d190SMd Sadre Alam #define NAND_FLASH_SPI_CFG 0xc0 27*7304d190SMd Sadre Alam #define NAND_NUM_ADDR_CYCLES 0xc4 28*7304d190SMd Sadre Alam #define NAND_BUSY_CHECK_WAIT_CNT 0xc8 29*7304d190SMd Sadre Alam #define NAND_FLASH_FEATURES 0xf64 30*7304d190SMd Sadre Alam 31*7304d190SMd Sadre Alam /* QSPI NAND config reg bits */ 32*7304d190SMd Sadre Alam #define LOAD_CLK_CNTR_INIT_EN BIT(28) 33*7304d190SMd Sadre Alam #define CLK_CNTR_INIT_VAL_VEC 0x924 34*7304d190SMd Sadre Alam #define CLK_CNTR_INIT_VAL_VEC_MASK GENMASK(27, 16) 35*7304d190SMd Sadre Alam #define FEA_STATUS_DEV_ADDR 0xc0 36*7304d190SMd Sadre Alam #define FEA_STATUS_DEV_ADDR_MASK GENMASK(15, 8) 37*7304d190SMd Sadre Alam #define SPI_CFG BIT(0) 38*7304d190SMd Sadre Alam #define SPI_NUM_ADDR 0xDA4DB 39*7304d190SMd Sadre Alam #define SPI_WAIT_CNT 0x10 40*7304d190SMd Sadre Alam #define QPIC_QSPI_NUM_CS 1 41*7304d190SMd Sadre Alam #define SPI_TRANSFER_MODE_x1 BIT(29) 42*7304d190SMd Sadre Alam #define SPI_TRANSFER_MODE_x4 (3 << 29) 43*7304d190SMd Sadre Alam #define SPI_WP BIT(28) 44*7304d190SMd Sadre Alam #define SPI_HOLD BIT(27) 45*7304d190SMd Sadre Alam #define QPIC_SET_FEATURE BIT(31) 46*7304d190SMd Sadre Alam 47*7304d190SMd Sadre Alam #define SPINAND_RESET 0xff 48*7304d190SMd Sadre Alam #define SPINAND_READID 0x9f 49*7304d190SMd Sadre Alam #define SPINAND_GET_FEATURE 0x0f 50*7304d190SMd Sadre Alam #define SPINAND_SET_FEATURE 0x1f 51*7304d190SMd Sadre Alam #define SPINAND_READ 0x13 52*7304d190SMd Sadre Alam #define SPINAND_ERASE 0xd8 53*7304d190SMd Sadre Alam #define SPINAND_WRITE_EN 0x06 54*7304d190SMd Sadre Alam #define SPINAND_PROGRAM_EXECUTE 0x10 55*7304d190SMd Sadre Alam #define SPINAND_PROGRAM_LOAD 0x84 56*7304d190SMd Sadre Alam 57*7304d190SMd Sadre Alam #define ACC_FEATURE 0xe 58*7304d190SMd Sadre Alam #define BAD_BLOCK_MARKER_SIZE 0x2 59*7304d190SMd Sadre Alam #define OOB_BUF_SIZE 128 60*7304d190SMd Sadre Alam #define ecceng_to_qspi(eng) container_of(eng, struct qpic_spi_nand, ecc_eng) 61*7304d190SMd Sadre Alam 62*7304d190SMd Sadre Alam struct qpic_snand_op { 63*7304d190SMd Sadre Alam u32 cmd_reg; 64*7304d190SMd Sadre Alam u32 addr1_reg; 65*7304d190SMd Sadre Alam u32 addr2_reg; 66*7304d190SMd Sadre Alam }; 67*7304d190SMd Sadre Alam 68*7304d190SMd Sadre Alam struct snandc_read_status { 69*7304d190SMd Sadre Alam __le32 snandc_flash; 70*7304d190SMd Sadre Alam __le32 snandc_buffer; 71*7304d190SMd Sadre Alam __le32 snandc_erased_cw; 72*7304d190SMd Sadre Alam }; 73*7304d190SMd Sadre Alam 74*7304d190SMd Sadre Alam /* 75*7304d190SMd Sadre Alam * ECC state struct 76*7304d190SMd Sadre Alam * @corrected: ECC corrected 77*7304d190SMd Sadre Alam * @bitflips: Max bit flip 78*7304d190SMd Sadre Alam * @failed: ECC failed 79*7304d190SMd Sadre Alam */ 80*7304d190SMd Sadre Alam struct qcom_ecc_stats { 81*7304d190SMd Sadre Alam u32 corrected; 82*7304d190SMd Sadre Alam u32 bitflips; 83*7304d190SMd Sadre Alam u32 failed; 84*7304d190SMd Sadre Alam }; 85*7304d190SMd Sadre Alam 86*7304d190SMd Sadre Alam struct qpic_ecc { 87*7304d190SMd Sadre Alam struct device *dev; 88*7304d190SMd Sadre Alam int ecc_bytes_hw; 89*7304d190SMd Sadre Alam int spare_bytes; 90*7304d190SMd Sadre Alam int bbm_size; 91*7304d190SMd Sadre Alam int ecc_mode; 92*7304d190SMd Sadre Alam int bytes; 93*7304d190SMd Sadre Alam int steps; 94*7304d190SMd Sadre Alam int step_size; 95*7304d190SMd Sadre Alam int strength; 96*7304d190SMd Sadre Alam int cw_size; 97*7304d190SMd Sadre Alam int cw_data; 98*7304d190SMd Sadre Alam u32 cfg0; 99*7304d190SMd Sadre Alam u32 cfg1; 100*7304d190SMd Sadre Alam u32 cfg0_raw; 101*7304d190SMd Sadre Alam u32 cfg1_raw; 102*7304d190SMd Sadre Alam u32 ecc_buf_cfg; 103*7304d190SMd Sadre Alam u32 ecc_bch_cfg; 104*7304d190SMd Sadre Alam u32 clrflashstatus; 105*7304d190SMd Sadre Alam u32 clrreadstatus; 106*7304d190SMd Sadre Alam bool bch_enabled; 107*7304d190SMd Sadre Alam }; 108*7304d190SMd Sadre Alam 109*7304d190SMd Sadre Alam struct qpic_spi_nand { 110*7304d190SMd Sadre Alam struct qcom_nand_controller *snandc; 111*7304d190SMd Sadre Alam struct spi_controller *ctlr; 112*7304d190SMd Sadre Alam struct mtd_info *mtd; 113*7304d190SMd Sadre Alam struct clk *iomacro_clk; 114*7304d190SMd Sadre Alam struct qpic_ecc *ecc; 115*7304d190SMd Sadre Alam struct qcom_ecc_stats ecc_stats; 116*7304d190SMd Sadre Alam struct nand_ecc_engine ecc_eng; 117*7304d190SMd Sadre Alam u8 *data_buf; 118*7304d190SMd Sadre Alam u8 *oob_buf; 119*7304d190SMd Sadre Alam u32 wlen; 120*7304d190SMd Sadre Alam __le32 addr1; 121*7304d190SMd Sadre Alam __le32 addr2; 122*7304d190SMd Sadre Alam __le32 cmd; 123*7304d190SMd Sadre Alam u32 num_cw; 124*7304d190SMd Sadre Alam bool oob_rw; 125*7304d190SMd Sadre Alam bool page_rw; 126*7304d190SMd Sadre Alam bool raw_rw; 127*7304d190SMd Sadre Alam }; 128*7304d190SMd Sadre Alam 129*7304d190SMd Sadre Alam static void qcom_spi_set_read_loc_first(struct qcom_nand_controller *snandc, 130*7304d190SMd Sadre Alam int reg, int cw_offset, int read_size, 131*7304d190SMd Sadre Alam int is_last_read_loc) 132*7304d190SMd Sadre Alam { 133*7304d190SMd Sadre Alam __le32 locreg_val; 134*7304d190SMd Sadre Alam u32 val = (((cw_offset) << READ_LOCATION_OFFSET) | 135*7304d190SMd Sadre Alam ((read_size) << READ_LOCATION_SIZE) | ((is_last_read_loc) 136*7304d190SMd Sadre Alam << READ_LOCATION_LAST)); 137*7304d190SMd Sadre Alam 138*7304d190SMd Sadre Alam locreg_val = cpu_to_le32(val); 139*7304d190SMd Sadre Alam 140*7304d190SMd Sadre Alam if (reg == NAND_READ_LOCATION_0) 141*7304d190SMd Sadre Alam snandc->regs->read_location0 = locreg_val; 142*7304d190SMd Sadre Alam else if (reg == NAND_READ_LOCATION_1) 143*7304d190SMd Sadre Alam snandc->regs->read_location1 = locreg_val; 144*7304d190SMd Sadre Alam else if (reg == NAND_READ_LOCATION_2) 145*7304d190SMd Sadre Alam snandc->regs->read_location1 = locreg_val; 146*7304d190SMd Sadre Alam else if (reg == NAND_READ_LOCATION_3) 147*7304d190SMd Sadre Alam snandc->regs->read_location3 = locreg_val; 148*7304d190SMd Sadre Alam } 149*7304d190SMd Sadre Alam 150*7304d190SMd Sadre Alam static void qcom_spi_set_read_loc_last(struct qcom_nand_controller *snandc, 151*7304d190SMd Sadre Alam int reg, int cw_offset, int read_size, 152*7304d190SMd Sadre Alam int is_last_read_loc) 153*7304d190SMd Sadre Alam { 154*7304d190SMd Sadre Alam __le32 locreg_val; 155*7304d190SMd Sadre Alam u32 val = (((cw_offset) << READ_LOCATION_OFFSET) | 156*7304d190SMd Sadre Alam ((read_size) << READ_LOCATION_SIZE) | ((is_last_read_loc) 157*7304d190SMd Sadre Alam << READ_LOCATION_LAST)); 158*7304d190SMd Sadre Alam 159*7304d190SMd Sadre Alam locreg_val = cpu_to_le32(val); 160*7304d190SMd Sadre Alam 161*7304d190SMd Sadre Alam if (reg == NAND_READ_LOCATION_LAST_CW_0) 162*7304d190SMd Sadre Alam snandc->regs->read_location_last0 = locreg_val; 163*7304d190SMd Sadre Alam else if (reg == NAND_READ_LOCATION_LAST_CW_1) 164*7304d190SMd Sadre Alam snandc->regs->read_location_last1 = locreg_val; 165*7304d190SMd Sadre Alam else if (reg == NAND_READ_LOCATION_LAST_CW_2) 166*7304d190SMd Sadre Alam snandc->regs->read_location_last2 = locreg_val; 167*7304d190SMd Sadre Alam else if (reg == NAND_READ_LOCATION_LAST_CW_3) 168*7304d190SMd Sadre Alam snandc->regs->read_location_last3 = locreg_val; 169*7304d190SMd Sadre Alam } 170*7304d190SMd Sadre Alam 171*7304d190SMd Sadre Alam static struct qcom_nand_controller *nand_to_qcom_snand(struct nand_device *nand) 172*7304d190SMd Sadre Alam { 173*7304d190SMd Sadre Alam struct nand_ecc_engine *eng = nand->ecc.engine; 174*7304d190SMd Sadre Alam struct qpic_spi_nand *qspi = ecceng_to_qspi(eng); 175*7304d190SMd Sadre Alam 176*7304d190SMd Sadre Alam return qspi->snandc; 177*7304d190SMd Sadre Alam } 178*7304d190SMd Sadre Alam 179*7304d190SMd Sadre Alam static int qcom_spi_init(struct qcom_nand_controller *snandc) 180*7304d190SMd Sadre Alam { 181*7304d190SMd Sadre Alam u32 snand_cfg_val = 0x0; 182*7304d190SMd Sadre Alam int ret; 183*7304d190SMd Sadre Alam 184*7304d190SMd Sadre Alam snand_cfg_val = FIELD_PREP(CLK_CNTR_INIT_VAL_VEC_MASK, CLK_CNTR_INIT_VAL_VEC) | 185*7304d190SMd Sadre Alam FIELD_PREP(LOAD_CLK_CNTR_INIT_EN, 0) | 186*7304d190SMd Sadre Alam FIELD_PREP(FEA_STATUS_DEV_ADDR_MASK, FEA_STATUS_DEV_ADDR) | 187*7304d190SMd Sadre Alam FIELD_PREP(SPI_CFG, 0); 188*7304d190SMd Sadre Alam 189*7304d190SMd Sadre Alam snandc->regs->spi_cfg = cpu_to_le32(snand_cfg_val); 190*7304d190SMd Sadre Alam snandc->regs->num_addr_cycle = cpu_to_le32(SPI_NUM_ADDR); 191*7304d190SMd Sadre Alam snandc->regs->busy_wait_cnt = cpu_to_le32(SPI_WAIT_CNT); 192*7304d190SMd Sadre Alam 193*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->spi_cfg, NAND_FLASH_SPI_CFG, 1, 0); 194*7304d190SMd Sadre Alam 195*7304d190SMd Sadre Alam snand_cfg_val &= ~LOAD_CLK_CNTR_INIT_EN; 196*7304d190SMd Sadre Alam snandc->regs->spi_cfg = cpu_to_le32(snand_cfg_val); 197*7304d190SMd Sadre Alam 198*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->spi_cfg, NAND_FLASH_SPI_CFG, 1, 0); 199*7304d190SMd Sadre Alam 200*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->num_addr_cycle, NAND_NUM_ADDR_CYCLES, 1, 0); 201*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->busy_wait_cnt, NAND_BUSY_CHECK_WAIT_CNT, 1, 202*7304d190SMd Sadre Alam NAND_BAM_NEXT_SGL); 203*7304d190SMd Sadre Alam 204*7304d190SMd Sadre Alam ret = qcom_submit_descs(snandc); 205*7304d190SMd Sadre Alam if (ret) { 206*7304d190SMd Sadre Alam dev_err(snandc->dev, "failure in submitting spi init descriptor\n"); 207*7304d190SMd Sadre Alam return ret; 208*7304d190SMd Sadre Alam } 209*7304d190SMd Sadre Alam 210*7304d190SMd Sadre Alam return ret; 211*7304d190SMd Sadre Alam } 212*7304d190SMd Sadre Alam 213*7304d190SMd Sadre Alam static int qcom_spi_ooblayout_ecc(struct mtd_info *mtd, int section, 214*7304d190SMd Sadre Alam struct mtd_oob_region *oobregion) 215*7304d190SMd Sadre Alam { 216*7304d190SMd Sadre Alam struct nand_device *nand = mtd_to_nanddev(mtd); 217*7304d190SMd Sadre Alam struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); 218*7304d190SMd Sadre Alam struct qpic_ecc *qecc = snandc->qspi->ecc; 219*7304d190SMd Sadre Alam 220*7304d190SMd Sadre Alam if (section > 1) 221*7304d190SMd Sadre Alam return -ERANGE; 222*7304d190SMd Sadre Alam 223*7304d190SMd Sadre Alam oobregion->length = qecc->ecc_bytes_hw + qecc->spare_bytes; 224*7304d190SMd Sadre Alam oobregion->offset = mtd->oobsize - oobregion->length; 225*7304d190SMd Sadre Alam 226*7304d190SMd Sadre Alam return 0; 227*7304d190SMd Sadre Alam } 228*7304d190SMd Sadre Alam 229*7304d190SMd Sadre Alam static int qcom_spi_ooblayout_free(struct mtd_info *mtd, int section, 230*7304d190SMd Sadre Alam struct mtd_oob_region *oobregion) 231*7304d190SMd Sadre Alam { 232*7304d190SMd Sadre Alam struct nand_device *nand = mtd_to_nanddev(mtd); 233*7304d190SMd Sadre Alam struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); 234*7304d190SMd Sadre Alam struct qpic_ecc *qecc = snandc->qspi->ecc; 235*7304d190SMd Sadre Alam 236*7304d190SMd Sadre Alam if (section) 237*7304d190SMd Sadre Alam return -ERANGE; 238*7304d190SMd Sadre Alam 239*7304d190SMd Sadre Alam oobregion->length = qecc->steps * 4; 240*7304d190SMd Sadre Alam oobregion->offset = ((qecc->steps - 1) * qecc->bytes) + qecc->bbm_size; 241*7304d190SMd Sadre Alam 242*7304d190SMd Sadre Alam return 0; 243*7304d190SMd Sadre Alam } 244*7304d190SMd Sadre Alam 245*7304d190SMd Sadre Alam static const struct mtd_ooblayout_ops qcom_spi_ooblayout = { 246*7304d190SMd Sadre Alam .ecc = qcom_spi_ooblayout_ecc, 247*7304d190SMd Sadre Alam .free = qcom_spi_ooblayout_free, 248*7304d190SMd Sadre Alam }; 249*7304d190SMd Sadre Alam 250*7304d190SMd Sadre Alam static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) 251*7304d190SMd Sadre Alam { 252*7304d190SMd Sadre Alam struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); 253*7304d190SMd Sadre Alam struct nand_ecc_props *conf = &nand->ecc.ctx.conf; 254*7304d190SMd Sadre Alam struct mtd_info *mtd = nanddev_to_mtd(nand); 255*7304d190SMd Sadre Alam int cwperpage, bad_block_byte; 256*7304d190SMd Sadre Alam struct qpic_ecc *ecc_cfg; 257*7304d190SMd Sadre Alam 258*7304d190SMd Sadre Alam cwperpage = mtd->writesize / NANDC_STEP_SIZE; 259*7304d190SMd Sadre Alam snandc->qspi->num_cw = cwperpage; 260*7304d190SMd Sadre Alam 261*7304d190SMd Sadre Alam ecc_cfg = kzalloc(sizeof(*ecc_cfg), GFP_KERNEL); 262*7304d190SMd Sadre Alam if (!ecc_cfg) 263*7304d190SMd Sadre Alam return -ENOMEM; 264*7304d190SMd Sadre Alam snandc->qspi->oob_buf = kzalloc(mtd->writesize + mtd->oobsize, 265*7304d190SMd Sadre Alam GFP_KERNEL); 266*7304d190SMd Sadre Alam if (!snandc->qspi->oob_buf) 267*7304d190SMd Sadre Alam return -ENOMEM; 268*7304d190SMd Sadre Alam 269*7304d190SMd Sadre Alam memset(snandc->qspi->oob_buf, 0xff, mtd->writesize + mtd->oobsize); 270*7304d190SMd Sadre Alam 271*7304d190SMd Sadre Alam nand->ecc.ctx.priv = ecc_cfg; 272*7304d190SMd Sadre Alam snandc->qspi->mtd = mtd; 273*7304d190SMd Sadre Alam 274*7304d190SMd Sadre Alam ecc_cfg->ecc_bytes_hw = 7; 275*7304d190SMd Sadre Alam ecc_cfg->spare_bytes = 4; 276*7304d190SMd Sadre Alam ecc_cfg->bbm_size = 1; 277*7304d190SMd Sadre Alam ecc_cfg->bch_enabled = true; 278*7304d190SMd Sadre Alam ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size; 279*7304d190SMd Sadre Alam 280*7304d190SMd Sadre Alam ecc_cfg->steps = 4; 281*7304d190SMd Sadre Alam ecc_cfg->strength = 4; 282*7304d190SMd Sadre Alam ecc_cfg->step_size = 512; 283*7304d190SMd Sadre Alam ecc_cfg->cw_data = 516; 284*7304d190SMd Sadre Alam ecc_cfg->cw_size = ecc_cfg->cw_data + ecc_cfg->bytes; 285*7304d190SMd Sadre Alam bad_block_byte = mtd->writesize - ecc_cfg->cw_size * (cwperpage - 1) + 1; 286*7304d190SMd Sadre Alam 287*7304d190SMd Sadre Alam mtd_set_ooblayout(mtd, &qcom_spi_ooblayout); 288*7304d190SMd Sadre Alam 289*7304d190SMd Sadre Alam ecc_cfg->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) | 290*7304d190SMd Sadre Alam FIELD_PREP(UD_SIZE_BYTES_MASK, ecc_cfg->cw_data) | 291*7304d190SMd Sadre Alam FIELD_PREP(DISABLE_STATUS_AFTER_WRITE, 1) | 292*7304d190SMd Sadre Alam FIELD_PREP(NUM_ADDR_CYCLES_MASK, 3) | 293*7304d190SMd Sadre Alam FIELD_PREP(ECC_PARITY_SIZE_BYTES_RS, ecc_cfg->ecc_bytes_hw) | 294*7304d190SMd Sadre Alam FIELD_PREP(STATUS_BFR_READ, 0) | 295*7304d190SMd Sadre Alam FIELD_PREP(SET_RD_MODE_AFTER_STATUS, 1) | 296*7304d190SMd Sadre Alam FIELD_PREP(SPARE_SIZE_BYTES_MASK, ecc_cfg->spare_bytes); 297*7304d190SMd Sadre Alam 298*7304d190SMd Sadre Alam ecc_cfg->cfg1 = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 0) | 299*7304d190SMd Sadre Alam FIELD_PREP(CS_ACTIVE_BSY, 0) | 300*7304d190SMd Sadre Alam FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, bad_block_byte) | 301*7304d190SMd Sadre Alam FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 0) | 302*7304d190SMd Sadre Alam FIELD_PREP(WR_RD_BSY_GAP_MASK, 20) | 303*7304d190SMd Sadre Alam FIELD_PREP(WIDE_FLASH, 0) | 304*7304d190SMd Sadre Alam FIELD_PREP(ENABLE_BCH_ECC, ecc_cfg->bch_enabled); 305*7304d190SMd Sadre Alam 306*7304d190SMd Sadre Alam ecc_cfg->cfg0_raw = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) | 307*7304d190SMd Sadre Alam FIELD_PREP(NUM_ADDR_CYCLES_MASK, 3) | 308*7304d190SMd Sadre Alam FIELD_PREP(UD_SIZE_BYTES_MASK, ecc_cfg->cw_size) | 309*7304d190SMd Sadre Alam FIELD_PREP(SPARE_SIZE_BYTES_MASK, 0); 310*7304d190SMd Sadre Alam 311*7304d190SMd Sadre Alam ecc_cfg->cfg1_raw = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 0) | 312*7304d190SMd Sadre Alam FIELD_PREP(CS_ACTIVE_BSY, 0) | 313*7304d190SMd Sadre Alam FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, 17) | 314*7304d190SMd Sadre Alam FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 1) | 315*7304d190SMd Sadre Alam FIELD_PREP(WR_RD_BSY_GAP_MASK, 20) | 316*7304d190SMd Sadre Alam FIELD_PREP(WIDE_FLASH, 0) | 317*7304d190SMd Sadre Alam FIELD_PREP(DEV0_CFG1_ECC_DISABLE, 1); 318*7304d190SMd Sadre Alam 319*7304d190SMd Sadre Alam ecc_cfg->ecc_bch_cfg = FIELD_PREP(ECC_CFG_ECC_DISABLE, !ecc_cfg->bch_enabled) | 320*7304d190SMd Sadre Alam FIELD_PREP(ECC_SW_RESET, 0) | 321*7304d190SMd Sadre Alam FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) | 322*7304d190SMd Sadre Alam FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) | 323*7304d190SMd Sadre Alam FIELD_PREP(ECC_MODE_MASK, 0) | 324*7304d190SMd Sadre Alam FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw); 325*7304d190SMd Sadre Alam 326*7304d190SMd Sadre Alam ecc_cfg->ecc_buf_cfg = 0x203 << NUM_STEPS; 327*7304d190SMd Sadre Alam ecc_cfg->clrflashstatus = FS_READY_BSY_N; 328*7304d190SMd Sadre Alam ecc_cfg->clrreadstatus = 0xc0; 329*7304d190SMd Sadre Alam 330*7304d190SMd Sadre Alam conf->step_size = ecc_cfg->step_size; 331*7304d190SMd Sadre Alam conf->strength = ecc_cfg->strength; 332*7304d190SMd Sadre Alam 333*7304d190SMd Sadre Alam snandc->regs->erased_cw_detect_cfg_clr = cpu_to_le32(CLR_ERASED_PAGE_DET); 334*7304d190SMd Sadre Alam snandc->regs->erased_cw_detect_cfg_set = cpu_to_le32(SET_ERASED_PAGE_DET); 335*7304d190SMd Sadre Alam 336*7304d190SMd Sadre Alam dev_dbg(snandc->dev, "ECC strength: %u bits per %u bytes\n", 337*7304d190SMd Sadre Alam ecc_cfg->strength, ecc_cfg->step_size); 338*7304d190SMd Sadre Alam 339*7304d190SMd Sadre Alam return 0; 340*7304d190SMd Sadre Alam } 341*7304d190SMd Sadre Alam 342*7304d190SMd Sadre Alam static void qcom_spi_ecc_cleanup_ctx_pipelined(struct nand_device *nand) 343*7304d190SMd Sadre Alam { 344*7304d190SMd Sadre Alam struct qpic_ecc *ecc_cfg = nand_to_ecc_ctx(nand); 345*7304d190SMd Sadre Alam 346*7304d190SMd Sadre Alam kfree(ecc_cfg); 347*7304d190SMd Sadre Alam } 348*7304d190SMd Sadre Alam 349*7304d190SMd Sadre Alam static int qcom_spi_ecc_prepare_io_req_pipelined(struct nand_device *nand, 350*7304d190SMd Sadre Alam struct nand_page_io_req *req) 351*7304d190SMd Sadre Alam { 352*7304d190SMd Sadre Alam struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); 353*7304d190SMd Sadre Alam struct qpic_ecc *ecc_cfg = nand_to_ecc_ctx(nand); 354*7304d190SMd Sadre Alam 355*7304d190SMd Sadre Alam snandc->qspi->ecc = ecc_cfg; 356*7304d190SMd Sadre Alam snandc->qspi->raw_rw = false; 357*7304d190SMd Sadre Alam snandc->qspi->oob_rw = false; 358*7304d190SMd Sadre Alam snandc->qspi->page_rw = false; 359*7304d190SMd Sadre Alam 360*7304d190SMd Sadre Alam if (req->datalen) 361*7304d190SMd Sadre Alam snandc->qspi->page_rw = true; 362*7304d190SMd Sadre Alam 363*7304d190SMd Sadre Alam if (req->ooblen) 364*7304d190SMd Sadre Alam snandc->qspi->oob_rw = true; 365*7304d190SMd Sadre Alam 366*7304d190SMd Sadre Alam if (req->mode == MTD_OPS_RAW) 367*7304d190SMd Sadre Alam snandc->qspi->raw_rw = true; 368*7304d190SMd Sadre Alam 369*7304d190SMd Sadre Alam return 0; 370*7304d190SMd Sadre Alam } 371*7304d190SMd Sadre Alam 372*7304d190SMd Sadre Alam static int qcom_spi_ecc_finish_io_req_pipelined(struct nand_device *nand, 373*7304d190SMd Sadre Alam struct nand_page_io_req *req) 374*7304d190SMd Sadre Alam { 375*7304d190SMd Sadre Alam struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); 376*7304d190SMd Sadre Alam struct mtd_info *mtd = nanddev_to_mtd(nand); 377*7304d190SMd Sadre Alam 378*7304d190SMd Sadre Alam if (req->mode == MTD_OPS_RAW || req->type != NAND_PAGE_READ) 379*7304d190SMd Sadre Alam return 0; 380*7304d190SMd Sadre Alam 381*7304d190SMd Sadre Alam if (snandc->qspi->ecc_stats.failed) 382*7304d190SMd Sadre Alam mtd->ecc_stats.failed += snandc->qspi->ecc_stats.failed; 383*7304d190SMd Sadre Alam else 384*7304d190SMd Sadre Alam mtd->ecc_stats.corrected += snandc->qspi->ecc_stats.corrected; 385*7304d190SMd Sadre Alam 386*7304d190SMd Sadre Alam if (snandc->qspi->ecc_stats.failed) 387*7304d190SMd Sadre Alam return -EBADMSG; 388*7304d190SMd Sadre Alam else 389*7304d190SMd Sadre Alam return snandc->qspi->ecc_stats.bitflips; 390*7304d190SMd Sadre Alam } 391*7304d190SMd Sadre Alam 392*7304d190SMd Sadre Alam static struct nand_ecc_engine_ops qcom_spi_ecc_engine_ops_pipelined = { 393*7304d190SMd Sadre Alam .init_ctx = qcom_spi_ecc_init_ctx_pipelined, 394*7304d190SMd Sadre Alam .cleanup_ctx = qcom_spi_ecc_cleanup_ctx_pipelined, 395*7304d190SMd Sadre Alam .prepare_io_req = qcom_spi_ecc_prepare_io_req_pipelined, 396*7304d190SMd Sadre Alam .finish_io_req = qcom_spi_ecc_finish_io_req_pipelined, 397*7304d190SMd Sadre Alam }; 398*7304d190SMd Sadre Alam 399*7304d190SMd Sadre Alam /* helper to configure location register values */ 400*7304d190SMd Sadre Alam static void qcom_spi_set_read_loc(struct qcom_nand_controller *snandc, int cw, int reg, 401*7304d190SMd Sadre Alam int cw_offset, int read_size, int is_last_read_loc) 402*7304d190SMd Sadre Alam { 403*7304d190SMd Sadre Alam int reg_base = NAND_READ_LOCATION_0; 404*7304d190SMd Sadre Alam int num_cw = snandc->qspi->num_cw; 405*7304d190SMd Sadre Alam 406*7304d190SMd Sadre Alam if (cw == (num_cw - 1)) 407*7304d190SMd Sadre Alam reg_base = NAND_READ_LOCATION_LAST_CW_0; 408*7304d190SMd Sadre Alam 409*7304d190SMd Sadre Alam reg_base += reg * 4; 410*7304d190SMd Sadre Alam 411*7304d190SMd Sadre Alam if (cw == (num_cw - 1)) 412*7304d190SMd Sadre Alam return qcom_spi_set_read_loc_last(snandc, reg_base, cw_offset, 413*7304d190SMd Sadre Alam read_size, is_last_read_loc); 414*7304d190SMd Sadre Alam else 415*7304d190SMd Sadre Alam return qcom_spi_set_read_loc_first(snandc, reg_base, cw_offset, 416*7304d190SMd Sadre Alam read_size, is_last_read_loc); 417*7304d190SMd Sadre Alam } 418*7304d190SMd Sadre Alam 419*7304d190SMd Sadre Alam static void 420*7304d190SMd Sadre Alam qcom_spi_config_cw_read(struct qcom_nand_controller *snandc, bool use_ecc, int cw) 421*7304d190SMd Sadre Alam { 422*7304d190SMd Sadre Alam __le32 *reg = &snandc->regs->read_location0; 423*7304d190SMd Sadre Alam int num_cw = snandc->qspi->num_cw; 424*7304d190SMd Sadre Alam 425*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL); 426*7304d190SMd Sadre Alam if (cw == (num_cw - 1)) { 427*7304d190SMd Sadre Alam reg = &snandc->regs->read_location_last0; 428*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, reg, NAND_READ_LOCATION_LAST_CW_0, 4, 429*7304d190SMd Sadre Alam NAND_BAM_NEXT_SGL); 430*7304d190SMd Sadre Alam } 431*7304d190SMd Sadre Alam 432*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); 433*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); 434*7304d190SMd Sadre Alam 435*7304d190SMd Sadre Alam qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 2, 0); 436*7304d190SMd Sadre Alam qcom_read_reg_dma(snandc, NAND_ERASED_CW_DETECT_STATUS, 1, 437*7304d190SMd Sadre Alam NAND_BAM_NEXT_SGL); 438*7304d190SMd Sadre Alam } 439*7304d190SMd Sadre Alam 440*7304d190SMd Sadre Alam static int qcom_spi_block_erase(struct qcom_nand_controller *snandc) 441*7304d190SMd Sadre Alam { 442*7304d190SMd Sadre Alam struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; 443*7304d190SMd Sadre Alam int ret; 444*7304d190SMd Sadre Alam 445*7304d190SMd Sadre Alam snandc->buf_count = 0; 446*7304d190SMd Sadre Alam snandc->buf_start = 0; 447*7304d190SMd Sadre Alam qcom_clear_read_regs(snandc); 448*7304d190SMd Sadre Alam qcom_clear_bam_transaction(snandc); 449*7304d190SMd Sadre Alam 450*7304d190SMd Sadre Alam snandc->regs->cmd = snandc->qspi->cmd; 451*7304d190SMd Sadre Alam snandc->regs->addr0 = snandc->qspi->addr1; 452*7304d190SMd Sadre Alam snandc->regs->addr1 = snandc->qspi->addr2; 453*7304d190SMd Sadre Alam snandc->regs->cfg0 = cpu_to_le32(ecc_cfg->cfg0_raw & ~(7 << CW_PER_PAGE)); 454*7304d190SMd Sadre Alam snandc->regs->cfg1 = cpu_to_le32(ecc_cfg->cfg1_raw); 455*7304d190SMd Sadre Alam snandc->regs->exec = cpu_to_le32(1); 456*7304d190SMd Sadre Alam 457*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); 458*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); 459*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); 460*7304d190SMd Sadre Alam 461*7304d190SMd Sadre Alam ret = qcom_submit_descs(snandc); 462*7304d190SMd Sadre Alam if (ret) { 463*7304d190SMd Sadre Alam dev_err(snandc->dev, "failure to erase block\n"); 464*7304d190SMd Sadre Alam return ret; 465*7304d190SMd Sadre Alam } 466*7304d190SMd Sadre Alam 467*7304d190SMd Sadre Alam return 0; 468*7304d190SMd Sadre Alam } 469*7304d190SMd Sadre Alam 470*7304d190SMd Sadre Alam static void qcom_spi_config_single_cw_page_read(struct qcom_nand_controller *snandc, 471*7304d190SMd Sadre Alam bool use_ecc, int cw) 472*7304d190SMd Sadre Alam { 473*7304d190SMd Sadre Alam __le32 *reg = &snandc->regs->read_location0; 474*7304d190SMd Sadre Alam int num_cw = snandc->qspi->num_cw; 475*7304d190SMd Sadre Alam 476*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0); 477*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); 478*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr, 479*7304d190SMd Sadre Alam NAND_ERASED_CW_DETECT_CFG, 1, 0); 480*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set, 481*7304d190SMd Sadre Alam NAND_ERASED_CW_DETECT_CFG, 1, 482*7304d190SMd Sadre Alam NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); 483*7304d190SMd Sadre Alam 484*7304d190SMd Sadre Alam if (cw == (num_cw - 1)) { 485*7304d190SMd Sadre Alam reg = &snandc->regs->read_location_last0; 486*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, reg, NAND_READ_LOCATION_LAST_CW_0, 4, NAND_BAM_NEXT_SGL); 487*7304d190SMd Sadre Alam } 488*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); 489*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); 490*7304d190SMd Sadre Alam 491*7304d190SMd Sadre Alam qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 1, 0); 492*7304d190SMd Sadre Alam } 493*7304d190SMd Sadre Alam 494*7304d190SMd Sadre Alam static int qcom_spi_read_last_cw(struct qcom_nand_controller *snandc, 495*7304d190SMd Sadre Alam const struct spi_mem_op *op) 496*7304d190SMd Sadre Alam { 497*7304d190SMd Sadre Alam struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; 498*7304d190SMd Sadre Alam struct mtd_info *mtd = snandc->qspi->mtd; 499*7304d190SMd Sadre Alam int size, ret = 0; 500*7304d190SMd Sadre Alam int col, bbpos; 501*7304d190SMd Sadre Alam u32 cfg0, cfg1, ecc_bch_cfg; 502*7304d190SMd Sadre Alam u32 num_cw = snandc->qspi->num_cw; 503*7304d190SMd Sadre Alam 504*7304d190SMd Sadre Alam qcom_clear_bam_transaction(snandc); 505*7304d190SMd Sadre Alam qcom_clear_read_regs(snandc); 506*7304d190SMd Sadre Alam 507*7304d190SMd Sadre Alam size = ecc_cfg->cw_size; 508*7304d190SMd Sadre Alam col = ecc_cfg->cw_size * (num_cw - 1); 509*7304d190SMd Sadre Alam 510*7304d190SMd Sadre Alam memset(snandc->data_buffer, 0xff, size); 511*7304d190SMd Sadre Alam snandc->regs->addr0 = (snandc->qspi->addr1 | cpu_to_le32(col)); 512*7304d190SMd Sadre Alam snandc->regs->addr1 = snandc->qspi->addr2; 513*7304d190SMd Sadre Alam 514*7304d190SMd Sadre Alam cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | 515*7304d190SMd Sadre Alam 0 << CW_PER_PAGE; 516*7304d190SMd Sadre Alam cfg1 = ecc_cfg->cfg1_raw; 517*7304d190SMd Sadre Alam ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; 518*7304d190SMd Sadre Alam 519*7304d190SMd Sadre Alam snandc->regs->cmd = snandc->qspi->cmd; 520*7304d190SMd Sadre Alam snandc->regs->cfg0 = cpu_to_le32(cfg0); 521*7304d190SMd Sadre Alam snandc->regs->cfg1 = cpu_to_le32(cfg1); 522*7304d190SMd Sadre Alam snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); 523*7304d190SMd Sadre Alam snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus); 524*7304d190SMd Sadre Alam snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus); 525*7304d190SMd Sadre Alam snandc->regs->exec = cpu_to_le32(1); 526*7304d190SMd Sadre Alam 527*7304d190SMd Sadre Alam qcom_spi_set_read_loc(snandc, num_cw - 1, 0, 0, ecc_cfg->cw_size, 1); 528*7304d190SMd Sadre Alam 529*7304d190SMd Sadre Alam qcom_spi_config_single_cw_page_read(snandc, false, num_cw - 1); 530*7304d190SMd Sadre Alam 531*7304d190SMd Sadre Alam qcom_read_data_dma(snandc, FLASH_BUF_ACC, snandc->data_buffer, size, 0); 532*7304d190SMd Sadre Alam 533*7304d190SMd Sadre Alam ret = qcom_submit_descs(snandc); 534*7304d190SMd Sadre Alam if (ret) { 535*7304d190SMd Sadre Alam dev_err(snandc->dev, "failed to read last cw\n"); 536*7304d190SMd Sadre Alam return ret; 537*7304d190SMd Sadre Alam } 538*7304d190SMd Sadre Alam 539*7304d190SMd Sadre Alam qcom_nandc_dev_to_mem(snandc, true); 540*7304d190SMd Sadre Alam u32 flash = le32_to_cpu(snandc->reg_read_buf[0]); 541*7304d190SMd Sadre Alam 542*7304d190SMd Sadre Alam if (flash & (FS_OP_ERR | FS_MPU_ERR)) 543*7304d190SMd Sadre Alam return -EIO; 544*7304d190SMd Sadre Alam 545*7304d190SMd Sadre Alam bbpos = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1); 546*7304d190SMd Sadre Alam 547*7304d190SMd Sadre Alam if (snandc->data_buffer[bbpos] == 0xff) 548*7304d190SMd Sadre Alam snandc->data_buffer[bbpos + 1] = 0xff; 549*7304d190SMd Sadre Alam if (snandc->data_buffer[bbpos] != 0xff) 550*7304d190SMd Sadre Alam snandc->data_buffer[bbpos + 1] = snandc->data_buffer[bbpos]; 551*7304d190SMd Sadre Alam 552*7304d190SMd Sadre Alam memcpy(op->data.buf.in, snandc->data_buffer + bbpos, op->data.nbytes); 553*7304d190SMd Sadre Alam 554*7304d190SMd Sadre Alam return ret; 555*7304d190SMd Sadre Alam } 556*7304d190SMd Sadre Alam 557*7304d190SMd Sadre Alam static int qcom_spi_check_error(struct qcom_nand_controller *snandc, u8 *data_buf, u8 *oob_buf) 558*7304d190SMd Sadre Alam { 559*7304d190SMd Sadre Alam struct snandc_read_status *buf; 560*7304d190SMd Sadre Alam struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; 561*7304d190SMd Sadre Alam int i, num_cw = snandc->qspi->num_cw; 562*7304d190SMd Sadre Alam bool flash_op_err = false, erased; 563*7304d190SMd Sadre Alam unsigned int max_bitflips = 0; 564*7304d190SMd Sadre Alam unsigned int uncorrectable_cws = 0; 565*7304d190SMd Sadre Alam 566*7304d190SMd Sadre Alam snandc->qspi->ecc_stats.failed = 0; 567*7304d190SMd Sadre Alam snandc->qspi->ecc_stats.corrected = 0; 568*7304d190SMd Sadre Alam 569*7304d190SMd Sadre Alam qcom_nandc_dev_to_mem(snandc, true); 570*7304d190SMd Sadre Alam buf = (struct snandc_read_status *)snandc->reg_read_buf; 571*7304d190SMd Sadre Alam 572*7304d190SMd Sadre Alam for (i = 0; i < num_cw; i++, buf++) { 573*7304d190SMd Sadre Alam u32 flash, buffer, erased_cw; 574*7304d190SMd Sadre Alam int data_len, oob_len; 575*7304d190SMd Sadre Alam 576*7304d190SMd Sadre Alam if (i == (num_cw - 1)) { 577*7304d190SMd Sadre Alam data_len = NANDC_STEP_SIZE - ((num_cw - 1) << 2); 578*7304d190SMd Sadre Alam oob_len = num_cw << 2; 579*7304d190SMd Sadre Alam } else { 580*7304d190SMd Sadre Alam data_len = ecc_cfg->cw_data; 581*7304d190SMd Sadre Alam oob_len = 0; 582*7304d190SMd Sadre Alam } 583*7304d190SMd Sadre Alam 584*7304d190SMd Sadre Alam flash = le32_to_cpu(buf->snandc_flash); 585*7304d190SMd Sadre Alam buffer = le32_to_cpu(buf->snandc_buffer); 586*7304d190SMd Sadre Alam erased_cw = le32_to_cpu(buf->snandc_erased_cw); 587*7304d190SMd Sadre Alam 588*7304d190SMd Sadre Alam if ((flash & FS_OP_ERR) && (buffer & BS_UNCORRECTABLE_BIT)) { 589*7304d190SMd Sadre Alam if (ecc_cfg->bch_enabled) 590*7304d190SMd Sadre Alam erased = (erased_cw & ERASED_CW) == ERASED_CW; 591*7304d190SMd Sadre Alam else 592*7304d190SMd Sadre Alam erased = false; 593*7304d190SMd Sadre Alam 594*7304d190SMd Sadre Alam if (!erased) 595*7304d190SMd Sadre Alam uncorrectable_cws |= BIT(i); 596*7304d190SMd Sadre Alam 597*7304d190SMd Sadre Alam } else if (flash & (FS_OP_ERR | FS_MPU_ERR)) { 598*7304d190SMd Sadre Alam flash_op_err = true; 599*7304d190SMd Sadre Alam } else { 600*7304d190SMd Sadre Alam unsigned int stat; 601*7304d190SMd Sadre Alam 602*7304d190SMd Sadre Alam stat = buffer & BS_CORRECTABLE_ERR_MSK; 603*7304d190SMd Sadre Alam snandc->qspi->ecc_stats.corrected += stat; 604*7304d190SMd Sadre Alam max_bitflips = max(max_bitflips, stat); 605*7304d190SMd Sadre Alam } 606*7304d190SMd Sadre Alam 607*7304d190SMd Sadre Alam if (data_buf) 608*7304d190SMd Sadre Alam data_buf += data_len; 609*7304d190SMd Sadre Alam if (oob_buf) 610*7304d190SMd Sadre Alam oob_buf += oob_len + ecc_cfg->bytes; 611*7304d190SMd Sadre Alam } 612*7304d190SMd Sadre Alam 613*7304d190SMd Sadre Alam if (flash_op_err) 614*7304d190SMd Sadre Alam return -EIO; 615*7304d190SMd Sadre Alam 616*7304d190SMd Sadre Alam if (!uncorrectable_cws) 617*7304d190SMd Sadre Alam snandc->qspi->ecc_stats.bitflips = max_bitflips; 618*7304d190SMd Sadre Alam else 619*7304d190SMd Sadre Alam snandc->qspi->ecc_stats.failed++; 620*7304d190SMd Sadre Alam 621*7304d190SMd Sadre Alam return 0; 622*7304d190SMd Sadre Alam } 623*7304d190SMd Sadre Alam 624*7304d190SMd Sadre Alam static int qcom_spi_check_raw_flash_errors(struct qcom_nand_controller *snandc, int cw_cnt) 625*7304d190SMd Sadre Alam { 626*7304d190SMd Sadre Alam int i; 627*7304d190SMd Sadre Alam 628*7304d190SMd Sadre Alam qcom_nandc_dev_to_mem(snandc, true); 629*7304d190SMd Sadre Alam 630*7304d190SMd Sadre Alam for (i = 0; i < cw_cnt; i++) { 631*7304d190SMd Sadre Alam u32 flash = le32_to_cpu(snandc->reg_read_buf[i]); 632*7304d190SMd Sadre Alam 633*7304d190SMd Sadre Alam if (flash & (FS_OP_ERR | FS_MPU_ERR)) 634*7304d190SMd Sadre Alam return -EIO; 635*7304d190SMd Sadre Alam } 636*7304d190SMd Sadre Alam 637*7304d190SMd Sadre Alam return 0; 638*7304d190SMd Sadre Alam } 639*7304d190SMd Sadre Alam 640*7304d190SMd Sadre Alam static int qcom_spi_read_cw_raw(struct qcom_nand_controller *snandc, u8 *data_buf, 641*7304d190SMd Sadre Alam u8 *oob_buf, int cw) 642*7304d190SMd Sadre Alam { 643*7304d190SMd Sadre Alam struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; 644*7304d190SMd Sadre Alam struct mtd_info *mtd = snandc->qspi->mtd; 645*7304d190SMd Sadre Alam int data_size1, data_size2, oob_size1, oob_size2; 646*7304d190SMd Sadre Alam int ret, reg_off = FLASH_BUF_ACC, read_loc = 0; 647*7304d190SMd Sadre Alam int raw_cw = cw; 648*7304d190SMd Sadre Alam u32 cfg0, cfg1, ecc_bch_cfg, num_cw = snandc->qspi->num_cw; 649*7304d190SMd Sadre Alam int col; 650*7304d190SMd Sadre Alam 651*7304d190SMd Sadre Alam snandc->buf_count = 0; 652*7304d190SMd Sadre Alam snandc->buf_start = 0; 653*7304d190SMd Sadre Alam qcom_clear_read_regs(snandc); 654*7304d190SMd Sadre Alam qcom_clear_bam_transaction(snandc); 655*7304d190SMd Sadre Alam raw_cw = num_cw - 1; 656*7304d190SMd Sadre Alam 657*7304d190SMd Sadre Alam cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | 658*7304d190SMd Sadre Alam 0 << CW_PER_PAGE; 659*7304d190SMd Sadre Alam cfg1 = ecc_cfg->cfg1_raw; 660*7304d190SMd Sadre Alam ecc_bch_cfg = ECC_CFG_ECC_DISABLE; 661*7304d190SMd Sadre Alam 662*7304d190SMd Sadre Alam col = ecc_cfg->cw_size * cw; 663*7304d190SMd Sadre Alam 664*7304d190SMd Sadre Alam snandc->regs->addr0 = (snandc->qspi->addr1 | cpu_to_le32(col)); 665*7304d190SMd Sadre Alam snandc->regs->addr1 = snandc->qspi->addr2; 666*7304d190SMd Sadre Alam snandc->regs->cmd = snandc->qspi->cmd; 667*7304d190SMd Sadre Alam snandc->regs->cfg0 = cpu_to_le32(cfg0); 668*7304d190SMd Sadre Alam snandc->regs->cfg1 = cpu_to_le32(cfg1); 669*7304d190SMd Sadre Alam snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); 670*7304d190SMd Sadre Alam snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus); 671*7304d190SMd Sadre Alam snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus); 672*7304d190SMd Sadre Alam snandc->regs->exec = cpu_to_le32(1); 673*7304d190SMd Sadre Alam 674*7304d190SMd Sadre Alam qcom_spi_set_read_loc(snandc, raw_cw, 0, 0, ecc_cfg->cw_size, 1); 675*7304d190SMd Sadre Alam 676*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0); 677*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); 678*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0); 679*7304d190SMd Sadre Alam 680*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr, 681*7304d190SMd Sadre Alam NAND_ERASED_CW_DETECT_CFG, 1, 0); 682*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set, 683*7304d190SMd Sadre Alam NAND_ERASED_CW_DETECT_CFG, 1, 684*7304d190SMd Sadre Alam NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); 685*7304d190SMd Sadre Alam 686*7304d190SMd Sadre Alam data_size1 = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1); 687*7304d190SMd Sadre Alam oob_size1 = ecc_cfg->bbm_size; 688*7304d190SMd Sadre Alam 689*7304d190SMd Sadre Alam if (cw == (num_cw - 1)) { 690*7304d190SMd Sadre Alam data_size2 = NANDC_STEP_SIZE - data_size1 - 691*7304d190SMd Sadre Alam ((num_cw - 1) * 4); 692*7304d190SMd Sadre Alam oob_size2 = (num_cw * 4) + ecc_cfg->ecc_bytes_hw + 693*7304d190SMd Sadre Alam ecc_cfg->spare_bytes; 694*7304d190SMd Sadre Alam } else { 695*7304d190SMd Sadre Alam data_size2 = ecc_cfg->cw_data - data_size1; 696*7304d190SMd Sadre Alam oob_size2 = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes; 697*7304d190SMd Sadre Alam } 698*7304d190SMd Sadre Alam 699*7304d190SMd Sadre Alam qcom_spi_set_read_loc(snandc, cw, 0, read_loc, data_size1, 0); 700*7304d190SMd Sadre Alam read_loc += data_size1; 701*7304d190SMd Sadre Alam 702*7304d190SMd Sadre Alam qcom_spi_set_read_loc(snandc, cw, 1, read_loc, oob_size1, 0); 703*7304d190SMd Sadre Alam read_loc += oob_size1; 704*7304d190SMd Sadre Alam 705*7304d190SMd Sadre Alam qcom_spi_set_read_loc(snandc, cw, 2, read_loc, data_size2, 0); 706*7304d190SMd Sadre Alam read_loc += data_size2; 707*7304d190SMd Sadre Alam 708*7304d190SMd Sadre Alam qcom_spi_set_read_loc(snandc, cw, 3, read_loc, oob_size2, 1); 709*7304d190SMd Sadre Alam 710*7304d190SMd Sadre Alam qcom_spi_config_cw_read(snandc, false, raw_cw); 711*7304d190SMd Sadre Alam 712*7304d190SMd Sadre Alam qcom_read_data_dma(snandc, reg_off, data_buf, data_size1, 0); 713*7304d190SMd Sadre Alam reg_off += data_size1; 714*7304d190SMd Sadre Alam 715*7304d190SMd Sadre Alam qcom_read_data_dma(snandc, reg_off, oob_buf, oob_size1, 0); 716*7304d190SMd Sadre Alam reg_off += oob_size1; 717*7304d190SMd Sadre Alam 718*7304d190SMd Sadre Alam qcom_read_data_dma(snandc, reg_off, data_buf + data_size1, data_size2, 0); 719*7304d190SMd Sadre Alam reg_off += data_size2; 720*7304d190SMd Sadre Alam 721*7304d190SMd Sadre Alam qcom_read_data_dma(snandc, reg_off, oob_buf + oob_size1, oob_size2, 0); 722*7304d190SMd Sadre Alam 723*7304d190SMd Sadre Alam ret = qcom_submit_descs(snandc); 724*7304d190SMd Sadre Alam if (ret) { 725*7304d190SMd Sadre Alam dev_err(snandc->dev, "failure to read raw cw %d\n", cw); 726*7304d190SMd Sadre Alam return ret; 727*7304d190SMd Sadre Alam } 728*7304d190SMd Sadre Alam 729*7304d190SMd Sadre Alam return qcom_spi_check_raw_flash_errors(snandc, 1); 730*7304d190SMd Sadre Alam } 731*7304d190SMd Sadre Alam 732*7304d190SMd Sadre Alam static int qcom_spi_read_page_raw(struct qcom_nand_controller *snandc, 733*7304d190SMd Sadre Alam const struct spi_mem_op *op) 734*7304d190SMd Sadre Alam { 735*7304d190SMd Sadre Alam struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; 736*7304d190SMd Sadre Alam u8 *data_buf = NULL, *oob_buf = NULL; 737*7304d190SMd Sadre Alam int ret, cw; 738*7304d190SMd Sadre Alam u32 num_cw = snandc->qspi->num_cw; 739*7304d190SMd Sadre Alam 740*7304d190SMd Sadre Alam if (snandc->qspi->page_rw) 741*7304d190SMd Sadre Alam data_buf = op->data.buf.in; 742*7304d190SMd Sadre Alam 743*7304d190SMd Sadre Alam oob_buf = snandc->qspi->oob_buf; 744*7304d190SMd Sadre Alam memset(oob_buf, 0xff, OOB_BUF_SIZE); 745*7304d190SMd Sadre Alam 746*7304d190SMd Sadre Alam for (cw = 0; cw < num_cw; cw++) { 747*7304d190SMd Sadre Alam ret = qcom_spi_read_cw_raw(snandc, data_buf, oob_buf, cw); 748*7304d190SMd Sadre Alam if (ret) 749*7304d190SMd Sadre Alam return ret; 750*7304d190SMd Sadre Alam 751*7304d190SMd Sadre Alam if (data_buf) 752*7304d190SMd Sadre Alam data_buf += ecc_cfg->cw_data; 753*7304d190SMd Sadre Alam if (oob_buf) 754*7304d190SMd Sadre Alam oob_buf += ecc_cfg->bytes; 755*7304d190SMd Sadre Alam } 756*7304d190SMd Sadre Alam 757*7304d190SMd Sadre Alam return 0; 758*7304d190SMd Sadre Alam } 759*7304d190SMd Sadre Alam 760*7304d190SMd Sadre Alam static int qcom_spi_read_page_ecc(struct qcom_nand_controller *snandc, 761*7304d190SMd Sadre Alam const struct spi_mem_op *op) 762*7304d190SMd Sadre Alam { 763*7304d190SMd Sadre Alam struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; 764*7304d190SMd Sadre Alam u8 *data_buf = NULL, *data_buf_start, *oob_buf = NULL, *oob_buf_start; 765*7304d190SMd Sadre Alam int ret, i; 766*7304d190SMd Sadre Alam u32 cfg0, cfg1, ecc_bch_cfg, num_cw = snandc->qspi->num_cw; 767*7304d190SMd Sadre Alam 768*7304d190SMd Sadre Alam data_buf = op->data.buf.in; 769*7304d190SMd Sadre Alam data_buf_start = data_buf; 770*7304d190SMd Sadre Alam 771*7304d190SMd Sadre Alam oob_buf = snandc->qspi->oob_buf; 772*7304d190SMd Sadre Alam oob_buf_start = oob_buf; 773*7304d190SMd Sadre Alam 774*7304d190SMd Sadre Alam snandc->buf_count = 0; 775*7304d190SMd Sadre Alam snandc->buf_start = 0; 776*7304d190SMd Sadre Alam qcom_clear_read_regs(snandc); 777*7304d190SMd Sadre Alam 778*7304d190SMd Sadre Alam cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | 779*7304d190SMd Sadre Alam (num_cw - 1) << CW_PER_PAGE; 780*7304d190SMd Sadre Alam cfg1 = ecc_cfg->cfg1; 781*7304d190SMd Sadre Alam ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; 782*7304d190SMd Sadre Alam 783*7304d190SMd Sadre Alam snandc->regs->addr0 = snandc->qspi->addr1; 784*7304d190SMd Sadre Alam snandc->regs->addr1 = snandc->qspi->addr2; 785*7304d190SMd Sadre Alam snandc->regs->cmd = snandc->qspi->cmd; 786*7304d190SMd Sadre Alam snandc->regs->cfg0 = cpu_to_le32(cfg0); 787*7304d190SMd Sadre Alam snandc->regs->cfg1 = cpu_to_le32(cfg1); 788*7304d190SMd Sadre Alam snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); 789*7304d190SMd Sadre Alam snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus); 790*7304d190SMd Sadre Alam snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus); 791*7304d190SMd Sadre Alam snandc->regs->exec = cpu_to_le32(1); 792*7304d190SMd Sadre Alam 793*7304d190SMd Sadre Alam qcom_spi_set_read_loc(snandc, 0, 0, 0, ecc_cfg->cw_data, 1); 794*7304d190SMd Sadre Alam 795*7304d190SMd Sadre Alam qcom_clear_bam_transaction(snandc); 796*7304d190SMd Sadre Alam 797*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0); 798*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); 799*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr, 800*7304d190SMd Sadre Alam NAND_ERASED_CW_DETECT_CFG, 1, 0); 801*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set, 802*7304d190SMd Sadre Alam NAND_ERASED_CW_DETECT_CFG, 1, 803*7304d190SMd Sadre Alam NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); 804*7304d190SMd Sadre Alam 805*7304d190SMd Sadre Alam for (i = 0; i < num_cw; i++) { 806*7304d190SMd Sadre Alam int data_size, oob_size; 807*7304d190SMd Sadre Alam 808*7304d190SMd Sadre Alam if (i == (num_cw - 1)) { 809*7304d190SMd Sadre Alam data_size = 512 - ((num_cw - 1) << 2); 810*7304d190SMd Sadre Alam oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw + 811*7304d190SMd Sadre Alam ecc_cfg->spare_bytes; 812*7304d190SMd Sadre Alam } else { 813*7304d190SMd Sadre Alam data_size = ecc_cfg->cw_data; 814*7304d190SMd Sadre Alam oob_size = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes; 815*7304d190SMd Sadre Alam } 816*7304d190SMd Sadre Alam 817*7304d190SMd Sadre Alam if (data_buf && oob_buf) { 818*7304d190SMd Sadre Alam qcom_spi_set_read_loc(snandc, i, 0, 0, data_size, 0); 819*7304d190SMd Sadre Alam qcom_spi_set_read_loc(snandc, i, 1, data_size, oob_size, 1); 820*7304d190SMd Sadre Alam } else if (data_buf) { 821*7304d190SMd Sadre Alam qcom_spi_set_read_loc(snandc, i, 0, 0, data_size, 1); 822*7304d190SMd Sadre Alam } else { 823*7304d190SMd Sadre Alam qcom_spi_set_read_loc(snandc, i, 0, data_size, oob_size, 1); 824*7304d190SMd Sadre Alam } 825*7304d190SMd Sadre Alam 826*7304d190SMd Sadre Alam qcom_spi_config_cw_read(snandc, true, i); 827*7304d190SMd Sadre Alam 828*7304d190SMd Sadre Alam if (data_buf) 829*7304d190SMd Sadre Alam qcom_read_data_dma(snandc, FLASH_BUF_ACC, data_buf, 830*7304d190SMd Sadre Alam data_size, 0); 831*7304d190SMd Sadre Alam if (oob_buf) { 832*7304d190SMd Sadre Alam int j; 833*7304d190SMd Sadre Alam 834*7304d190SMd Sadre Alam for (j = 0; j < ecc_cfg->bbm_size; j++) 835*7304d190SMd Sadre Alam *oob_buf++ = 0xff; 836*7304d190SMd Sadre Alam 837*7304d190SMd Sadre Alam qcom_read_data_dma(snandc, FLASH_BUF_ACC + data_size, 838*7304d190SMd Sadre Alam oob_buf, oob_size, 0); 839*7304d190SMd Sadre Alam } 840*7304d190SMd Sadre Alam 841*7304d190SMd Sadre Alam if (data_buf) 842*7304d190SMd Sadre Alam data_buf += data_size; 843*7304d190SMd Sadre Alam if (oob_buf) 844*7304d190SMd Sadre Alam oob_buf += oob_size; 845*7304d190SMd Sadre Alam } 846*7304d190SMd Sadre Alam 847*7304d190SMd Sadre Alam ret = qcom_submit_descs(snandc); 848*7304d190SMd Sadre Alam if (ret) { 849*7304d190SMd Sadre Alam dev_err(snandc->dev, "failure to read page\n"); 850*7304d190SMd Sadre Alam return ret; 851*7304d190SMd Sadre Alam } 852*7304d190SMd Sadre Alam 853*7304d190SMd Sadre Alam return qcom_spi_check_error(snandc, data_buf_start, oob_buf_start); 854*7304d190SMd Sadre Alam } 855*7304d190SMd Sadre Alam 856*7304d190SMd Sadre Alam static int qcom_spi_read_page_oob(struct qcom_nand_controller *snandc, 857*7304d190SMd Sadre Alam const struct spi_mem_op *op) 858*7304d190SMd Sadre Alam { 859*7304d190SMd Sadre Alam struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; 860*7304d190SMd Sadre Alam u8 *data_buf = NULL, *data_buf_start, *oob_buf = NULL, *oob_buf_start; 861*7304d190SMd Sadre Alam int ret, i; 862*7304d190SMd Sadre Alam u32 cfg0, cfg1, ecc_bch_cfg, num_cw = snandc->qspi->num_cw; 863*7304d190SMd Sadre Alam 864*7304d190SMd Sadre Alam oob_buf = op->data.buf.in; 865*7304d190SMd Sadre Alam oob_buf_start = oob_buf; 866*7304d190SMd Sadre Alam 867*7304d190SMd Sadre Alam data_buf_start = data_buf; 868*7304d190SMd Sadre Alam 869*7304d190SMd Sadre Alam snandc->buf_count = 0; 870*7304d190SMd Sadre Alam snandc->buf_start = 0; 871*7304d190SMd Sadre Alam qcom_clear_read_regs(snandc); 872*7304d190SMd Sadre Alam qcom_clear_bam_transaction(snandc); 873*7304d190SMd Sadre Alam 874*7304d190SMd Sadre Alam cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | 875*7304d190SMd Sadre Alam (num_cw - 1) << CW_PER_PAGE; 876*7304d190SMd Sadre Alam cfg1 = ecc_cfg->cfg1; 877*7304d190SMd Sadre Alam ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; 878*7304d190SMd Sadre Alam 879*7304d190SMd Sadre Alam snandc->regs->addr0 = snandc->qspi->addr1; 880*7304d190SMd Sadre Alam snandc->regs->addr1 = snandc->qspi->addr2; 881*7304d190SMd Sadre Alam snandc->regs->cmd = snandc->qspi->cmd; 882*7304d190SMd Sadre Alam snandc->regs->cfg0 = cpu_to_le32(cfg0); 883*7304d190SMd Sadre Alam snandc->regs->cfg1 = cpu_to_le32(cfg1); 884*7304d190SMd Sadre Alam snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); 885*7304d190SMd Sadre Alam snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus); 886*7304d190SMd Sadre Alam snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus); 887*7304d190SMd Sadre Alam snandc->regs->exec = cpu_to_le32(1); 888*7304d190SMd Sadre Alam 889*7304d190SMd Sadre Alam qcom_spi_set_read_loc(snandc, 0, 0, 0, ecc_cfg->cw_data, 1); 890*7304d190SMd Sadre Alam 891*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0); 892*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); 893*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr, 894*7304d190SMd Sadre Alam NAND_ERASED_CW_DETECT_CFG, 1, 0); 895*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set, 896*7304d190SMd Sadre Alam NAND_ERASED_CW_DETECT_CFG, 1, 897*7304d190SMd Sadre Alam NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); 898*7304d190SMd Sadre Alam 899*7304d190SMd Sadre Alam for (i = 0; i < num_cw; i++) { 900*7304d190SMd Sadre Alam int data_size, oob_size; 901*7304d190SMd Sadre Alam 902*7304d190SMd Sadre Alam if (i == (num_cw - 1)) { 903*7304d190SMd Sadre Alam data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2); 904*7304d190SMd Sadre Alam oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw + 905*7304d190SMd Sadre Alam ecc_cfg->spare_bytes; 906*7304d190SMd Sadre Alam } else { 907*7304d190SMd Sadre Alam data_size = ecc_cfg->cw_data; 908*7304d190SMd Sadre Alam oob_size = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes; 909*7304d190SMd Sadre Alam } 910*7304d190SMd Sadre Alam 911*7304d190SMd Sadre Alam qcom_spi_set_read_loc(snandc, i, 0, data_size, oob_size, 1); 912*7304d190SMd Sadre Alam 913*7304d190SMd Sadre Alam qcom_spi_config_cw_read(snandc, true, i); 914*7304d190SMd Sadre Alam 915*7304d190SMd Sadre Alam if (oob_buf) { 916*7304d190SMd Sadre Alam int j; 917*7304d190SMd Sadre Alam 918*7304d190SMd Sadre Alam for (j = 0; j < ecc_cfg->bbm_size; j++) 919*7304d190SMd Sadre Alam *oob_buf++ = 0xff; 920*7304d190SMd Sadre Alam 921*7304d190SMd Sadre Alam qcom_read_data_dma(snandc, FLASH_BUF_ACC + data_size, 922*7304d190SMd Sadre Alam oob_buf, oob_size, 0); 923*7304d190SMd Sadre Alam } 924*7304d190SMd Sadre Alam 925*7304d190SMd Sadre Alam if (oob_buf) 926*7304d190SMd Sadre Alam oob_buf += oob_size; 927*7304d190SMd Sadre Alam } 928*7304d190SMd Sadre Alam 929*7304d190SMd Sadre Alam ret = qcom_submit_descs(snandc); 930*7304d190SMd Sadre Alam if (ret) { 931*7304d190SMd Sadre Alam dev_err(snandc->dev, "failure to read oob\n"); 932*7304d190SMd Sadre Alam return ret; 933*7304d190SMd Sadre Alam } 934*7304d190SMd Sadre Alam 935*7304d190SMd Sadre Alam return qcom_spi_check_error(snandc, data_buf_start, oob_buf_start); 936*7304d190SMd Sadre Alam } 937*7304d190SMd Sadre Alam 938*7304d190SMd Sadre Alam static int qcom_spi_read_page(struct qcom_nand_controller *snandc, 939*7304d190SMd Sadre Alam const struct spi_mem_op *op) 940*7304d190SMd Sadre Alam { 941*7304d190SMd Sadre Alam if (snandc->qspi->page_rw && snandc->qspi->raw_rw) 942*7304d190SMd Sadre Alam return qcom_spi_read_page_raw(snandc, op); 943*7304d190SMd Sadre Alam 944*7304d190SMd Sadre Alam if (snandc->qspi->page_rw) 945*7304d190SMd Sadre Alam return qcom_spi_read_page_ecc(snandc, op); 946*7304d190SMd Sadre Alam 947*7304d190SMd Sadre Alam if (snandc->qspi->oob_rw && snandc->qspi->raw_rw) 948*7304d190SMd Sadre Alam return qcom_spi_read_last_cw(snandc, op); 949*7304d190SMd Sadre Alam 950*7304d190SMd Sadre Alam if (snandc->qspi->oob_rw) 951*7304d190SMd Sadre Alam return qcom_spi_read_page_oob(snandc, op); 952*7304d190SMd Sadre Alam 953*7304d190SMd Sadre Alam return 0; 954*7304d190SMd Sadre Alam } 955*7304d190SMd Sadre Alam 956*7304d190SMd Sadre Alam static void qcom_spi_config_page_write(struct qcom_nand_controller *snandc) 957*7304d190SMd Sadre Alam { 958*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0); 959*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0); 960*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 961*7304d190SMd Sadre Alam 1, NAND_BAM_NEXT_SGL); 962*7304d190SMd Sadre Alam } 963*7304d190SMd Sadre Alam 964*7304d190SMd Sadre Alam static void qcom_spi_config_cw_write(struct qcom_nand_controller *snandc) 965*7304d190SMd Sadre Alam { 966*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); 967*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); 968*7304d190SMd Sadre Alam qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); 969*7304d190SMd Sadre Alam 970*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0); 971*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->clrreadstatus, NAND_READ_STATUS, 1, 972*7304d190SMd Sadre Alam NAND_BAM_NEXT_SGL); 973*7304d190SMd Sadre Alam } 974*7304d190SMd Sadre Alam 975*7304d190SMd Sadre Alam static int qcom_spi_program_raw(struct qcom_nand_controller *snandc, 976*7304d190SMd Sadre Alam const struct spi_mem_op *op) 977*7304d190SMd Sadre Alam { 978*7304d190SMd Sadre Alam struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; 979*7304d190SMd Sadre Alam struct mtd_info *mtd = snandc->qspi->mtd; 980*7304d190SMd Sadre Alam u8 *data_buf = NULL, *oob_buf = NULL; 981*7304d190SMd Sadre Alam int i, ret; 982*7304d190SMd Sadre Alam int num_cw = snandc->qspi->num_cw; 983*7304d190SMd Sadre Alam u32 cfg0, cfg1, ecc_bch_cfg; 984*7304d190SMd Sadre Alam 985*7304d190SMd Sadre Alam cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | 986*7304d190SMd Sadre Alam (num_cw - 1) << CW_PER_PAGE; 987*7304d190SMd Sadre Alam cfg1 = ecc_cfg->cfg1_raw; 988*7304d190SMd Sadre Alam ecc_bch_cfg = ECC_CFG_ECC_DISABLE; 989*7304d190SMd Sadre Alam 990*7304d190SMd Sadre Alam data_buf = snandc->qspi->data_buf; 991*7304d190SMd Sadre Alam 992*7304d190SMd Sadre Alam oob_buf = snandc->qspi->oob_buf; 993*7304d190SMd Sadre Alam memset(oob_buf, 0xff, OOB_BUF_SIZE); 994*7304d190SMd Sadre Alam 995*7304d190SMd Sadre Alam snandc->buf_count = 0; 996*7304d190SMd Sadre Alam snandc->buf_start = 0; 997*7304d190SMd Sadre Alam qcom_clear_read_regs(snandc); 998*7304d190SMd Sadre Alam qcom_clear_bam_transaction(snandc); 999*7304d190SMd Sadre Alam 1000*7304d190SMd Sadre Alam snandc->regs->addr0 = snandc->qspi->addr1; 1001*7304d190SMd Sadre Alam snandc->regs->addr1 = snandc->qspi->addr2; 1002*7304d190SMd Sadre Alam snandc->regs->cmd = snandc->qspi->cmd; 1003*7304d190SMd Sadre Alam snandc->regs->cfg0 = cpu_to_le32(cfg0); 1004*7304d190SMd Sadre Alam snandc->regs->cfg1 = cpu_to_le32(cfg1); 1005*7304d190SMd Sadre Alam snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); 1006*7304d190SMd Sadre Alam snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus); 1007*7304d190SMd Sadre Alam snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus); 1008*7304d190SMd Sadre Alam snandc->regs->exec = cpu_to_le32(1); 1009*7304d190SMd Sadre Alam 1010*7304d190SMd Sadre Alam qcom_spi_config_page_write(snandc); 1011*7304d190SMd Sadre Alam 1012*7304d190SMd Sadre Alam for (i = 0; i < num_cw; i++) { 1013*7304d190SMd Sadre Alam int data_size1, data_size2, oob_size1, oob_size2; 1014*7304d190SMd Sadre Alam int reg_off = FLASH_BUF_ACC; 1015*7304d190SMd Sadre Alam 1016*7304d190SMd Sadre Alam data_size1 = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1); 1017*7304d190SMd Sadre Alam oob_size1 = ecc_cfg->bbm_size; 1018*7304d190SMd Sadre Alam 1019*7304d190SMd Sadre Alam if (i == (num_cw - 1)) { 1020*7304d190SMd Sadre Alam data_size2 = NANDC_STEP_SIZE - data_size1 - 1021*7304d190SMd Sadre Alam ((num_cw - 1) << 2); 1022*7304d190SMd Sadre Alam oob_size2 = (num_cw << 2) + ecc_cfg->ecc_bytes_hw + 1023*7304d190SMd Sadre Alam ecc_cfg->spare_bytes; 1024*7304d190SMd Sadre Alam } else { 1025*7304d190SMd Sadre Alam data_size2 = ecc_cfg->cw_data - data_size1; 1026*7304d190SMd Sadre Alam oob_size2 = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes; 1027*7304d190SMd Sadre Alam } 1028*7304d190SMd Sadre Alam 1029*7304d190SMd Sadre Alam qcom_write_data_dma(snandc, reg_off, data_buf, data_size1, 1030*7304d190SMd Sadre Alam NAND_BAM_NO_EOT); 1031*7304d190SMd Sadre Alam reg_off += data_size1; 1032*7304d190SMd Sadre Alam data_buf += data_size1; 1033*7304d190SMd Sadre Alam 1034*7304d190SMd Sadre Alam qcom_write_data_dma(snandc, reg_off, oob_buf, oob_size1, 1035*7304d190SMd Sadre Alam NAND_BAM_NO_EOT); 1036*7304d190SMd Sadre Alam oob_buf += oob_size1; 1037*7304d190SMd Sadre Alam reg_off += oob_size1; 1038*7304d190SMd Sadre Alam 1039*7304d190SMd Sadre Alam qcom_write_data_dma(snandc, reg_off, data_buf, data_size2, 1040*7304d190SMd Sadre Alam NAND_BAM_NO_EOT); 1041*7304d190SMd Sadre Alam reg_off += data_size2; 1042*7304d190SMd Sadre Alam data_buf += data_size2; 1043*7304d190SMd Sadre Alam 1044*7304d190SMd Sadre Alam qcom_write_data_dma(snandc, reg_off, oob_buf, oob_size2, 0); 1045*7304d190SMd Sadre Alam oob_buf += oob_size2; 1046*7304d190SMd Sadre Alam 1047*7304d190SMd Sadre Alam qcom_spi_config_cw_write(snandc); 1048*7304d190SMd Sadre Alam } 1049*7304d190SMd Sadre Alam 1050*7304d190SMd Sadre Alam ret = qcom_submit_descs(snandc); 1051*7304d190SMd Sadre Alam if (ret) { 1052*7304d190SMd Sadre Alam dev_err(snandc->dev, "failure to write raw page\n"); 1053*7304d190SMd Sadre Alam return ret; 1054*7304d190SMd Sadre Alam } 1055*7304d190SMd Sadre Alam 1056*7304d190SMd Sadre Alam return 0; 1057*7304d190SMd Sadre Alam } 1058*7304d190SMd Sadre Alam 1059*7304d190SMd Sadre Alam static int qcom_spi_program_ecc(struct qcom_nand_controller *snandc, 1060*7304d190SMd Sadre Alam const struct spi_mem_op *op) 1061*7304d190SMd Sadre Alam { 1062*7304d190SMd Sadre Alam struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; 1063*7304d190SMd Sadre Alam u8 *data_buf = NULL, *oob_buf = NULL; 1064*7304d190SMd Sadre Alam int i, ret; 1065*7304d190SMd Sadre Alam int num_cw = snandc->qspi->num_cw; 1066*7304d190SMd Sadre Alam u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg; 1067*7304d190SMd Sadre Alam 1068*7304d190SMd Sadre Alam cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | 1069*7304d190SMd Sadre Alam (num_cw - 1) << CW_PER_PAGE; 1070*7304d190SMd Sadre Alam cfg1 = ecc_cfg->cfg1; 1071*7304d190SMd Sadre Alam ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; 1072*7304d190SMd Sadre Alam ecc_buf_cfg = ecc_cfg->ecc_buf_cfg; 1073*7304d190SMd Sadre Alam 1074*7304d190SMd Sadre Alam if (snandc->qspi->data_buf) 1075*7304d190SMd Sadre Alam data_buf = snandc->qspi->data_buf; 1076*7304d190SMd Sadre Alam 1077*7304d190SMd Sadre Alam oob_buf = snandc->qspi->oob_buf; 1078*7304d190SMd Sadre Alam 1079*7304d190SMd Sadre Alam snandc->buf_count = 0; 1080*7304d190SMd Sadre Alam snandc->buf_start = 0; 1081*7304d190SMd Sadre Alam qcom_clear_read_regs(snandc); 1082*7304d190SMd Sadre Alam qcom_clear_bam_transaction(snandc); 1083*7304d190SMd Sadre Alam 1084*7304d190SMd Sadre Alam snandc->regs->addr0 = snandc->qspi->addr1; 1085*7304d190SMd Sadre Alam snandc->regs->addr1 = snandc->qspi->addr2; 1086*7304d190SMd Sadre Alam snandc->regs->cmd = snandc->qspi->cmd; 1087*7304d190SMd Sadre Alam snandc->regs->cfg0 = cpu_to_le32(cfg0); 1088*7304d190SMd Sadre Alam snandc->regs->cfg1 = cpu_to_le32(cfg1); 1089*7304d190SMd Sadre Alam snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); 1090*7304d190SMd Sadre Alam snandc->regs->ecc_buf_cfg = cpu_to_le32(ecc_buf_cfg); 1091*7304d190SMd Sadre Alam snandc->regs->exec = cpu_to_le32(1); 1092*7304d190SMd Sadre Alam 1093*7304d190SMd Sadre Alam qcom_spi_config_page_write(snandc); 1094*7304d190SMd Sadre Alam 1095*7304d190SMd Sadre Alam for (i = 0; i < num_cw; i++) { 1096*7304d190SMd Sadre Alam int data_size, oob_size; 1097*7304d190SMd Sadre Alam 1098*7304d190SMd Sadre Alam if (i == (num_cw - 1)) { 1099*7304d190SMd Sadre Alam data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2); 1100*7304d190SMd Sadre Alam oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw + 1101*7304d190SMd Sadre Alam ecc_cfg->spare_bytes; 1102*7304d190SMd Sadre Alam } else { 1103*7304d190SMd Sadre Alam data_size = ecc_cfg->cw_data; 1104*7304d190SMd Sadre Alam oob_size = ecc_cfg->bytes; 1105*7304d190SMd Sadre Alam } 1106*7304d190SMd Sadre Alam 1107*7304d190SMd Sadre Alam if (data_buf) 1108*7304d190SMd Sadre Alam qcom_write_data_dma(snandc, FLASH_BUF_ACC, data_buf, data_size, 1109*7304d190SMd Sadre Alam i == (num_cw - 1) ? NAND_BAM_NO_EOT : 0); 1110*7304d190SMd Sadre Alam 1111*7304d190SMd Sadre Alam if (i == (num_cw - 1)) { 1112*7304d190SMd Sadre Alam if (oob_buf) { 1113*7304d190SMd Sadre Alam oob_buf += ecc_cfg->bbm_size; 1114*7304d190SMd Sadre Alam qcom_write_data_dma(snandc, FLASH_BUF_ACC + data_size, 1115*7304d190SMd Sadre Alam oob_buf, oob_size, 0); 1116*7304d190SMd Sadre Alam } 1117*7304d190SMd Sadre Alam } 1118*7304d190SMd Sadre Alam 1119*7304d190SMd Sadre Alam qcom_spi_config_cw_write(snandc); 1120*7304d190SMd Sadre Alam 1121*7304d190SMd Sadre Alam if (data_buf) 1122*7304d190SMd Sadre Alam data_buf += data_size; 1123*7304d190SMd Sadre Alam if (oob_buf) 1124*7304d190SMd Sadre Alam oob_buf += oob_size; 1125*7304d190SMd Sadre Alam } 1126*7304d190SMd Sadre Alam 1127*7304d190SMd Sadre Alam ret = qcom_submit_descs(snandc); 1128*7304d190SMd Sadre Alam if (ret) { 1129*7304d190SMd Sadre Alam dev_err(snandc->dev, "failure to write page\n"); 1130*7304d190SMd Sadre Alam return ret; 1131*7304d190SMd Sadre Alam } 1132*7304d190SMd Sadre Alam 1133*7304d190SMd Sadre Alam return 0; 1134*7304d190SMd Sadre Alam } 1135*7304d190SMd Sadre Alam 1136*7304d190SMd Sadre Alam static int qcom_spi_program_oob(struct qcom_nand_controller *snandc, 1137*7304d190SMd Sadre Alam const struct spi_mem_op *op) 1138*7304d190SMd Sadre Alam { 1139*7304d190SMd Sadre Alam struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; 1140*7304d190SMd Sadre Alam u8 *oob_buf = NULL; 1141*7304d190SMd Sadre Alam int ret, col, data_size, oob_size; 1142*7304d190SMd Sadre Alam int num_cw = snandc->qspi->num_cw; 1143*7304d190SMd Sadre Alam u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg; 1144*7304d190SMd Sadre Alam 1145*7304d190SMd Sadre Alam cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | 1146*7304d190SMd Sadre Alam (num_cw - 1) << CW_PER_PAGE; 1147*7304d190SMd Sadre Alam cfg1 = ecc_cfg->cfg1; 1148*7304d190SMd Sadre Alam ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; 1149*7304d190SMd Sadre Alam ecc_buf_cfg = ecc_cfg->ecc_buf_cfg; 1150*7304d190SMd Sadre Alam 1151*7304d190SMd Sadre Alam col = ecc_cfg->cw_size * (num_cw - 1); 1152*7304d190SMd Sadre Alam 1153*7304d190SMd Sadre Alam oob_buf = snandc->qspi->data_buf; 1154*7304d190SMd Sadre Alam 1155*7304d190SMd Sadre Alam snandc->buf_count = 0; 1156*7304d190SMd Sadre Alam snandc->buf_start = 0; 1157*7304d190SMd Sadre Alam qcom_clear_read_regs(snandc); 1158*7304d190SMd Sadre Alam qcom_clear_bam_transaction(snandc); 1159*7304d190SMd Sadre Alam snandc->regs->addr0 = (snandc->qspi->addr1 | cpu_to_le32(col)); 1160*7304d190SMd Sadre Alam snandc->regs->addr1 = snandc->qspi->addr2; 1161*7304d190SMd Sadre Alam snandc->regs->cmd = snandc->qspi->cmd; 1162*7304d190SMd Sadre Alam snandc->regs->cfg0 = cpu_to_le32(cfg0); 1163*7304d190SMd Sadre Alam snandc->regs->cfg1 = cpu_to_le32(cfg1); 1164*7304d190SMd Sadre Alam snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg); 1165*7304d190SMd Sadre Alam snandc->regs->ecc_buf_cfg = cpu_to_le32(ecc_buf_cfg); 1166*7304d190SMd Sadre Alam snandc->regs->exec = cpu_to_le32(1); 1167*7304d190SMd Sadre Alam 1168*7304d190SMd Sadre Alam /* calculate the data and oob size for the last codeword/step */ 1169*7304d190SMd Sadre Alam data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2); 1170*7304d190SMd Sadre Alam oob_size = snandc->qspi->mtd->oobavail; 1171*7304d190SMd Sadre Alam 1172*7304d190SMd Sadre Alam memset(snandc->data_buffer, 0xff, ecc_cfg->cw_data); 1173*7304d190SMd Sadre Alam /* override new oob content to last codeword */ 1174*7304d190SMd Sadre Alam mtd_ooblayout_get_databytes(snandc->qspi->mtd, snandc->data_buffer + data_size, 1175*7304d190SMd Sadre Alam oob_buf, 0, snandc->qspi->mtd->oobavail); 1176*7304d190SMd Sadre Alam qcom_spi_config_page_write(snandc); 1177*7304d190SMd Sadre Alam qcom_write_data_dma(snandc, FLASH_BUF_ACC, snandc->data_buffer, data_size + oob_size, 0); 1178*7304d190SMd Sadre Alam qcom_spi_config_cw_write(snandc); 1179*7304d190SMd Sadre Alam 1180*7304d190SMd Sadre Alam ret = qcom_submit_descs(snandc); 1181*7304d190SMd Sadre Alam if (ret) { 1182*7304d190SMd Sadre Alam dev_err(snandc->dev, "failure to write oob\n"); 1183*7304d190SMd Sadre Alam return ret; 1184*7304d190SMd Sadre Alam } 1185*7304d190SMd Sadre Alam 1186*7304d190SMd Sadre Alam return 0; 1187*7304d190SMd Sadre Alam } 1188*7304d190SMd Sadre Alam 1189*7304d190SMd Sadre Alam static int qcom_spi_program_execute(struct qcom_nand_controller *snandc, 1190*7304d190SMd Sadre Alam const struct spi_mem_op *op) 1191*7304d190SMd Sadre Alam { 1192*7304d190SMd Sadre Alam if (snandc->qspi->page_rw && snandc->qspi->raw_rw) 1193*7304d190SMd Sadre Alam return qcom_spi_program_raw(snandc, op); 1194*7304d190SMd Sadre Alam 1195*7304d190SMd Sadre Alam if (snandc->qspi->page_rw) 1196*7304d190SMd Sadre Alam return qcom_spi_program_ecc(snandc, op); 1197*7304d190SMd Sadre Alam 1198*7304d190SMd Sadre Alam if (snandc->qspi->oob_rw) 1199*7304d190SMd Sadre Alam return qcom_spi_program_oob(snandc, op); 1200*7304d190SMd Sadre Alam 1201*7304d190SMd Sadre Alam return 0; 1202*7304d190SMd Sadre Alam } 1203*7304d190SMd Sadre Alam 1204*7304d190SMd Sadre Alam static int qcom_spi_cmd_mapping(struct qcom_nand_controller *snandc, u32 opcode, u32 *cmd) 1205*7304d190SMd Sadre Alam { 1206*7304d190SMd Sadre Alam switch (opcode) { 1207*7304d190SMd Sadre Alam case SPINAND_RESET: 1208*7304d190SMd Sadre Alam *cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_RESET_DEVICE); 1209*7304d190SMd Sadre Alam break; 1210*7304d190SMd Sadre Alam case SPINAND_READID: 1211*7304d190SMd Sadre Alam *cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_FETCH_ID); 1212*7304d190SMd Sadre Alam break; 1213*7304d190SMd Sadre Alam case SPINAND_GET_FEATURE: 1214*7304d190SMd Sadre Alam *cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE); 1215*7304d190SMd Sadre Alam break; 1216*7304d190SMd Sadre Alam case SPINAND_SET_FEATURE: 1217*7304d190SMd Sadre Alam *cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE | 1218*7304d190SMd Sadre Alam QPIC_SET_FEATURE); 1219*7304d190SMd Sadre Alam break; 1220*7304d190SMd Sadre Alam case SPINAND_READ: 1221*7304d190SMd Sadre Alam if (snandc->qspi->raw_rw) { 1222*7304d190SMd Sadre Alam *cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 | 1223*7304d190SMd Sadre Alam SPI_WP | SPI_HOLD | OP_PAGE_READ); 1224*7304d190SMd Sadre Alam } else { 1225*7304d190SMd Sadre Alam *cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 | 1226*7304d190SMd Sadre Alam SPI_WP | SPI_HOLD | OP_PAGE_READ_WITH_ECC); 1227*7304d190SMd Sadre Alam } 1228*7304d190SMd Sadre Alam 1229*7304d190SMd Sadre Alam break; 1230*7304d190SMd Sadre Alam case SPINAND_ERASE: 1231*7304d190SMd Sadre Alam *cmd = OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE | SPI_WP | 1232*7304d190SMd Sadre Alam SPI_HOLD | SPI_TRANSFER_MODE_x1; 1233*7304d190SMd Sadre Alam break; 1234*7304d190SMd Sadre Alam case SPINAND_WRITE_EN: 1235*7304d190SMd Sadre Alam *cmd = SPINAND_WRITE_EN; 1236*7304d190SMd Sadre Alam break; 1237*7304d190SMd Sadre Alam case SPINAND_PROGRAM_EXECUTE: 1238*7304d190SMd Sadre Alam *cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 | 1239*7304d190SMd Sadre Alam SPI_WP | SPI_HOLD | OP_PROGRAM_PAGE); 1240*7304d190SMd Sadre Alam break; 1241*7304d190SMd Sadre Alam case SPINAND_PROGRAM_LOAD: 1242*7304d190SMd Sadre Alam *cmd = SPINAND_PROGRAM_LOAD; 1243*7304d190SMd Sadre Alam break; 1244*7304d190SMd Sadre Alam default: 1245*7304d190SMd Sadre Alam dev_err(snandc->dev, "Opcode not supported: %u\n", opcode); 1246*7304d190SMd Sadre Alam return -EOPNOTSUPP; 1247*7304d190SMd Sadre Alam } 1248*7304d190SMd Sadre Alam 1249*7304d190SMd Sadre Alam return 0; 1250*7304d190SMd Sadre Alam } 1251*7304d190SMd Sadre Alam 1252*7304d190SMd Sadre Alam static int qcom_spi_write_page(struct qcom_nand_controller *snandc, 1253*7304d190SMd Sadre Alam const struct spi_mem_op *op) 1254*7304d190SMd Sadre Alam { 1255*7304d190SMd Sadre Alam int ret; 1256*7304d190SMd Sadre Alam u32 cmd; 1257*7304d190SMd Sadre Alam 1258*7304d190SMd Sadre Alam ret = qcom_spi_cmd_mapping(snandc, op->cmd.opcode, &cmd); 1259*7304d190SMd Sadre Alam if (ret < 0) 1260*7304d190SMd Sadre Alam return ret; 1261*7304d190SMd Sadre Alam 1262*7304d190SMd Sadre Alam if (op->cmd.opcode == SPINAND_PROGRAM_LOAD) 1263*7304d190SMd Sadre Alam snandc->qspi->data_buf = (u8 *)op->data.buf.out; 1264*7304d190SMd Sadre Alam 1265*7304d190SMd Sadre Alam return 0; 1266*7304d190SMd Sadre Alam } 1267*7304d190SMd Sadre Alam 1268*7304d190SMd Sadre Alam static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc, 1269*7304d190SMd Sadre Alam const struct spi_mem_op *op) 1270*7304d190SMd Sadre Alam { 1271*7304d190SMd Sadre Alam struct qpic_snand_op s_op = {}; 1272*7304d190SMd Sadre Alam u32 cmd; 1273*7304d190SMd Sadre Alam int ret, opcode; 1274*7304d190SMd Sadre Alam 1275*7304d190SMd Sadre Alam ret = qcom_spi_cmd_mapping(snandc, op->cmd.opcode, &cmd); 1276*7304d190SMd Sadre Alam if (ret < 0) 1277*7304d190SMd Sadre Alam return ret; 1278*7304d190SMd Sadre Alam 1279*7304d190SMd Sadre Alam s_op.cmd_reg = cmd; 1280*7304d190SMd Sadre Alam s_op.addr1_reg = op->addr.val; 1281*7304d190SMd Sadre Alam s_op.addr2_reg = 0; 1282*7304d190SMd Sadre Alam 1283*7304d190SMd Sadre Alam opcode = op->cmd.opcode; 1284*7304d190SMd Sadre Alam 1285*7304d190SMd Sadre Alam switch (opcode) { 1286*7304d190SMd Sadre Alam case SPINAND_WRITE_EN: 1287*7304d190SMd Sadre Alam return 0; 1288*7304d190SMd Sadre Alam case SPINAND_PROGRAM_EXECUTE: 1289*7304d190SMd Sadre Alam s_op.addr1_reg = op->addr.val << 16; 1290*7304d190SMd Sadre Alam s_op.addr2_reg = op->addr.val >> 16 & 0xff; 1291*7304d190SMd Sadre Alam snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg); 1292*7304d190SMd Sadre Alam snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); 1293*7304d190SMd Sadre Alam snandc->qspi->cmd = cpu_to_le32(cmd); 1294*7304d190SMd Sadre Alam return qcom_spi_program_execute(snandc, op); 1295*7304d190SMd Sadre Alam case SPINAND_READ: 1296*7304d190SMd Sadre Alam s_op.addr1_reg = (op->addr.val << 16); 1297*7304d190SMd Sadre Alam s_op.addr2_reg = op->addr.val >> 16 & 0xff; 1298*7304d190SMd Sadre Alam snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg); 1299*7304d190SMd Sadre Alam snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); 1300*7304d190SMd Sadre Alam snandc->qspi->cmd = cpu_to_le32(cmd); 1301*7304d190SMd Sadre Alam return 0; 1302*7304d190SMd Sadre Alam case SPINAND_ERASE: 1303*7304d190SMd Sadre Alam s_op.addr2_reg = (op->addr.val >> 16) & 0xffff; 1304*7304d190SMd Sadre Alam s_op.addr1_reg = op->addr.val; 1305*7304d190SMd Sadre Alam snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg << 16); 1306*7304d190SMd Sadre Alam snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); 1307*7304d190SMd Sadre Alam snandc->qspi->cmd = cpu_to_le32(cmd); 1308*7304d190SMd Sadre Alam qcom_spi_block_erase(snandc); 1309*7304d190SMd Sadre Alam return 0; 1310*7304d190SMd Sadre Alam default: 1311*7304d190SMd Sadre Alam break; 1312*7304d190SMd Sadre Alam } 1313*7304d190SMd Sadre Alam 1314*7304d190SMd Sadre Alam snandc->buf_count = 0; 1315*7304d190SMd Sadre Alam snandc->buf_start = 0; 1316*7304d190SMd Sadre Alam qcom_clear_read_regs(snandc); 1317*7304d190SMd Sadre Alam qcom_clear_bam_transaction(snandc); 1318*7304d190SMd Sadre Alam 1319*7304d190SMd Sadre Alam snandc->regs->cmd = cpu_to_le32(s_op.cmd_reg); 1320*7304d190SMd Sadre Alam snandc->regs->exec = cpu_to_le32(1); 1321*7304d190SMd Sadre Alam snandc->regs->addr0 = cpu_to_le32(s_op.addr1_reg); 1322*7304d190SMd Sadre Alam snandc->regs->addr1 = cpu_to_le32(s_op.addr2_reg); 1323*7304d190SMd Sadre Alam 1324*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); 1325*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); 1326*7304d190SMd Sadre Alam 1327*7304d190SMd Sadre Alam ret = qcom_submit_descs(snandc); 1328*7304d190SMd Sadre Alam if (ret) 1329*7304d190SMd Sadre Alam dev_err(snandc->dev, "failure in submitting cmd descriptor\n"); 1330*7304d190SMd Sadre Alam 1331*7304d190SMd Sadre Alam return ret; 1332*7304d190SMd Sadre Alam } 1333*7304d190SMd Sadre Alam 1334*7304d190SMd Sadre Alam static int qcom_spi_io_op(struct qcom_nand_controller *snandc, const struct spi_mem_op *op) 1335*7304d190SMd Sadre Alam { 1336*7304d190SMd Sadre Alam int ret, val, opcode; 1337*7304d190SMd Sadre Alam bool copy = false, copy_ftr = false; 1338*7304d190SMd Sadre Alam 1339*7304d190SMd Sadre Alam ret = qcom_spi_send_cmdaddr(snandc, op); 1340*7304d190SMd Sadre Alam if (ret) 1341*7304d190SMd Sadre Alam return ret; 1342*7304d190SMd Sadre Alam 1343*7304d190SMd Sadre Alam snandc->buf_count = 0; 1344*7304d190SMd Sadre Alam snandc->buf_start = 0; 1345*7304d190SMd Sadre Alam qcom_clear_read_regs(snandc); 1346*7304d190SMd Sadre Alam qcom_clear_bam_transaction(snandc); 1347*7304d190SMd Sadre Alam opcode = op->cmd.opcode; 1348*7304d190SMd Sadre Alam 1349*7304d190SMd Sadre Alam switch (opcode) { 1350*7304d190SMd Sadre Alam case SPINAND_READID: 1351*7304d190SMd Sadre Alam snandc->buf_count = 4; 1352*7304d190SMd Sadre Alam qcom_read_reg_dma(snandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); 1353*7304d190SMd Sadre Alam copy = true; 1354*7304d190SMd Sadre Alam break; 1355*7304d190SMd Sadre Alam case SPINAND_GET_FEATURE: 1356*7304d190SMd Sadre Alam snandc->buf_count = 4; 1357*7304d190SMd Sadre Alam qcom_read_reg_dma(snandc, NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL); 1358*7304d190SMd Sadre Alam copy_ftr = true; 1359*7304d190SMd Sadre Alam break; 1360*7304d190SMd Sadre Alam case SPINAND_SET_FEATURE: 1361*7304d190SMd Sadre Alam snandc->regs->flash_feature = cpu_to_le32(*(u32 *)op->data.buf.out); 1362*7304d190SMd Sadre Alam qcom_write_reg_dma(snandc, &snandc->regs->flash_feature, 1363*7304d190SMd Sadre Alam NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL); 1364*7304d190SMd Sadre Alam break; 1365*7304d190SMd Sadre Alam case SPINAND_PROGRAM_EXECUTE: 1366*7304d190SMd Sadre Alam case SPINAND_WRITE_EN: 1367*7304d190SMd Sadre Alam case SPINAND_RESET: 1368*7304d190SMd Sadre Alam case SPINAND_ERASE: 1369*7304d190SMd Sadre Alam case SPINAND_READ: 1370*7304d190SMd Sadre Alam return 0; 1371*7304d190SMd Sadre Alam default: 1372*7304d190SMd Sadre Alam return -EOPNOTSUPP; 1373*7304d190SMd Sadre Alam } 1374*7304d190SMd Sadre Alam 1375*7304d190SMd Sadre Alam ret = qcom_submit_descs(snandc); 1376*7304d190SMd Sadre Alam if (ret) 1377*7304d190SMd Sadre Alam dev_err(snandc->dev, "failure in submitting descriptor for:%d\n", opcode); 1378*7304d190SMd Sadre Alam 1379*7304d190SMd Sadre Alam if (copy) { 1380*7304d190SMd Sadre Alam qcom_nandc_dev_to_mem(snandc, true); 1381*7304d190SMd Sadre Alam memcpy(op->data.buf.in, snandc->reg_read_buf, snandc->buf_count); 1382*7304d190SMd Sadre Alam } 1383*7304d190SMd Sadre Alam 1384*7304d190SMd Sadre Alam if (copy_ftr) { 1385*7304d190SMd Sadre Alam qcom_nandc_dev_to_mem(snandc, true); 1386*7304d190SMd Sadre Alam val = le32_to_cpu(*(__le32 *)snandc->reg_read_buf); 1387*7304d190SMd Sadre Alam val >>= 8; 1388*7304d190SMd Sadre Alam memcpy(op->data.buf.in, &val, snandc->buf_count); 1389*7304d190SMd Sadre Alam } 1390*7304d190SMd Sadre Alam 1391*7304d190SMd Sadre Alam return ret; 1392*7304d190SMd Sadre Alam } 1393*7304d190SMd Sadre Alam 1394*7304d190SMd Sadre Alam static bool qcom_spi_is_page_op(const struct spi_mem_op *op) 1395*7304d190SMd Sadre Alam { 1396*7304d190SMd Sadre Alam if (op->addr.buswidth != 1 && op->addr.buswidth != 2 && op->addr.buswidth != 4) 1397*7304d190SMd Sadre Alam return false; 1398*7304d190SMd Sadre Alam 1399*7304d190SMd Sadre Alam if (op->data.dir == SPI_MEM_DATA_IN) { 1400*7304d190SMd Sadre Alam if (op->addr.buswidth == 4 && op->data.buswidth == 4) 1401*7304d190SMd Sadre Alam return true; 1402*7304d190SMd Sadre Alam 1403*7304d190SMd Sadre Alam if (op->addr.nbytes == 2 && op->addr.buswidth == 1) 1404*7304d190SMd Sadre Alam return true; 1405*7304d190SMd Sadre Alam 1406*7304d190SMd Sadre Alam } else if (op->data.dir == SPI_MEM_DATA_OUT) { 1407*7304d190SMd Sadre Alam if (op->data.buswidth == 4) 1408*7304d190SMd Sadre Alam return true; 1409*7304d190SMd Sadre Alam if (op->addr.nbytes == 2 && op->addr.buswidth == 1) 1410*7304d190SMd Sadre Alam return true; 1411*7304d190SMd Sadre Alam } 1412*7304d190SMd Sadre Alam 1413*7304d190SMd Sadre Alam return false; 1414*7304d190SMd Sadre Alam } 1415*7304d190SMd Sadre Alam 1416*7304d190SMd Sadre Alam static bool qcom_spi_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) 1417*7304d190SMd Sadre Alam { 1418*7304d190SMd Sadre Alam if (!spi_mem_default_supports_op(mem, op)) 1419*7304d190SMd Sadre Alam return false; 1420*7304d190SMd Sadre Alam 1421*7304d190SMd Sadre Alam if (op->cmd.nbytes != 1 || op->cmd.buswidth != 1) 1422*7304d190SMd Sadre Alam return false; 1423*7304d190SMd Sadre Alam 1424*7304d190SMd Sadre Alam if (qcom_spi_is_page_op(op)) 1425*7304d190SMd Sadre Alam return true; 1426*7304d190SMd Sadre Alam 1427*7304d190SMd Sadre Alam return ((!op->addr.nbytes || op->addr.buswidth == 1) && 1428*7304d190SMd Sadre Alam (!op->dummy.nbytes || op->dummy.buswidth == 1) && 1429*7304d190SMd Sadre Alam (!op->data.nbytes || op->data.buswidth == 1)); 1430*7304d190SMd Sadre Alam } 1431*7304d190SMd Sadre Alam 1432*7304d190SMd Sadre Alam static int qcom_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) 1433*7304d190SMd Sadre Alam { 1434*7304d190SMd Sadre Alam struct qcom_nand_controller *snandc = spi_controller_get_devdata(mem->spi->controller); 1435*7304d190SMd Sadre Alam 1436*7304d190SMd Sadre Alam dev_dbg(snandc->dev, "OP %02x ADDR %08llX@%d:%u DATA %d:%u", op->cmd.opcode, 1437*7304d190SMd Sadre Alam op->addr.val, op->addr.buswidth, op->addr.nbytes, 1438*7304d190SMd Sadre Alam op->data.buswidth, op->data.nbytes); 1439*7304d190SMd Sadre Alam 1440*7304d190SMd Sadre Alam if (qcom_spi_is_page_op(op)) { 1441*7304d190SMd Sadre Alam if (op->data.dir == SPI_MEM_DATA_IN) 1442*7304d190SMd Sadre Alam return qcom_spi_read_page(snandc, op); 1443*7304d190SMd Sadre Alam if (op->data.dir == SPI_MEM_DATA_OUT) 1444*7304d190SMd Sadre Alam return qcom_spi_write_page(snandc, op); 1445*7304d190SMd Sadre Alam } else { 1446*7304d190SMd Sadre Alam return qcom_spi_io_op(snandc, op); 1447*7304d190SMd Sadre Alam } 1448*7304d190SMd Sadre Alam 1449*7304d190SMd Sadre Alam return 0; 1450*7304d190SMd Sadre Alam } 1451*7304d190SMd Sadre Alam 1452*7304d190SMd Sadre Alam static const struct spi_controller_mem_ops qcom_spi_mem_ops = { 1453*7304d190SMd Sadre Alam .supports_op = qcom_spi_supports_op, 1454*7304d190SMd Sadre Alam .exec_op = qcom_spi_exec_op, 1455*7304d190SMd Sadre Alam }; 1456*7304d190SMd Sadre Alam 1457*7304d190SMd Sadre Alam static const struct spi_controller_mem_caps qcom_spi_mem_caps = { 1458*7304d190SMd Sadre Alam .ecc = true, 1459*7304d190SMd Sadre Alam }; 1460*7304d190SMd Sadre Alam 1461*7304d190SMd Sadre Alam static int qcom_spi_probe(struct platform_device *pdev) 1462*7304d190SMd Sadre Alam { 1463*7304d190SMd Sadre Alam struct device *dev = &pdev->dev; 1464*7304d190SMd Sadre Alam struct spi_controller *ctlr; 1465*7304d190SMd Sadre Alam struct qcom_nand_controller *snandc; 1466*7304d190SMd Sadre Alam struct qpic_spi_nand *qspi; 1467*7304d190SMd Sadre Alam struct qpic_ecc *ecc; 1468*7304d190SMd Sadre Alam struct resource *res; 1469*7304d190SMd Sadre Alam const void *dev_data; 1470*7304d190SMd Sadre Alam int ret; 1471*7304d190SMd Sadre Alam 1472*7304d190SMd Sadre Alam ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL); 1473*7304d190SMd Sadre Alam if (!ecc) 1474*7304d190SMd Sadre Alam return -ENOMEM; 1475*7304d190SMd Sadre Alam 1476*7304d190SMd Sadre Alam qspi = devm_kzalloc(dev, sizeof(*qspi), GFP_KERNEL); 1477*7304d190SMd Sadre Alam if (!qspi) 1478*7304d190SMd Sadre Alam return -ENOMEM; 1479*7304d190SMd Sadre Alam 1480*7304d190SMd Sadre Alam ctlr = __devm_spi_alloc_controller(dev, sizeof(*snandc), false); 1481*7304d190SMd Sadre Alam if (!ctlr) 1482*7304d190SMd Sadre Alam return -ENOMEM; 1483*7304d190SMd Sadre Alam 1484*7304d190SMd Sadre Alam platform_set_drvdata(pdev, ctlr); 1485*7304d190SMd Sadre Alam 1486*7304d190SMd Sadre Alam snandc = spi_controller_get_devdata(ctlr); 1487*7304d190SMd Sadre Alam qspi->snandc = snandc; 1488*7304d190SMd Sadre Alam 1489*7304d190SMd Sadre Alam snandc->dev = dev; 1490*7304d190SMd Sadre Alam snandc->qspi = qspi; 1491*7304d190SMd Sadre Alam snandc->qspi->ctlr = ctlr; 1492*7304d190SMd Sadre Alam snandc->qspi->ecc = ecc; 1493*7304d190SMd Sadre Alam 1494*7304d190SMd Sadre Alam dev_data = of_device_get_match_data(dev); 1495*7304d190SMd Sadre Alam if (!dev_data) { 1496*7304d190SMd Sadre Alam dev_err(&pdev->dev, "failed to get device data\n"); 1497*7304d190SMd Sadre Alam return -ENODEV; 1498*7304d190SMd Sadre Alam } 1499*7304d190SMd Sadre Alam 1500*7304d190SMd Sadre Alam snandc->props = dev_data; 1501*7304d190SMd Sadre Alam snandc->dev = &pdev->dev; 1502*7304d190SMd Sadre Alam 1503*7304d190SMd Sadre Alam snandc->core_clk = devm_clk_get(dev, "core"); 1504*7304d190SMd Sadre Alam if (IS_ERR(snandc->core_clk)) 1505*7304d190SMd Sadre Alam return PTR_ERR(snandc->core_clk); 1506*7304d190SMd Sadre Alam 1507*7304d190SMd Sadre Alam snandc->aon_clk = devm_clk_get(dev, "aon"); 1508*7304d190SMd Sadre Alam if (IS_ERR(snandc->aon_clk)) 1509*7304d190SMd Sadre Alam return PTR_ERR(snandc->aon_clk); 1510*7304d190SMd Sadre Alam 1511*7304d190SMd Sadre Alam snandc->qspi->iomacro_clk = devm_clk_get(dev, "iom"); 1512*7304d190SMd Sadre Alam if (IS_ERR(snandc->qspi->iomacro_clk)) 1513*7304d190SMd Sadre Alam return PTR_ERR(snandc->qspi->iomacro_clk); 1514*7304d190SMd Sadre Alam 1515*7304d190SMd Sadre Alam snandc->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 1516*7304d190SMd Sadre Alam if (IS_ERR(snandc->base)) 1517*7304d190SMd Sadre Alam return PTR_ERR(snandc->base); 1518*7304d190SMd Sadre Alam 1519*7304d190SMd Sadre Alam snandc->base_phys = res->start; 1520*7304d190SMd Sadre Alam snandc->base_dma = dma_map_resource(dev, res->start, resource_size(res), 1521*7304d190SMd Sadre Alam DMA_BIDIRECTIONAL, 0); 1522*7304d190SMd Sadre Alam if (dma_mapping_error(dev, snandc->base_dma)) 1523*7304d190SMd Sadre Alam return -ENXIO; 1524*7304d190SMd Sadre Alam 1525*7304d190SMd Sadre Alam ret = clk_prepare_enable(snandc->core_clk); 1526*7304d190SMd Sadre Alam if (ret) 1527*7304d190SMd Sadre Alam goto err_dis_core_clk; 1528*7304d190SMd Sadre Alam 1529*7304d190SMd Sadre Alam ret = clk_prepare_enable(snandc->aon_clk); 1530*7304d190SMd Sadre Alam if (ret) 1531*7304d190SMd Sadre Alam goto err_dis_aon_clk; 1532*7304d190SMd Sadre Alam 1533*7304d190SMd Sadre Alam ret = clk_prepare_enable(snandc->qspi->iomacro_clk); 1534*7304d190SMd Sadre Alam if (ret) 1535*7304d190SMd Sadre Alam goto err_dis_iom_clk; 1536*7304d190SMd Sadre Alam 1537*7304d190SMd Sadre Alam ret = qcom_nandc_alloc(snandc); 1538*7304d190SMd Sadre Alam if (ret) 1539*7304d190SMd Sadre Alam goto err_snand_alloc; 1540*7304d190SMd Sadre Alam 1541*7304d190SMd Sadre Alam ret = qcom_spi_init(snandc); 1542*7304d190SMd Sadre Alam if (ret) 1543*7304d190SMd Sadre Alam goto err_spi_init; 1544*7304d190SMd Sadre Alam 1545*7304d190SMd Sadre Alam /* setup ECC engine */ 1546*7304d190SMd Sadre Alam snandc->qspi->ecc_eng.dev = &pdev->dev; 1547*7304d190SMd Sadre Alam snandc->qspi->ecc_eng.integration = NAND_ECC_ENGINE_INTEGRATION_PIPELINED; 1548*7304d190SMd Sadre Alam snandc->qspi->ecc_eng.ops = &qcom_spi_ecc_engine_ops_pipelined; 1549*7304d190SMd Sadre Alam snandc->qspi->ecc_eng.priv = snandc; 1550*7304d190SMd Sadre Alam 1551*7304d190SMd Sadre Alam ret = nand_ecc_register_on_host_hw_engine(&snandc->qspi->ecc_eng); 1552*7304d190SMd Sadre Alam if (ret) { 1553*7304d190SMd Sadre Alam dev_err(&pdev->dev, "failed to register ecc engine:%d\n", ret); 1554*7304d190SMd Sadre Alam goto err_spi_init; 1555*7304d190SMd Sadre Alam } 1556*7304d190SMd Sadre Alam 1557*7304d190SMd Sadre Alam ctlr->num_chipselect = QPIC_QSPI_NUM_CS; 1558*7304d190SMd Sadre Alam ctlr->mem_ops = &qcom_spi_mem_ops; 1559*7304d190SMd Sadre Alam ctlr->mem_caps = &qcom_spi_mem_caps; 1560*7304d190SMd Sadre Alam ctlr->dev.of_node = pdev->dev.of_node; 1561*7304d190SMd Sadre Alam ctlr->mode_bits = SPI_TX_DUAL | SPI_RX_DUAL | 1562*7304d190SMd Sadre Alam SPI_TX_QUAD | SPI_RX_QUAD; 1563*7304d190SMd Sadre Alam 1564*7304d190SMd Sadre Alam ret = spi_register_controller(ctlr); 1565*7304d190SMd Sadre Alam if (ret) { 1566*7304d190SMd Sadre Alam dev_err(&pdev->dev, "spi_register_controller failed.\n"); 1567*7304d190SMd Sadre Alam goto err_spi_init; 1568*7304d190SMd Sadre Alam } 1569*7304d190SMd Sadre Alam 1570*7304d190SMd Sadre Alam return 0; 1571*7304d190SMd Sadre Alam 1572*7304d190SMd Sadre Alam err_spi_init: 1573*7304d190SMd Sadre Alam qcom_nandc_unalloc(snandc); 1574*7304d190SMd Sadre Alam err_snand_alloc: 1575*7304d190SMd Sadre Alam clk_disable_unprepare(snandc->qspi->iomacro_clk); 1576*7304d190SMd Sadre Alam err_dis_iom_clk: 1577*7304d190SMd Sadre Alam clk_disable_unprepare(snandc->aon_clk); 1578*7304d190SMd Sadre Alam err_dis_aon_clk: 1579*7304d190SMd Sadre Alam clk_disable_unprepare(snandc->core_clk); 1580*7304d190SMd Sadre Alam err_dis_core_clk: 1581*7304d190SMd Sadre Alam dma_unmap_resource(dev, res->start, resource_size(res), 1582*7304d190SMd Sadre Alam DMA_BIDIRECTIONAL, 0); 1583*7304d190SMd Sadre Alam return ret; 1584*7304d190SMd Sadre Alam } 1585*7304d190SMd Sadre Alam 1586*7304d190SMd Sadre Alam static void qcom_spi_remove(struct platform_device *pdev) 1587*7304d190SMd Sadre Alam { 1588*7304d190SMd Sadre Alam struct spi_controller *ctlr = platform_get_drvdata(pdev); 1589*7304d190SMd Sadre Alam struct qcom_nand_controller *snandc = spi_controller_get_devdata(ctlr); 1590*7304d190SMd Sadre Alam struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1591*7304d190SMd Sadre Alam 1592*7304d190SMd Sadre Alam spi_unregister_controller(ctlr); 1593*7304d190SMd Sadre Alam 1594*7304d190SMd Sadre Alam qcom_nandc_unalloc(snandc); 1595*7304d190SMd Sadre Alam 1596*7304d190SMd Sadre Alam clk_disable_unprepare(snandc->aon_clk); 1597*7304d190SMd Sadre Alam clk_disable_unprepare(snandc->core_clk); 1598*7304d190SMd Sadre Alam clk_disable_unprepare(snandc->qspi->iomacro_clk); 1599*7304d190SMd Sadre Alam 1600*7304d190SMd Sadre Alam dma_unmap_resource(&pdev->dev, snandc->base_dma, resource_size(res), 1601*7304d190SMd Sadre Alam DMA_BIDIRECTIONAL, 0); 1602*7304d190SMd Sadre Alam } 1603*7304d190SMd Sadre Alam 1604*7304d190SMd Sadre Alam static const struct qcom_nandc_props ipq9574_snandc_props = { 1605*7304d190SMd Sadre Alam .dev_cmd_reg_start = 0x7000, 1606*7304d190SMd Sadre Alam .supports_bam = true, 1607*7304d190SMd Sadre Alam }; 1608*7304d190SMd Sadre Alam 1609*7304d190SMd Sadre Alam static const struct of_device_id qcom_snandc_of_match[] = { 1610*7304d190SMd Sadre Alam { 1611*7304d190SMd Sadre Alam .compatible = "qcom,ipq9574-snand", 1612*7304d190SMd Sadre Alam .data = &ipq9574_snandc_props, 1613*7304d190SMd Sadre Alam }, 1614*7304d190SMd Sadre Alam {} 1615*7304d190SMd Sadre Alam } 1616*7304d190SMd Sadre Alam MODULE_DEVICE_TABLE(of, qcom_snandc_of_match); 1617*7304d190SMd Sadre Alam 1618*7304d190SMd Sadre Alam static struct platform_driver qcom_spi_driver = { 1619*7304d190SMd Sadre Alam .driver = { 1620*7304d190SMd Sadre Alam .name = "qcom_snand", 1621*7304d190SMd Sadre Alam .of_match_table = qcom_snandc_of_match, 1622*7304d190SMd Sadre Alam }, 1623*7304d190SMd Sadre Alam .probe = qcom_spi_probe, 1624*7304d190SMd Sadre Alam .remove = qcom_spi_remove, 1625*7304d190SMd Sadre Alam }; 1626*7304d190SMd Sadre Alam module_platform_driver(qcom_spi_driver); 1627*7304d190SMd Sadre Alam 1628*7304d190SMd Sadre Alam MODULE_DESCRIPTION("SPI driver for QPIC QSPI cores"); 1629*7304d190SMd Sadre Alam MODULE_AUTHOR("Md Sadre Alam <quic_mdalam@quicinc.com>"); 1630*7304d190SMd Sadre Alam MODULE_LICENSE("GPL"); 1631*7304d190SMd Sadre Alam 1632