/*
* SPDX-License-Identifier: GPL-2.0
*
* Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
*
* Authors:
* Md Sadre Alam <quic_mdalam@quicinc.com>
* Sricharan R <quic_srichara@quicinc.com>
* Varadarajan Narayanan <quic_varada@quicinc.com>
*/
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dma/qcom_adm.h>
#include <linux/dma/qcom_bam_dma.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/mtd/nand-qpic-common.h>
#include <linux/mtd/spinand.h>
#include <linux/bitfield.h>
#define NAND_FLASH_SPI_CFG 0xc0
#define NAND_NUM_ADDR_CYCLES 0xc4
#define NAND_BUSY_CHECK_WAIT_CNT 0xc8
#define NAND_FLASH_FEATURES 0xf64
/* QSPI NAND config reg bits */
#define LOAD_CLK_CNTR_INIT_EN BIT(28)
#define CLK_CNTR_INIT_VAL_VEC 0x924
#define CLK_CNTR_INIT_VAL_VEC_MASK GENMASK(27, 16)
#define FEA_STATUS_DEV_ADDR 0xc0
#define FEA_STATUS_DEV_ADDR_MASK GENMASK(15, 8)
#define SPI_CFG BIT(0)
#define SPI_NUM_ADDR 0xDA4DB
#define SPI_WAIT_CNT 0x10
#define QPIC_QSPI_NUM_CS 1
#define SPI_TRANSFER_MODE_x1 BIT(29)
#define SPI_TRANSFER_MODE_x4 (3 << 29)
#define SPI_WP BIT(28)
#define SPI_HOLD BIT(27)
#define QPIC_SET_FEATURE BIT(31)
#define SPINAND_RESET 0xff
#define SPINAND_READID 0x9f
#define SPINAND_GET_FEATURE 0x0f
#define SPINAND_SET_FEATURE 0x1f
#define SPINAND_READ 0x13
#define SPINAND_ERASE 0xd8
#define SPINAND_WRITE_EN 0x06
#define SPINAND_PROGRAM_EXECUTE 0x10
#define SPINAND_PROGRAM_LOAD 0x84
#define ACC_FEATURE 0xe
#define BAD_BLOCK_MARKER_SIZE 0x2
#define OOB_BUF_SIZE 128
#define ecceng_to_qspi(eng) container_of(eng, struct qpic_spi_nand, ecc_eng)
struct snandc_read_status {
__le32 snandc_flash;
__le32 snandc_buffer;
__le32 snandc_erased_cw;
};
/*
* ECC state struct
* @corrected: ECC corrected
* @bitflips: Max bit flip
* @failed: ECC failed
*/
struct qcom_ecc_stats {
u32 corrected;
u32 bitflips;
u32 failed;
};
struct qpic_ecc {
int ecc_bytes_hw;
int spare_bytes;
int bbm_size;
int ecc_mode;
int bytes;
int steps;
int step_size;
int strength;
int cw_size;
int cw_data;
u32 cfg0;
u32 cfg1;
u32 cfg0_raw;
u32 cfg1_raw;
u32 ecc_buf_cfg;
u32 ecc_bch_cfg;
bool bch_enabled;
};
struct qpic_spi_nand {
struct qcom_nand_controller *snandc;
struct spi_controller *ctlr;
struct mtd_info *mtd;
struct clk *iomacro_clk;
struct qpic_ecc *ecc;
struct qcom_ecc_stats ecc_stats;
struct nand_ecc_engine ecc_eng;
u8 *data_buf;
u8 *oob_buf;
__le32 addr1;
__le32 addr2;
__le32 cmd;
u32 num_cw;
bool oob_rw;
bool page_rw;
bool raw_rw;
};
static void qcom_spi_set_read_loc_first(struct qcom_nand_controller *snandc,
int reg, int cw_offset, int read_size,
int is_last_read_loc)
{
__le32 locreg_val;
u32 val = FIELD_PREP(READ_LOCATION_OFFSET_MASK, cw_offset) |
FIELD_PREP(READ_LOCATION_SIZE_MASK, read_size) |
FIELD_PREP(READ_LOCATION_LAST_MASK, is_last_read_loc);
locreg_val = cpu_to_le32(val);
if (reg == NAND_READ_LOCATION_0)
snandc->regs->read_location0 = locreg_val;
else if (reg == NAND_READ_LOCATION_1)
snandc->regs->read_location1 = locreg_val;
else if (reg == NAND_READ_LOCATION_2)
snandc->regs->
|