Received: by 2002:a05:7412:1e0b:b0:fc:a2b0:25d7 with SMTP id kr11csp1180125rdb; Fri, 16 Feb 2024 07:37:32 -0800 (PST) X-Forwarded-Encrypted: i=3; AJvYcCWIpXffxWUyguA3/5jvx9bdxQeZi/cVs+sCNxySYMk9yVsqAb1EvrMfxJj9Os0FKSa2kiQUgFsZZL86L5KAv5PW6yECmGe5Cz4YMoLYYQ== X-Google-Smtp-Source: AGHT+IHtZBG3Z8ZC+yBFC1kDajX89K7Rz07LWz4tOII3U/x1lZhaMB9efKBDmkWTFSgOARY7GDx7 X-Received: by 2002:a17:906:cd10:b0:a3c:f575:a0f6 with SMTP id oz16-20020a170906cd1000b00a3cf575a0f6mr3365134ejb.72.1708097852072; Fri, 16 Feb 2024 07:37:32 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1708097852; cv=pass; d=google.com; s=arc-20160816; b=sva1jCdgiu7xbzDWuITEA5FvC8D4J7r+Rr8KLUvVrbpQhTGJV8N7rR5KKOAb+sAuEm LLBjdPbbP2oY02N5vs1o259S1zgZPglKi6Z4x2WCk9IsWPAvnQH3kSx6faojV1ZdjOz4 M6sxkwLO6zPwiLTbpO/p0VpJV5T25VlsRCKq0+t4aL27jCEVhj12nogE6JzlItHL6zHJ cjHf/5F7OL6Wn5UJ3ff5QAEFYh4j0qLKocwAuLo52Oy4VtQQgbOitgRhkczHhEwENjzC kQkYeYER8Luf5Kxr+U2aUE62owRmGcINKjFtkDQNJK5RHN2RHehccRfiJFiEmTMn3maN 0K3A== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:in-reply-to:from:references:cc:to :content-language:subject:user-agent:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:date:message-id:dkim-signature; bh=NalFMK/tYb1fN2SS3rQbBPeXk1PujgGgLKh7n9fd9Hs=; fh=M59L/y8kvMsAqFnZZcTelBjD04Xn4ekQSMHFcNB/Mpc=; b=IBqL01I2IW5Y63YsupcJxGlA+YEZslBaAiCRvkWra4xgeTZj56GwB7Hn8xEFel/fUl j9QR9rJzakcn4HqT3O7ul7Olvr/JraVQ6l7319fVC2Ob3TkfqfkIlyTW+smXC0PU1RJB BF4QLFcKdYSJG/+UEAsnn4OXhd66KUK1zI+dtqEkQlcXEF0OMF6vr35CAlYvZnYc7C/G Qgibzw8UV1D0iU+sEIkoq8086yKF1emlKXWPJM9sWBqmVprOonqvQSS+R7R27D2z431w maSe73BDGV1La02NgioqpsVwtW5JL0VHzWtx3ncLia/MIWIIPdqmoDkR4AURpKI8Wlwk 2t/g==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@quicinc.com header.s=qcppdkim1 header.b=TfWCPPkq; arc=pass (i=1 spf=pass spfdomain=quicinc.com dkim=pass dkdomain=quicinc.com dmarc=pass fromdomain=quicinc.com); spf=pass (google.com: domain of linux-kernel+bounces-68879-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) smtp.mailfrom="linux-kernel+bounces-68879-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=quicinc.com Return-Path: Received: from am.mirrors.kernel.org (am.mirrors.kernel.org. [147.75.80.249]) by mx.google.com with ESMTPS id s6-20020a170906168600b00a3d595713d7si32341ejd.628.2024.02.16.07.37.32 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Feb 2024 07:37:32 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-68879-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) client-ip=147.75.80.249; Authentication-Results: mx.google.com; dkim=pass header.i=@quicinc.com header.s=qcppdkim1 header.b=TfWCPPkq; arc=pass (i=1 spf=pass spfdomain=quicinc.com dkim=pass dkdomain=quicinc.com dmarc=pass fromdomain=quicinc.com); spf=pass (google.com: domain of linux-kernel+bounces-68879-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) smtp.mailfrom="linux-kernel+bounces-68879-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=quicinc.com Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by am.mirrors.kernel.org (Postfix) with ESMTPS id E875D1F216A3 for ; Fri, 16 Feb 2024 15:37:30 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 80C43146000; Fri, 16 Feb 2024 15:30:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="TfWCPPkq" Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 790F112FF8E; Fri, 16 Feb 2024 15:29:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708097400; cv=none; b=KldqAhy2paRMPGGTYqF8vm4LjDwkQ+5RlhynFR33VMRHJJFFALqYtOQaUCfJl25VdBrWm32hRtH+FQDZTj0/BteJfid64B0IQob0TwzMXzTg2NswA/QvvXDMdq1g2q2czDaVtUrDi3HytPnT90V+RzwpUEPzNGG1TQgCO7q+Opg= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1708097400; c=relaxed/simple; bh=UueqOHh7kA2DWoVtIO9NZSVVMqAQSTCI/0DMWgMfK2k=; h=Message-ID:Date:MIME-Version:Subject:To:CC:References:From: In-Reply-To:Content-Type; b=GhBXclKQH4244IMsmERAEFjIHW8RVBNdC9BFAaocVLBwoONid3/Gz3ftrhaAkercqSqj3JuVZTzJk33zoAV3Lx9YTY5weLDCTecYiPbcD9zUuYeGWCVsYvMFgVZfbFjXZRuq+R/cmahWiSf0ol60y3RszG2/J4h7M0NV2C2+ycA= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=TfWCPPkq; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Received: from pps.filterd (m0279871.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.17.1.24/8.17.1.24) with ESMTP id 41GFSXYO003477; Fri, 16 Feb 2024 15:29:21 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= message-id:date:mime-version:subject:to:cc:references:from :in-reply-to:content-type:content-transfer-encoding; s= qcppdkim1; bh=NalFMK/tYb1fN2SS3rQbBPeXk1PujgGgLKh7n9fd9Hs=; b=Tf WCPPkqnSuoNE+/NP0bBEwjMeLhUs+Ot2OIX4C5XFpjNdsTLdMwtO4vevFv/VA5eE qzzrFoydsdm8DkUYAY7ZuAgV/nFXqFFIjFWtTNHFCop3y+XGA8nvBLirEYzXO2X4 g2wBLL875EflFnTArR0jO3jYmN0Tzg4T4/LmbMAcF0mqfkgKv1frm9AVv353KtNt +d1gf2IxLJifc0rlqJEYD+mPBQVFdq0vh06Np2d+TUMa3u3OoB55rQbLVfBmn4Xz 43NGCfiSs9X9WAn5Tii5CbbqL3fkmsxmlmXG+PZoKchjPGbfRMpEoauK/bxiEDeI DB2LbD+NanSZCrlhRxVA== Received: from nalasppmta04.qualcomm.com (Global_NAT1.qualcomm.com [129.46.96.20]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 3w9xdx9bcc-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 16 Feb 2024 15:29:20 +0000 (GMT) Received: from nalasex01a.na.qualcomm.com (nalasex01a.na.qualcomm.com [10.47.209.196]) by NALASPPMTA04.qualcomm.com (8.17.1.5/8.17.1.5) with ESMTPS id 41GFTJpc026725 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 16 Feb 2024 15:29:19 GMT Received: from [10.216.32.60] (10.80.80.8) by nalasex01a.na.qualcomm.com (10.47.209.196) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.40; Fri, 16 Feb 2024 07:29:12 -0800 Message-ID: <16fbe862-a8aa-4017-b332-8d031d784d7f@quicinc.com> Date: Fri, 16 Feb 2024 20:59:08 +0530 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH 2/5] drivers: mtd: nand: Add qpic_common API file Content-Language: en-US To: Md Sadre Alam , , , , , , , , , , , , , , , CC: , References: <20240215134856.1313239-1-quic_mdalam@quicinc.com> <20240215134856.1313239-3-quic_mdalam@quicinc.com> From: Kathiravan Thirumoorthy In-Reply-To: <20240215134856.1313239-3-quic_mdalam@quicinc.com> Content-Type: text/plain; charset="UTF-8"; format=flowed Content-Transfer-Encoding: 7bit X-ClientProxiedBy: nasanex01b.na.qualcomm.com (10.46.141.250) To nalasex01a.na.qualcomm.com (10.47.209.196) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-ORIG-GUID: mbjw1jtL5BFToT7JixioSyRRxxgsd4eU X-Proofpoint-GUID: mbjw1jtL5BFToT7JixioSyRRxxgsd4eU X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.272,Aquarius:18.0.1011,Hydra:6.0.619,FMLib:17.11.176.26 definitions=2024-02-16_15,2024-02-16_01,2023-05-22_02 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 malwarescore=0 suspectscore=0 mlxlogscore=999 adultscore=0 phishscore=0 mlxscore=0 clxscore=1011 impostorscore=0 bulkscore=0 priorityscore=1501 lowpriorityscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2401310000 definitions=main-2402160124 On 2/15/2024 7:18 PM, Md Sadre Alam wrote: > Add qpic_common.c file which hold all the common > qpic APIs which will be used by both qpic raw nand > driver and qpic spi nand driver. > > Co-developed-by: Sricharan Ramabadhran > Signed-off-by: Sricharan Ramabadhran > Co-developed-by: Varadarajan Narayanan > Signed-off-by: Varadarajan Narayanan > Signed-off-by: Md Sadre Alam > --- > drivers/mtd/nand/Makefile | 1 + > drivers/mtd/nand/qpic_common.c | 786 +++++++++++++++++ > drivers/mtd/nand/raw/qcom_nandc.c | 1226 +------------------------- > include/linux/mtd/nand-qpic-common.h | 488 ++++++++++ > 4 files changed, 1291 insertions(+), 1210 deletions(-) > create mode 100644 drivers/mtd/nand/qpic_common.c > create mode 100644 include/linux/mtd/nand-qpic-common.h > > diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile > index 19e1291ac4d5..131707a41293 100644 > --- a/drivers/mtd/nand/Makefile > +++ b/drivers/mtd/nand/Makefile > @@ -12,3 +12,4 @@ nandcore-$(CONFIG_MTD_NAND_ECC) += ecc.o > nandcore-$(CONFIG_MTD_NAND_ECC_SW_HAMMING) += ecc-sw-hamming.o > nandcore-$(CONFIG_MTD_NAND_ECC_SW_BCH) += ecc-sw-bch.o > nandcore-$(CONFIG_MTD_NAND_ECC_MXIC) += ecc-mxic.o > +obj-y += qpic_common.o > diff --git a/drivers/mtd/nand/qpic_common.c b/drivers/mtd/nand/qpic_common.c > new file mode 100644 > index 000000000000..4d74ba888028 > --- /dev/null > +++ b/drivers/mtd/nand/qpic_common.c > @@ -0,0 +1,786 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * QPIC Controller common API file. > + * Copyright (C) 2023 Qualcomm Inc. Copyright should be repharsed? > + * Authors: Md sadre Alam > + * Sricharan R > + * Varadarajan Narayanan > + * > + */ > + > +#include > + > +struct qcom_nand_controller * > +get_qcom_nand_controller(struct nand_chip *chip) > +{ > + return container_of(chip->controller, struct qcom_nand_controller, > + controller); > +} > +EXPORT_SYMBOL(get_qcom_nand_controller); > + > +/* > + * Helper to prepare DMA descriptors for configuring registers > + * before reading a NAND page. > + */ > +void config_nand_page_read(struct nand_chip *chip) > +{ > + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); > + > + write_reg_dma(nandc, NAND_ADDR0, 2, 0); > + write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); > + if (!nandc->props->qpic_v2) > + write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0); > + write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0); > + write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, > + NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); > +} > +EXPORT_SYMBOL(config_nand_page_read); > + > +/* Frees the BAM transaction memory */ > +void free_bam_transaction(struct qcom_nand_controller *nandc) > +{ > + struct bam_transaction *bam_txn = nandc->bam_txn; > + > + devm_kfree(nandc->dev, bam_txn); > +} > +EXPORT_SYMBOL(free_bam_transaction); > + > +/* Callback for DMA descriptor completion */ > +void qpic_bam_dma_done(void *data) > +{ > + struct bam_transaction *bam_txn = data; > + > + /* > + * In case of data transfer with NAND, 2 callbacks will be generated. > + * One for command channel and another one for data channel. > + * If current transaction has data descriptors > + * (i.e. wait_second_completion is true), then set this to false > + * and wait for second DMA descriptor completion. > + */ > + if (bam_txn->wait_second_completion) > + bam_txn->wait_second_completion = false; > + else > + complete(&bam_txn->txn_done); > +} > +EXPORT_SYMBOL(qpic_bam_dma_done); > + > +void nandc_read_buffer_sync(struct qcom_nand_controller *nandc, > + bool is_cpu) > +{ > + if (!nandc->props->is_bam) > + return; > + > + if (is_cpu) > + dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma, > + MAX_REG_RD * > + sizeof(*nandc->reg_read_buf), > + DMA_FROM_DEVICE); > + else > + dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma, > + MAX_REG_RD * > + sizeof(*nandc->reg_read_buf), > + DMA_FROM_DEVICE); > +} > +EXPORT_SYMBOL(nandc_read_buffer_sync); > + > +__le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset) > +{ > + switch (offset) { > + case NAND_FLASH_CMD: > + return ®s->cmd; > + case NAND_ADDR0: > + return ®s->addr0; > + case NAND_ADDR1: > + return ®s->addr1; > + case NAND_FLASH_CHIP_SELECT: > + return ®s->chip_sel; > + case NAND_EXEC_CMD: > + return ®s->exec; > + case NAND_FLASH_STATUS: > + return ®s->clrflashstatus; > + case NAND_DEV0_CFG0: > + return ®s->cfg0; > + case NAND_DEV0_CFG1: > + return ®s->cfg1; > + case NAND_DEV0_ECC_CFG: > + return ®s->ecc_bch_cfg; > + case NAND_READ_STATUS: > + return ®s->clrreadstatus; > + case NAND_DEV_CMD1: > + return ®s->cmd1; > + case NAND_DEV_CMD1_RESTORE: > + return ®s->orig_cmd1; > + case NAND_DEV_CMD_VLD: > + return ®s->vld; > + case NAND_DEV_CMD_VLD_RESTORE: > + return ®s->orig_vld; > + case NAND_EBI2_ECC_BUF_CFG: > + return ®s->ecc_buf_cfg; > + case NAND_READ_LOCATION_0: > + return ®s->read_location0; > + case NAND_READ_LOCATION_1: > + return ®s->read_location1; > + case NAND_READ_LOCATION_2: > + return ®s->read_location2; > + case NAND_READ_LOCATION_3: > + return ®s->read_location3; > + case NAND_READ_LOCATION_LAST_CW_0: > + return ®s->read_location_last0; > + case NAND_READ_LOCATION_LAST_CW_1: > + return ®s->read_location_last1; > + case NAND_READ_LOCATION_LAST_CW_2: > + return ®s->read_location_last2; > + case NAND_READ_LOCATION_LAST_CW_3: > + return ®s->read_location_last3; > + default: > + return NULL; > + } > +} > +EXPORT_SYMBOL(offset_to_nandc_reg); > + > +/* reset the register read buffer for next NAND operation */ > +void clear_read_regs(struct qcom_nand_controller *nandc) > +{ > + nandc->reg_read_pos = 0; > + nandc_read_buffer_sync(nandc, false); > +} > +EXPORT_SYMBOL(clear_read_regs); > + > +int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, > + int reg_off, const void *vaddr, int size, > + bool flow_control) > +{ > + struct desc_info *desc; > + struct dma_async_tx_descriptor *dma_desc; > + struct scatterlist *sgl; > + struct dma_slave_config slave_conf; > + struct qcom_adm_peripheral_config periph_conf = {}; > + enum dma_transfer_direction dir_eng; > + int ret; > + > + desc = kzalloc(sizeof(*desc), GFP_KERNEL); > + if (!desc) > + return -ENOMEM; > + > + sgl = &desc->adm_sgl; > + > + sg_init_one(sgl, vaddr, size); > + > + if (read) { > + dir_eng = DMA_DEV_TO_MEM; > + desc->dir = DMA_FROM_DEVICE; > + } else { > + dir_eng = DMA_MEM_TO_DEV; > + desc->dir = DMA_TO_DEVICE; > + } > + > + ret = dma_map_sg(nandc->dev, sgl, 1, desc->dir); > + if (ret == 0) { > + ret = -ENOMEM; > + goto err; > + } > + > + memset(&slave_conf, 0x00, sizeof(slave_conf)); > + > + slave_conf.device_fc = flow_control; > + if (read) { > + slave_conf.src_maxburst = 16; > + slave_conf.src_addr = nandc->base_dma + reg_off; > + if (nandc->data_crci) { > + periph_conf.crci = nandc->data_crci; > + slave_conf.peripheral_config = &periph_conf; > + slave_conf.peripheral_size = sizeof(periph_conf); > + } > + } else { > + slave_conf.dst_maxburst = 16; > + slave_conf.dst_addr = nandc->base_dma + reg_off; > + if (nandc->cmd_crci) { > + periph_conf.crci = nandc->cmd_crci; > + slave_conf.peripheral_config = &periph_conf; > + slave_conf.peripheral_size = sizeof(periph_conf); > + } > + } > + > + ret = dmaengine_slave_config(nandc->chan, &slave_conf); > + if (ret) { > + dev_err(nandc->dev, "failed to configure dma channel\n"); > + goto err; > + } > + > + dma_desc = dmaengine_prep_slave_sg(nandc->chan, sgl, 1, dir_eng, 0); > + if (!dma_desc) { > + dev_err(nandc->dev, "failed to prepare desc\n"); > + ret = -EINVAL; > + goto err; > + } > + > + desc->dma_desc = dma_desc; > + > + list_add_tail(&desc->node, &nandc->desc_list); > + > + return 0; > +err: > + kfree(desc); > + > + return ret; > +} > +EXPORT_SYMBOL(prep_adm_dma_desc); > + > +/* helpers to submit/free our list of dma descriptors */ > +int submit_descs(struct qcom_nand_controller *nandc) > +{ > + struct desc_info *desc, *n; > + dma_cookie_t cookie = 0; > + struct bam_transaction *bam_txn = nandc->bam_txn; > + int ret = 0; > + > + if (nandc->props->is_bam) { > + if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { > + ret = prepare_bam_async_desc(nandc, nandc->rx_chan, 0); > + if (ret) > + goto err_unmap_free_desc; > + } > + > + if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) { > + ret = prepare_bam_async_desc(nandc, nandc->tx_chan, > + DMA_PREP_INTERRUPT); > + if (ret) > + goto err_unmap_free_desc; > + } > + > + if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) { > + ret = prepare_bam_async_desc(nandc, nandc->cmd_chan, > + DMA_PREP_CMD); > + if (ret) > + goto err_unmap_free_desc; > + } > + } > + > + list_for_each_entry(desc, &nandc->desc_list, node) > + cookie = dmaengine_submit(desc->dma_desc); > + > + if (nandc->props->is_bam) { > + bam_txn->last_cmd_desc->callback = qpic_bam_dma_done; > + bam_txn->last_cmd_desc->callback_param = bam_txn; > + if (bam_txn->last_data_desc) { > + bam_txn->last_data_desc->callback = qpic_bam_dma_done; > + bam_txn->last_data_desc->callback_param = bam_txn; > + bam_txn->wait_second_completion = true; > + } > + > + dma_async_issue_pending(nandc->tx_chan); > + dma_async_issue_pending(nandc->rx_chan); > + dma_async_issue_pending(nandc->cmd_chan); > + > + if (!wait_for_completion_timeout(&bam_txn->txn_done, > + QPIC_NAND_COMPLETION_TIMEOUT)) > + ret = -ETIMEDOUT; > + } else { > + if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) > + ret = -ETIMEDOUT; > + } > + > +err_unmap_free_desc: > + /* > + * Unmap the dma sg_list and free the desc allocated by both > + * prepare_bam_async_desc() and prep_adm_dma_desc() functions. > + */ > + list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { > + list_del(&desc->node); > + > + if (nandc->props->is_bam) > + dma_unmap_sg(nandc->dev, desc->bam_sgl, > + desc->sgl_cnt, desc->dir); > + else > + dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1, > + desc->dir); > + > + kfree(desc); > + } > + > + return ret; > +} > +EXPORT_SYMBOL(submit_descs); > + > +/* > + * Maps the scatter gather list for DMA transfer and forms the DMA descriptor > + * for BAM. This descriptor will be added in the NAND DMA descriptor queue > + * which will be submitted to DMA engine. > + */ > +int prepare_bam_async_desc(struct qcom_nand_controller *nandc, > + struct dma_chan *chan, > + unsigned long flags) > +{ > + struct desc_info *desc; > + struct scatterlist *sgl; > + unsigned int sgl_cnt; > + int ret; > + struct bam_transaction *bam_txn = nandc->bam_txn; > + enum dma_transfer_direction dir_eng; > + struct dma_async_tx_descriptor *dma_desc; > + > + desc = kzalloc(sizeof(*desc), GFP_KERNEL); > + if (!desc) > + return -ENOMEM; > + > + if (chan == nandc->cmd_chan) { > + sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start]; > + sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start; > + bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos; > + dir_eng = DMA_MEM_TO_DEV; > + desc->dir = DMA_TO_DEVICE; > + } else if (chan == nandc->tx_chan) { > + sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start]; > + sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start; > + bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos; > + dir_eng = DMA_MEM_TO_DEV; > + desc->dir = DMA_TO_DEVICE; > + } else { > + sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start]; > + sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start; > + bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos; > + dir_eng = DMA_DEV_TO_MEM; > + desc->dir = DMA_FROM_DEVICE; > + } > + > + sg_mark_end(sgl + sgl_cnt - 1); > + ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir); > + if (ret == 0) { > + dev_err(nandc->dev, "failure in mapping desc\n"); > + kfree(desc); > + return -ENOMEM; > + } > + > + desc->sgl_cnt = sgl_cnt; > + desc->bam_sgl = sgl; > + > + dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng, > + flags); > + > + if (!dma_desc) { > + dev_err(nandc->dev, "failure in prep desc\n"); > + dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir); > + kfree(desc); > + return -EINVAL; > + } > + > + desc->dma_desc = dma_desc; > + > + /* update last data/command descriptor */ > + if (chan == nandc->cmd_chan) > + bam_txn->last_cmd_desc = dma_desc; > + else > + bam_txn->last_data_desc = dma_desc; > + > + list_add_tail(&desc->node, &nandc->desc_list); > + > + return 0; > +} > +EXPORT_SYMBOL(prepare_bam_async_desc); > + > +/* > + * Prepares the command descriptor for BAM DMA which will be used for NAND > + * register reads and writes. The command descriptor requires the command > + * to be formed in command element type so this function uses the command > + * element from bam transaction ce array and fills the same with required > + * data. A single SGL can contain multiple command elements so > + * NAND_BAM_NEXT_SGL will be used for starting the separate SGL > + * after the current command element. > + */ > +int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, > + int reg_off, const void *vaddr, > + int size, unsigned int flags) > +{ > + int bam_ce_size; > + int i, ret; > + struct bam_cmd_element *bam_ce_buffer; > + struct bam_transaction *bam_txn = nandc->bam_txn; > + > + bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos]; > + > + /* fill the command desc */ > + for (i = 0; i < size; i++) { > + if (read) > + bam_prep_ce(&bam_ce_buffer[i], > + nandc_reg_phys(nandc, reg_off + 4 * i), > + BAM_READ_COMMAND, > + reg_buf_dma_addr(nandc, > + (__le32 *)vaddr + i)); > + else > + bam_prep_ce_le32(&bam_ce_buffer[i], > + nandc_reg_phys(nandc, reg_off + 4 * i), > + BAM_WRITE_COMMAND, > + *((__le32 *)vaddr + i)); > + } > + > + bam_txn->bam_ce_pos += size; > + > + /* use the separate sgl after this command */ > + if (flags & NAND_BAM_NEXT_SGL) { > + bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start]; > + bam_ce_size = (bam_txn->bam_ce_pos - > + bam_txn->bam_ce_start) * > + sizeof(struct bam_cmd_element); > + sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos], > + bam_ce_buffer, bam_ce_size); > + bam_txn->cmd_sgl_pos++; > + bam_txn->bam_ce_start = bam_txn->bam_ce_pos; > + > + if (flags & NAND_BAM_NWD) { > + ret = prepare_bam_async_desc(nandc, nandc->cmd_chan, > + DMA_PREP_FENCE | > + DMA_PREP_CMD); > + if (ret) > + return ret; > + } > + } > + > + return 0; > +} > +EXPORT_SYMBOL(prep_bam_dma_desc_cmd); > + > +/* > + * Prepares the data descriptor for BAM DMA which will be used for NAND > + * data reads and writes. > + */ > +int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, > + const void *vaddr, > + int size, unsigned int flags) > +{ > + int ret; > + struct bam_transaction *bam_txn = nandc->bam_txn; > + > + if (read) { > + sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos], > + vaddr, size); > + bam_txn->rx_sgl_pos++; > + } else { > + sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos], > + vaddr, size); > + bam_txn->tx_sgl_pos++; > + > + /* > + * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag > + * is not set, form the DMA descriptor > + */ > + if (!(flags & NAND_BAM_NO_EOT)) { > + ret = prepare_bam_async_desc(nandc, nandc->tx_chan, > + DMA_PREP_INTERRUPT); > + if (ret) > + return ret; > + } > + } > + > + return 0; > +} > +EXPORT_SYMBOL(prep_bam_dma_desc_data); > + > +/* > + * read_reg_dma: prepares a descriptor to read a given number of > + * contiguous registers to the reg_read_buf pointer > + * > + * @first: offset of the first register in the contiguous block > + * @num_regs: number of registers to read > + * @flags: flags to control DMA descriptor preparation > + */ > +int read_reg_dma(struct qcom_nand_controller *nandc, int first, > + int num_regs, unsigned int flags) > +{ > + bool flow_control = false; > + void *vaddr; > + > + vaddr = nandc->reg_read_buf + nandc->reg_read_pos; > + nandc->reg_read_pos += num_regs; > + > + if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1) > + first = dev_cmd_reg_addr(nandc, first); > + > + if (nandc->props->is_bam) > + return prep_bam_dma_desc_cmd(nandc, true, first, vaddr, > + num_regs, flags); > + > + if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) > + flow_control = true; > + > + return prep_adm_dma_desc(nandc, true, first, vaddr, > + num_regs * sizeof(u32), flow_control); > +} > +EXPORT_SYMBOL(read_reg_dma); > + > +/* > + * write_reg_dma: prepares a descriptor to write a given number of > + * contiguous registers > + * > + * @first: offset of the first register in the contiguous block > + * @num_regs: number of registers to write > + * @flags: flags to control DMA descriptor preparation > + */ > +int write_reg_dma(struct qcom_nand_controller *nandc, int first, > + int num_regs, unsigned int flags) > +{ > + bool flow_control = false; > + struct nandc_regs *regs = nandc->regs; > + void *vaddr; > + > + vaddr = offset_to_nandc_reg(regs, first); > + > + if (first == NAND_ERASED_CW_DETECT_CFG) { > + if (flags & NAND_ERASED_CW_SET) > + vaddr = ®s->erased_cw_detect_cfg_set; > + else > + vaddr = ®s->erased_cw_detect_cfg_clr; > + } > + > + if (first == NAND_EXEC_CMD) > + flags |= NAND_BAM_NWD; > + > + if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1) > + first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1); > + > + if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD) > + first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); > + > + if (nandc->props->is_bam) > + return prep_bam_dma_desc_cmd(nandc, false, first, vaddr, > + num_regs, flags); > + > + if (first == NAND_FLASH_CMD) > + flow_control = true; > + > + return prep_adm_dma_desc(nandc, false, first, vaddr, > + num_regs * sizeof(u32), flow_control); > +} > +EXPORT_SYMBOL(write_reg_dma); > + > +/* > + * read_data_dma: prepares a DMA descriptor to transfer data from the > + * controller's internal buffer to the buffer 'vaddr' > + * > + * @reg_off: offset within the controller's data buffer > + * @vaddr: virtual address of the buffer we want to write to > + * @size: DMA transaction size in bytes > + * @flags: flags to control DMA descriptor preparation > + */ > +int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, > + const u8 *vaddr, int size, unsigned int flags) > +{ > + if (nandc->props->is_bam) > + return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); > + > + return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); > +} > +EXPORT_SYMBOL(read_data_dma); > + > +/* > + * write_data_dma: prepares a DMA descriptor to transfer data from > + * 'vaddr' to the controller's internal buffer > + * > + * @reg_off: offset within the controller's data buffer > + * @vaddr: virtual address of the buffer we want to read from > + * @size: DMA transaction size in bytes > + * @flags: flags to control DMA descriptor preparation > + */ > +int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, > + const u8 *vaddr, int size, unsigned int flags) > +{ > + if (nandc->props->is_bam) > + return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); > + > + return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); > +} > +EXPORT_SYMBOL(write_data_dma); > + > +/* Allocates and Initializes the BAM transaction */ > +struct bam_transaction * > +alloc_bam_transaction(struct qcom_nand_controller *nandc) > +{ > + struct bam_transaction *bam_txn; > + size_t bam_txn_size; > + unsigned int num_cw = nandc->max_cwperpage; > + void *bam_txn_buf; > + > + bam_txn_size = > + sizeof(*bam_txn) + num_cw * > + ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) + > + (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) + > + (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL)); > + > + bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL); > + if (!bam_txn_buf) > + return NULL; > + > + bam_txn = bam_txn_buf; > + bam_txn_buf += sizeof(*bam_txn); > + > + bam_txn->bam_ce = bam_txn_buf; > + bam_txn_buf += > + sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw; > + > + bam_txn->cmd_sgl = bam_txn_buf; > + bam_txn_buf += > + sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw; > + > + bam_txn->data_sgl = bam_txn_buf; > + > + init_completion(&bam_txn->txn_done); > + > + return bam_txn; > +} > +EXPORT_SYMBOL(alloc_bam_transaction); > + > +/* Clears the BAM transaction indexes */ > +void clear_bam_transaction(struct qcom_nand_controller *nandc) > +{ > + struct bam_transaction *bam_txn = nandc->bam_txn; > + > + if (!nandc->props->is_bam) > + return; > + > + bam_txn->bam_ce_pos = 0; > + bam_txn->bam_ce_start = 0; > + bam_txn->cmd_sgl_pos = 0; > + bam_txn->cmd_sgl_start = 0; > + bam_txn->tx_sgl_pos = 0; > + bam_txn->tx_sgl_start = 0; > + bam_txn->rx_sgl_pos = 0; > + bam_txn->rx_sgl_start = 0; > + bam_txn->last_data_desc = NULL; > + bam_txn->wait_second_completion = false; > + > + sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * > + QPIC_PER_CW_CMD_SGL); > + sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage * > + QPIC_PER_CW_DATA_SGL); > + > + reinit_completion(&bam_txn->txn_done); > +} > +EXPORT_SYMBOL(clear_bam_transaction); > + > +void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) > +{ > + if (nandc->props->is_bam) { > + if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) > + dma_unmap_single(nandc->dev, nandc->reg_read_dma, > + MAX_REG_RD * > + sizeof(*nandc->reg_read_buf), > + DMA_FROM_DEVICE); > + > + if (nandc->tx_chan) > + dma_release_channel(nandc->tx_chan); > + > + if (nandc->rx_chan) > + dma_release_channel(nandc->rx_chan); > + > + if (nandc->cmd_chan) > + dma_release_channel(nandc->cmd_chan); > + } else { > + if (nandc->chan) > + dma_release_channel(nandc->chan); > + } > +} > +EXPORT_SYMBOL(qcom_nandc_unalloc); > + > +int qcom_nandc_alloc(struct qcom_nand_controller *nandc) > +{ > + int ret; > + > + ret = dma_set_coherent_mask(nandc->dev, DMA_BIT_MASK(32)); > + if (ret) { > + dev_err(nandc->dev, "failed to set DMA mask\n"); > + return ret; > + } > + > + /* > + * we use the internal buffer for reading ONFI params, reading small > + * data like ID and status, and preforming read-copy-write operations > + * when writing to a codeword partially. 532 is the maximum possible > + * size of a codeword for our nand controller > + */ > + nandc->buf_size = 532; > + > + nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size, GFP_KERNEL); > + if (!nandc->data_buffer) > + return -ENOMEM; > + > + nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs), GFP_KERNEL); > + if (!nandc->regs) > + return -ENOMEM; > + > + nandc->reg_read_buf = devm_kcalloc(nandc->dev, MAX_REG_RD, > + sizeof(*nandc->reg_read_buf), > + GFP_KERNEL); > + if (!nandc->reg_read_buf) > + return -ENOMEM; > + > + if (nandc->props->is_bam) { > + nandc->reg_read_dma = > + dma_map_single(nandc->dev, nandc->reg_read_buf, > + MAX_REG_RD * > + sizeof(*nandc->reg_read_buf), > + DMA_FROM_DEVICE); > + if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) { > + dev_err(nandc->dev, "failed to DMA MAP reg buffer\n"); > + return -EIO; > + } > + > + nandc->tx_chan = dma_request_chan(nandc->dev, "tx"); > + if (IS_ERR(nandc->tx_chan)) { > + ret = PTR_ERR(nandc->tx_chan); > + nandc->tx_chan = NULL; > + dev_err_probe(nandc->dev, ret, > + "tx DMA channel request failed\n"); > + goto unalloc; > + } > + > + nandc->rx_chan = dma_request_chan(nandc->dev, "rx"); > + if (IS_ERR(nandc->rx_chan)) { > + ret = PTR_ERR(nandc->rx_chan); > + nandc->rx_chan = NULL; > + dev_err_probe(nandc->dev, ret, > + "rx DMA channel request failed\n"); > + goto unalloc; > + } > + > + nandc->cmd_chan = dma_request_chan(nandc->dev, "cmd"); > + if (IS_ERR(nandc->cmd_chan)) { > + ret = PTR_ERR(nandc->cmd_chan); > + nandc->cmd_chan = NULL; > + dev_err_probe(nandc->dev, ret, > + "cmd DMA channel request failed\n"); > + goto unalloc; > + } > + > + /* > + * Initially allocate BAM transaction to read ONFI param page. > + * After detecting all the devices, this BAM transaction will > + * be freed and the next BAM transaction will be allocated with > + * maximum codeword size > + */ > + nandc->max_cwperpage = 1; > + nandc->bam_txn = alloc_bam_transaction(nandc); > + if (!nandc->bam_txn) { > + dev_err(nandc->dev, > + "failed to allocate bam transaction\n"); > + ret = -ENOMEM; > + goto unalloc; > + } > + } else { > + nandc->chan = dma_request_chan(nandc->dev, "rxtx"); > + if (IS_ERR(nandc->chan)) { > + ret = PTR_ERR(nandc->chan); > + nandc->chan = NULL; > + dev_err_probe(nandc->dev, ret, > + "rxtx DMA channel request failed\n"); > + return ret; > + } > + } > + > + INIT_LIST_HEAD(&nandc->desc_list); > + INIT_LIST_HEAD(&nandc->host_list); > + > + return 0; > +unalloc: > + qcom_nandc_unalloc(nandc); > + return ret; > +} > +EXPORT_SYMBOL(qcom_nandc_alloc); > diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c > index b079605c84d3..75c6ca698c85 100644 > --- a/drivers/mtd/nand/raw/qcom_nandc.c > +++ b/drivers/mtd/nand/raw/qcom_nandc.c > @@ -2,186 +2,7 @@ > /* > * Copyright (c) 2016, The Linux Foundation. All rights reserved. > */ > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > - > -/* NANDc reg offsets */ > -#define NAND_FLASH_CMD 0x00 > -#define NAND_ADDR0 0x04 > -#define NAND_ADDR1 0x08 > -#define NAND_FLASH_CHIP_SELECT 0x0c > -#define NAND_EXEC_CMD 0x10 > -#define NAND_FLASH_STATUS 0x14 > -#define NAND_BUFFER_STATUS 0x18 > -#define NAND_DEV0_CFG0 0x20 > -#define NAND_DEV0_CFG1 0x24 > -#define NAND_DEV0_ECC_CFG 0x28 > -#define NAND_AUTO_STATUS_EN 0x2c > -#define NAND_DEV1_CFG0 0x30 > -#define NAND_DEV1_CFG1 0x34 > -#define NAND_READ_ID 0x40 > -#define NAND_READ_STATUS 0x44 > -#define NAND_DEV_CMD0 0xa0 > -#define NAND_DEV_CMD1 0xa4 > -#define NAND_DEV_CMD2 0xa8 > -#define NAND_DEV_CMD_VLD 0xac > -#define SFLASHC_BURST_CFG 0xe0 > -#define NAND_ERASED_CW_DETECT_CFG 0xe8 > -#define NAND_ERASED_CW_DETECT_STATUS 0xec > -#define NAND_EBI2_ECC_BUF_CFG 0xf0 > -#define FLASH_BUF_ACC 0x100 > - > -#define NAND_CTRL 0xf00 > -#define NAND_VERSION 0xf08 > -#define NAND_READ_LOCATION_0 0xf20 > -#define NAND_READ_LOCATION_1 0xf24 > -#define NAND_READ_LOCATION_2 0xf28 > -#define NAND_READ_LOCATION_3 0xf2c > -#define NAND_READ_LOCATION_LAST_CW_0 0xf40 > -#define NAND_READ_LOCATION_LAST_CW_1 0xf44 > -#define NAND_READ_LOCATION_LAST_CW_2 0xf48 > -#define NAND_READ_LOCATION_LAST_CW_3 0xf4c > - > -/* dummy register offsets, used by write_reg_dma */ > -#define NAND_DEV_CMD1_RESTORE 0xdead > -#define NAND_DEV_CMD_VLD_RESTORE 0xbeef > - > -/* NAND_FLASH_CMD bits */ > -#define PAGE_ACC BIT(4) > -#define LAST_PAGE BIT(5) > - > -/* NAND_FLASH_CHIP_SELECT bits */ > -#define NAND_DEV_SEL 0 > -#define DM_EN BIT(2) > - > -/* NAND_FLASH_STATUS bits */ > -#define FS_OP_ERR BIT(4) > -#define FS_READY_BSY_N BIT(5) > -#define FS_MPU_ERR BIT(8) > -#define FS_DEVICE_STS_ERR BIT(16) > -#define FS_DEVICE_WP BIT(23) > - > -/* NAND_BUFFER_STATUS bits */ > -#define BS_UNCORRECTABLE_BIT BIT(8) > -#define BS_CORRECTABLE_ERR_MSK 0x1f > - > -/* NAND_DEVn_CFG0 bits */ > -#define DISABLE_STATUS_AFTER_WRITE 4 > -#define CW_PER_PAGE 6 > -#define UD_SIZE_BYTES 9 > -#define UD_SIZE_BYTES_MASK GENMASK(18, 9) > -#define ECC_PARITY_SIZE_BYTES_RS 19 > -#define SPARE_SIZE_BYTES 23 > -#define SPARE_SIZE_BYTES_MASK GENMASK(26, 23) > -#define NUM_ADDR_CYCLES 27 > -#define STATUS_BFR_READ 30 > -#define SET_RD_MODE_AFTER_STATUS 31 > - > -/* NAND_DEVn_CFG0 bits */ > -#define DEV0_CFG1_ECC_DISABLE 0 > -#define WIDE_FLASH 1 > -#define NAND_RECOVERY_CYCLES 2 > -#define CS_ACTIVE_BSY 5 > -#define BAD_BLOCK_BYTE_NUM 6 > -#define BAD_BLOCK_IN_SPARE_AREA 16 > -#define WR_RD_BSY_GAP 17 > -#define ENABLE_BCH_ECC 27 > - > -/* NAND_DEV0_ECC_CFG bits */ > -#define ECC_CFG_ECC_DISABLE 0 > -#define ECC_SW_RESET 1 > -#define ECC_MODE 4 > -#define ECC_PARITY_SIZE_BYTES_BCH 8 > -#define ECC_NUM_DATA_BYTES 16 > -#define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16) > -#define ECC_FORCE_CLK_OPEN 30 > - > -/* NAND_DEV_CMD1 bits */ > -#define READ_ADDR 0 > - > -/* NAND_DEV_CMD_VLD bits */ > -#define READ_START_VLD BIT(0) > -#define READ_STOP_VLD BIT(1) > -#define WRITE_START_VLD BIT(2) > -#define ERASE_START_VLD BIT(3) > -#define SEQ_READ_START_VLD BIT(4) > - > -/* NAND_EBI2_ECC_BUF_CFG bits */ > -#define NUM_STEPS 0 > - > -/* NAND_ERASED_CW_DETECT_CFG bits */ > -#define ERASED_CW_ECC_MASK 1 > -#define AUTO_DETECT_RES 0 > -#define MASK_ECC BIT(ERASED_CW_ECC_MASK) > -#define RESET_ERASED_DET BIT(AUTO_DETECT_RES) > -#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) > -#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) > -#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) > - > -/* NAND_ERASED_CW_DETECT_STATUS bits */ > -#define PAGE_ALL_ERASED BIT(7) > -#define CODEWORD_ALL_ERASED BIT(6) > -#define PAGE_ERASED BIT(5) > -#define CODEWORD_ERASED BIT(4) > -#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) > -#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) > - > -/* NAND_READ_LOCATION_n bits */ > -#define READ_LOCATION_OFFSET 0 > -#define READ_LOCATION_SIZE 16 > -#define READ_LOCATION_LAST 31 > - > -/* Version Mask */ > -#define NAND_VERSION_MAJOR_MASK 0xf0000000 > -#define NAND_VERSION_MAJOR_SHIFT 28 > -#define NAND_VERSION_MINOR_MASK 0x0fff0000 > -#define NAND_VERSION_MINOR_SHIFT 16 > - > -/* NAND OP_CMDs */ > -#define OP_PAGE_READ 0x2 > -#define OP_PAGE_READ_WITH_ECC 0x3 > -#define OP_PAGE_READ_WITH_ECC_SPARE 0x4 > -#define OP_PAGE_READ_ONFI_READ 0x5 > -#define OP_PROGRAM_PAGE 0x6 > -#define OP_PAGE_PROGRAM_WITH_ECC 0x7 > -#define OP_PROGRAM_PAGE_SPARE 0x9 > -#define OP_BLOCK_ERASE 0xa > -#define OP_CHECK_STATUS 0xc > -#define OP_FETCH_ID 0xb > -#define OP_RESET_DEVICE 0xd > - > -/* Default Value for NAND_DEV_CMD_VLD */ > -#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \ > - ERASE_START_VLD | SEQ_READ_START_VLD) > - > -/* NAND_CTRL bits */ > -#define BAM_MODE_EN BIT(0) > - > -/* > - * the NAND controller performs reads/writes with ECC in 516 byte chunks. > - * the driver calls the chunks 'step' or 'codeword' interchangeably > - */ > -#define NANDC_STEP_SIZE 512 > - > -/* > - * the largest page size we support is 8K, this will have 16 steps/codewords > - * of 512 bytes each > - */ > -#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE) > - > -/* we read at most 3 registers per codeword scan */ > -#define MAX_REG_RD (3 * MAX_NUM_STEPS) > +#include > > /* ECC modes supported by the controller */ > #define ECC_NONE BIT(0) > @@ -200,247 +21,6 @@ nandc_set_reg(chip, reg, \ > ((cw_offset) << READ_LOCATION_OFFSET) | \ > ((read_size) << READ_LOCATION_SIZE) | \ > ((is_last_read_loc) << READ_LOCATION_LAST)) > -/* > - * Returns the actual register address for all NAND_DEV_ registers > - * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD) > - */ > -#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg)) > - > -/* Returns the NAND register physical address */ > -#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset)) > - > -/* Returns the dma address for reg read buffer */ > -#define reg_buf_dma_addr(chip, vaddr) \ > - ((chip)->reg_read_dma + \ > - ((u8 *)(vaddr) - (u8 *)(chip)->reg_read_buf)) > - > -#define QPIC_PER_CW_CMD_ELEMENTS 32 > -#define QPIC_PER_CW_CMD_SGL 32 > -#define QPIC_PER_CW_DATA_SGL 8 > - > -#define QPIC_NAND_COMPLETION_TIMEOUT msecs_to_jiffies(2000) > - > -/* > - * Flags used in DMA descriptor preparation helper functions > - * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma) > - */ > -/* Don't set the EOT in current tx BAM sgl */ > -#define NAND_BAM_NO_EOT BIT(0) > -/* Set the NWD flag in current BAM sgl */ > -#define NAND_BAM_NWD BIT(1) > -/* Finish writing in the current BAM sgl and start writing in another BAM sgl */ > -#define NAND_BAM_NEXT_SGL BIT(2) > -/* > - * Erased codeword status is being used two times in single transfer so this > - * flag will determine the current value of erased codeword status register > - */ > -#define NAND_ERASED_CW_SET BIT(4) > - > -#define MAX_ADDRESS_CYCLE 5 > - > -/* > - * This data type corresponds to the BAM transaction which will be used for all > - * NAND transfers. > - * @bam_ce - the array of BAM command elements > - * @cmd_sgl - sgl for NAND BAM command pipe > - * @data_sgl - sgl for NAND BAM consumer/producer pipe > - * @last_data_desc - last DMA desc in data channel (tx/rx). > - * @last_cmd_desc - last DMA desc in command channel. > - * @txn_done - completion for NAND transfer. > - * @bam_ce_pos - the index in bam_ce which is available for next sgl > - * @bam_ce_start - the index in bam_ce which marks the start position ce > - * for current sgl. It will be used for size calculation > - * for current sgl > - * @cmd_sgl_pos - current index in command sgl. > - * @cmd_sgl_start - start index in command sgl. > - * @tx_sgl_pos - current index in data sgl for tx. > - * @tx_sgl_start - start index in data sgl for tx. > - * @rx_sgl_pos - current index in data sgl for rx. > - * @rx_sgl_start - start index in data sgl for rx. > - * @wait_second_completion - wait for second DMA desc completion before making > - * the NAND transfer completion. > - */ > -struct bam_transaction { > - struct bam_cmd_element *bam_ce; > - struct scatterlist *cmd_sgl; > - struct scatterlist *data_sgl; > - struct dma_async_tx_descriptor *last_data_desc; > - struct dma_async_tx_descriptor *last_cmd_desc; > - struct completion txn_done; > - u32 bam_ce_pos; > - u32 bam_ce_start; > - u32 cmd_sgl_pos; > - u32 cmd_sgl_start; > - u32 tx_sgl_pos; > - u32 tx_sgl_start; > - u32 rx_sgl_pos; > - u32 rx_sgl_start; > - bool wait_second_completion; > -}; > - > -/* > - * This data type corresponds to the nand dma descriptor > - * @dma_desc - low level DMA engine descriptor > - * @list - list for desc_info > - * > - * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by > - * ADM > - * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM > - * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM > - * @dir - DMA transfer direction > - */ > -struct desc_info { > - struct dma_async_tx_descriptor *dma_desc; > - struct list_head node; > - > - union { > - struct scatterlist adm_sgl; > - struct { > - struct scatterlist *bam_sgl; > - int sgl_cnt; > - }; > - }; > - enum dma_data_direction dir; > -}; > - > -/* > - * holds the current register values that we want to write. acts as a contiguous > - * chunk of memory which we use to write the controller registers through DMA. > - */ > -struct nandc_regs { > - __le32 cmd; > - __le32 addr0; > - __le32 addr1; > - __le32 chip_sel; > - __le32 exec; > - > - __le32 cfg0; > - __le32 cfg1; > - __le32 ecc_bch_cfg; > - > - __le32 clrflashstatus; > - __le32 clrreadstatus; > - > - __le32 cmd1; > - __le32 vld; > - > - __le32 orig_cmd1; > - __le32 orig_vld; > - > - __le32 ecc_buf_cfg; > - __le32 read_location0; > - __le32 read_location1; > - __le32 read_location2; > - __le32 read_location3; > - __le32 read_location_last0; > - __le32 read_location_last1; > - __le32 read_location_last2; > - __le32 read_location_last3; > - > - __le32 erased_cw_detect_cfg_clr; > - __le32 erased_cw_detect_cfg_set; > -}; > - > -/* > - * NAND controller data struct > - * > - * @dev: parent device > - * > - * @base: MMIO base > - * > - * @core_clk: controller clock > - * @aon_clk: another controller clock > - * > - * @regs: a contiguous chunk of memory for DMA register > - * writes. contains the register values to be > - * written to controller > - * > - * @props: properties of current NAND controller, > - * initialized via DT match data > - * > - * @controller: base controller structure > - * @host_list: list containing all the chips attached to the > - * controller > - * > - * @chan: dma channel > - * @cmd_crci: ADM DMA CRCI for command flow control > - * @data_crci: ADM DMA CRCI for data flow control > - * > - * @desc_list: DMA descriptor list (list of desc_infos) > - * > - * @data_buffer: our local DMA buffer for page read/writes, > - * used when we can't use the buffer provided > - * by upper layers directly > - * @reg_read_buf: local buffer for reading back registers via DMA > - * > - * @base_phys: physical base address of controller registers > - * @base_dma: dma base address of controller registers > - * @reg_read_dma: contains dma address for register read buffer > - * > - * @buf_size/count/start: markers for chip->legacy.read_buf/write_buf > - * functions > - * @max_cwperpage: maximum QPIC codewords required. calculated > - * from all connected NAND devices pagesize > - * > - * @reg_read_pos: marker for data read in reg_read_buf > - * > - * @cmd1/vld: some fixed controller register values > - * > - * @exec_opwrite: flag to select correct number of code word > - * while reading status > - */ > -struct qcom_nand_controller { > - struct device *dev; > - > - void __iomem *base; > - > - struct clk *core_clk; > - struct clk *aon_clk; > - > - struct nandc_regs *regs; > - struct bam_transaction *bam_txn; > - > - const struct qcom_nandc_props *props; > - > - struct nand_controller controller; > - struct list_head host_list; > - > - union { > - /* will be used only by QPIC for BAM DMA */ > - struct { > - struct dma_chan *tx_chan; > - struct dma_chan *rx_chan; > - struct dma_chan *cmd_chan; > - }; > - > - /* will be used only by EBI2 for ADM DMA */ > - struct { > - struct dma_chan *chan; > - unsigned int cmd_crci; > - unsigned int data_crci; > - }; > - }; > - > - struct list_head desc_list; > - > - u8 *data_buffer; > - __le32 *reg_read_buf; > - > - phys_addr_t base_phys; > - dma_addr_t base_dma; > - dma_addr_t reg_read_dma; > - > - int buf_size; > - int buf_count; > - int buf_start; > - unsigned int max_cwperpage; > - > - int reg_read_pos; > - > - u32 cmd1, vld; > - bool exec_opwrite; > -}; > - > /* > * NAND special boot partitions > * > @@ -544,113 +124,17 @@ struct qcom_nand_host { > bool bch_enabled; > }; > > -/* > - * This data type corresponds to the NAND controller properties which varies > - * among different NAND controllers. > - * @ecc_modes - ecc mode for NAND > - * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset > - * @is_bam - whether NAND controller is using BAM > - * @is_qpic - whether NAND CTRL is part of qpic IP > - * @qpic_v2 - flag to indicate QPIC IP version 2 > - * @use_codeword_fixup - whether NAND has different layout for boot partitions > - */ > -struct qcom_nandc_props { > - u32 ecc_modes; > - u32 dev_cmd_reg_start; > - bool is_bam; > - bool is_qpic; > - bool qpic_v2; > - bool use_codeword_fixup; > -}; > - > -/* Frees the BAM transaction memory */ > -static void free_bam_transaction(struct qcom_nand_controller *nandc) > -{ > - struct bam_transaction *bam_txn = nandc->bam_txn; > - > - devm_kfree(nandc->dev, bam_txn); > -} > - > -/* Allocates and Initializes the BAM transaction */ > -static struct bam_transaction * > -alloc_bam_transaction(struct qcom_nand_controller *nandc) > -{ > - struct bam_transaction *bam_txn; > - size_t bam_txn_size; > - unsigned int num_cw = nandc->max_cwperpage; > - void *bam_txn_buf; > - > - bam_txn_size = > - sizeof(*bam_txn) + num_cw * > - ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) + > - (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) + > - (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL)); > - > - bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL); > - if (!bam_txn_buf) > - return NULL; > - > - bam_txn = bam_txn_buf; > - bam_txn_buf += sizeof(*bam_txn); > - > - bam_txn->bam_ce = bam_txn_buf; > - bam_txn_buf += > - sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw; > - > - bam_txn->cmd_sgl = bam_txn_buf; > - bam_txn_buf += > - sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw; > - > - bam_txn->data_sgl = bam_txn_buf; > - > - init_completion(&bam_txn->txn_done); > - > - return bam_txn; > -} > - > -/* Clears the BAM transaction indexes */ > -static void clear_bam_transaction(struct qcom_nand_controller *nandc) > +static void nandc_set_reg(struct nand_chip *chip, int offset, > + u32 val) > { > - struct bam_transaction *bam_txn = nandc->bam_txn; > - > - if (!nandc->props->is_bam) > - return; > - > - bam_txn->bam_ce_pos = 0; > - bam_txn->bam_ce_start = 0; > - bam_txn->cmd_sgl_pos = 0; > - bam_txn->cmd_sgl_start = 0; > - bam_txn->tx_sgl_pos = 0; > - bam_txn->tx_sgl_start = 0; > - bam_txn->rx_sgl_pos = 0; > - bam_txn->rx_sgl_start = 0; > - bam_txn->last_data_desc = NULL; > - bam_txn->wait_second_completion = false; > - > - sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * > - QPIC_PER_CW_CMD_SGL); > - sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage * > - QPIC_PER_CW_DATA_SGL); > - > - reinit_completion(&bam_txn->txn_done); > -} > + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); > + struct nandc_regs *regs = nandc->regs; > + __le32 *reg; > > -/* Callback for DMA descriptor completion */ > -static void qpic_bam_dma_done(void *data) > -{ > - struct bam_transaction *bam_txn = data; > + reg = offset_to_nandc_reg(regs, offset); > > - /* > - * In case of data transfer with NAND, 2 callbacks will be generated. > - * One for command channel and another one for data channel. > - * If current transaction has data descriptors > - * (i.e. wait_second_completion is true), then set this to false > - * and wait for second DMA descriptor completion. > - */ > - if (bam_txn->wait_second_completion) > - bam_txn->wait_second_completion = false; > - else > - complete(&bam_txn->txn_done); > + if (reg) > + *reg = cpu_to_le32(val); > } > > static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) > @@ -658,13 +142,6 @@ static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) > return container_of(chip, struct qcom_nand_host, chip); > } > > -static inline struct qcom_nand_controller * > -get_qcom_nand_controller(struct nand_chip *chip) > -{ > - return container_of(chip->controller, struct qcom_nand_controller, > - controller); > -} > - > static inline u32 nandc_read(struct qcom_nand_controller *nandc, int offset) > { > return ioread32(nandc->base + offset); > @@ -676,91 +153,6 @@ static inline void nandc_write(struct qcom_nand_controller *nandc, int offset, > iowrite32(val, nandc->base + offset); > } > > -static inline void nandc_read_buffer_sync(struct qcom_nand_controller *nandc, > - bool is_cpu) > -{ > - if (!nandc->props->is_bam) > - return; > - > - if (is_cpu) > - dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma, > - MAX_REG_RD * > - sizeof(*nandc->reg_read_buf), > - DMA_FROM_DEVICE); > - else > - dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma, > - MAX_REG_RD * > - sizeof(*nandc->reg_read_buf), > - DMA_FROM_DEVICE); > -} > - > -static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset) > -{ > - switch (offset) { > - case NAND_FLASH_CMD: > - return ®s->cmd; > - case NAND_ADDR0: > - return ®s->addr0; > - case NAND_ADDR1: > - return ®s->addr1; > - case NAND_FLASH_CHIP_SELECT: > - return ®s->chip_sel; > - case NAND_EXEC_CMD: > - return ®s->exec; > - case NAND_FLASH_STATUS: > - return ®s->clrflashstatus; > - case NAND_DEV0_CFG0: > - return ®s->cfg0; > - case NAND_DEV0_CFG1: > - return ®s->cfg1; > - case NAND_DEV0_ECC_CFG: > - return ®s->ecc_bch_cfg; > - case NAND_READ_STATUS: > - return ®s->clrreadstatus; > - case NAND_DEV_CMD1: > - return ®s->cmd1; > - case NAND_DEV_CMD1_RESTORE: > - return ®s->orig_cmd1; > - case NAND_DEV_CMD_VLD: > - return ®s->vld; > - case NAND_DEV_CMD_VLD_RESTORE: > - return ®s->orig_vld; > - case NAND_EBI2_ECC_BUF_CFG: > - return ®s->ecc_buf_cfg; > - case NAND_READ_LOCATION_0: > - return ®s->read_location0; > - case NAND_READ_LOCATION_1: > - return ®s->read_location1; > - case NAND_READ_LOCATION_2: > - return ®s->read_location2; > - case NAND_READ_LOCATION_3: > - return ®s->read_location3; > - case NAND_READ_LOCATION_LAST_CW_0: > - return ®s->read_location_last0; > - case NAND_READ_LOCATION_LAST_CW_1: > - return ®s->read_location_last1; > - case NAND_READ_LOCATION_LAST_CW_2: > - return ®s->read_location_last2; > - case NAND_READ_LOCATION_LAST_CW_3: > - return ®s->read_location_last3; > - default: > - return NULL; > - } > -} > - > -static void nandc_set_reg(struct nand_chip *chip, int offset, > - u32 val) > -{ > - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); > - struct nandc_regs *regs = nandc->regs; > - __le32 *reg; > - > - reg = offset_to_nandc_reg(regs, offset); > - > - if (reg) > - *reg = cpu_to_le32(val); > -} > - > /* Helper to check the code word, whether it is last cw or not */ > static bool qcom_nandc_is_last_cw(struct nand_ecc_ctrl *ecc, int cw) > { > @@ -852,383 +244,6 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read, i > host->cw_data : host->cw_size, 1); > } > > -/* > - * Maps the scatter gather list for DMA transfer and forms the DMA descriptor > - * for BAM. This descriptor will be added in the NAND DMA descriptor queue > - * which will be submitted to DMA engine. > - */ > -static int prepare_bam_async_desc(struct qcom_nand_controller *nandc, > - struct dma_chan *chan, > - unsigned long flags) > -{ > - struct desc_info *desc; > - struct scatterlist *sgl; > - unsigned int sgl_cnt; > - int ret; > - struct bam_transaction *bam_txn = nandc->bam_txn; > - enum dma_transfer_direction dir_eng; > - struct dma_async_tx_descriptor *dma_desc; > - > - desc = kzalloc(sizeof(*desc), GFP_KERNEL); > - if (!desc) > - return -ENOMEM; > - > - if (chan == nandc->cmd_chan) { > - sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start]; > - sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start; > - bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos; > - dir_eng = DMA_MEM_TO_DEV; > - desc->dir = DMA_TO_DEVICE; > - } else if (chan == nandc->tx_chan) { > - sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start]; > - sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start; > - bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos; > - dir_eng = DMA_MEM_TO_DEV; > - desc->dir = DMA_TO_DEVICE; > - } else { > - sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start]; > - sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start; > - bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos; > - dir_eng = DMA_DEV_TO_MEM; > - desc->dir = DMA_FROM_DEVICE; > - } > - > - sg_mark_end(sgl + sgl_cnt - 1); > - ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir); > - if (ret == 0) { > - dev_err(nandc->dev, "failure in mapping desc\n"); > - kfree(desc); > - return -ENOMEM; > - } > - > - desc->sgl_cnt = sgl_cnt; > - desc->bam_sgl = sgl; > - > - dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng, > - flags); > - > - if (!dma_desc) { > - dev_err(nandc->dev, "failure in prep desc\n"); > - dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir); > - kfree(desc); > - return -EINVAL; > - } > - > - desc->dma_desc = dma_desc; > - > - /* update last data/command descriptor */ > - if (chan == nandc->cmd_chan) > - bam_txn->last_cmd_desc = dma_desc; > - else > - bam_txn->last_data_desc = dma_desc; > - > - list_add_tail(&desc->node, &nandc->desc_list); > - > - return 0; > -} > - > -/* > - * Prepares the command descriptor for BAM DMA which will be used for NAND > - * register reads and writes. The command descriptor requires the command > - * to be formed in command element type so this function uses the command > - * element from bam transaction ce array and fills the same with required > - * data. A single SGL can contain multiple command elements so > - * NAND_BAM_NEXT_SGL will be used for starting the separate SGL > - * after the current command element. > - */ > -static int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, > - int reg_off, const void *vaddr, > - int size, unsigned int flags) > -{ > - int bam_ce_size; > - int i, ret; > - struct bam_cmd_element *bam_ce_buffer; > - struct bam_transaction *bam_txn = nandc->bam_txn; > - > - bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos]; > - > - /* fill the command desc */ > - for (i = 0; i < size; i++) { > - if (read) > - bam_prep_ce(&bam_ce_buffer[i], > - nandc_reg_phys(nandc, reg_off + 4 * i), > - BAM_READ_COMMAND, > - reg_buf_dma_addr(nandc, > - (__le32 *)vaddr + i)); > - else > - bam_prep_ce_le32(&bam_ce_buffer[i], > - nandc_reg_phys(nandc, reg_off + 4 * i), > - BAM_WRITE_COMMAND, > - *((__le32 *)vaddr + i)); > - } > - > - bam_txn->bam_ce_pos += size; > - > - /* use the separate sgl after this command */ > - if (flags & NAND_BAM_NEXT_SGL) { > - bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start]; > - bam_ce_size = (bam_txn->bam_ce_pos - > - bam_txn->bam_ce_start) * > - sizeof(struct bam_cmd_element); > - sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos], > - bam_ce_buffer, bam_ce_size); > - bam_txn->cmd_sgl_pos++; > - bam_txn->bam_ce_start = bam_txn->bam_ce_pos; > - > - if (flags & NAND_BAM_NWD) { > - ret = prepare_bam_async_desc(nandc, nandc->cmd_chan, > - DMA_PREP_FENCE | > - DMA_PREP_CMD); > - if (ret) > - return ret; > - } > - } > - > - return 0; > -} > - > -/* > - * Prepares the data descriptor for BAM DMA which will be used for NAND > - * data reads and writes. > - */ > -static int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, > - const void *vaddr, > - int size, unsigned int flags) > -{ > - int ret; > - struct bam_transaction *bam_txn = nandc->bam_txn; > - > - if (read) { > - sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos], > - vaddr, size); > - bam_txn->rx_sgl_pos++; > - } else { > - sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos], > - vaddr, size); > - bam_txn->tx_sgl_pos++; > - > - /* > - * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag > - * is not set, form the DMA descriptor > - */ > - if (!(flags & NAND_BAM_NO_EOT)) { > - ret = prepare_bam_async_desc(nandc, nandc->tx_chan, > - DMA_PREP_INTERRUPT); > - if (ret) > - return ret; > - } > - } > - > - return 0; > -} > - > -static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, > - int reg_off, const void *vaddr, int size, > - bool flow_control) > -{ > - struct desc_info *desc; > - struct dma_async_tx_descriptor *dma_desc; > - struct scatterlist *sgl; > - struct dma_slave_config slave_conf; > - struct qcom_adm_peripheral_config periph_conf = {}; > - enum dma_transfer_direction dir_eng; > - int ret; > - > - desc = kzalloc(sizeof(*desc), GFP_KERNEL); > - if (!desc) > - return -ENOMEM; > - > - sgl = &desc->adm_sgl; > - > - sg_init_one(sgl, vaddr, size); > - > - if (read) { > - dir_eng = DMA_DEV_TO_MEM; > - desc->dir = DMA_FROM_DEVICE; > - } else { > - dir_eng = DMA_MEM_TO_DEV; > - desc->dir = DMA_TO_DEVICE; > - } > - > - ret = dma_map_sg(nandc->dev, sgl, 1, desc->dir); > - if (ret == 0) { > - ret = -ENOMEM; > - goto err; > - } > - > - memset(&slave_conf, 0x00, sizeof(slave_conf)); > - > - slave_conf.device_fc = flow_control; > - if (read) { > - slave_conf.src_maxburst = 16; > - slave_conf.src_addr = nandc->base_dma + reg_off; > - if (nandc->data_crci) { > - periph_conf.crci = nandc->data_crci; > - slave_conf.peripheral_config = &periph_conf; > - slave_conf.peripheral_size = sizeof(periph_conf); > - } > - } else { > - slave_conf.dst_maxburst = 16; > - slave_conf.dst_addr = nandc->base_dma + reg_off; > - if (nandc->cmd_crci) { > - periph_conf.crci = nandc->cmd_crci; > - slave_conf.peripheral_config = &periph_conf; > - slave_conf.peripheral_size = sizeof(periph_conf); > - } > - } > - > - ret = dmaengine_slave_config(nandc->chan, &slave_conf); > - if (ret) { > - dev_err(nandc->dev, "failed to configure dma channel\n"); > - goto err; > - } > - > - dma_desc = dmaengine_prep_slave_sg(nandc->chan, sgl, 1, dir_eng, 0); > - if (!dma_desc) { > - dev_err(nandc->dev, "failed to prepare desc\n"); > - ret = -EINVAL; > - goto err; > - } > - > - desc->dma_desc = dma_desc; > - > - list_add_tail(&desc->node, &nandc->desc_list); > - > - return 0; > -err: > - kfree(desc); > - > - return ret; > -} > - > -/* > - * read_reg_dma: prepares a descriptor to read a given number of > - * contiguous registers to the reg_read_buf pointer > - * > - * @first: offset of the first register in the contiguous block > - * @num_regs: number of registers to read > - * @flags: flags to control DMA descriptor preparation > - */ > -static int read_reg_dma(struct qcom_nand_controller *nandc, int first, > - int num_regs, unsigned int flags) > -{ > - bool flow_control = false; > - void *vaddr; > - > - vaddr = nandc->reg_read_buf + nandc->reg_read_pos; > - nandc->reg_read_pos += num_regs; > - > - if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1) > - first = dev_cmd_reg_addr(nandc, first); > - > - if (nandc->props->is_bam) > - return prep_bam_dma_desc_cmd(nandc, true, first, vaddr, > - num_regs, flags); > - > - if (first == NAND_READ_ID || first == NAND_FLASH_STATUS) > - flow_control = true; > - > - return prep_adm_dma_desc(nandc, true, first, vaddr, > - num_regs * sizeof(u32), flow_control); > -} > - > -/* > - * write_reg_dma: prepares a descriptor to write a given number of > - * contiguous registers > - * > - * @first: offset of the first register in the contiguous block > - * @num_regs: number of registers to write > - * @flags: flags to control DMA descriptor preparation > - */ > -static int write_reg_dma(struct qcom_nand_controller *nandc, int first, > - int num_regs, unsigned int flags) > -{ > - bool flow_control = false; > - struct nandc_regs *regs = nandc->regs; > - void *vaddr; > - > - vaddr = offset_to_nandc_reg(regs, first); > - > - if (first == NAND_ERASED_CW_DETECT_CFG) { > - if (flags & NAND_ERASED_CW_SET) > - vaddr = ®s->erased_cw_detect_cfg_set; > - else > - vaddr = ®s->erased_cw_detect_cfg_clr; > - } > - > - if (first == NAND_EXEC_CMD) > - flags |= NAND_BAM_NWD; > - > - if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1) > - first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1); > - > - if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD) > - first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD); > - > - if (nandc->props->is_bam) > - return prep_bam_dma_desc_cmd(nandc, false, first, vaddr, > - num_regs, flags); > - > - if (first == NAND_FLASH_CMD) > - flow_control = true; > - > - return prep_adm_dma_desc(nandc, false, first, vaddr, > - num_regs * sizeof(u32), flow_control); > -} > - > -/* > - * read_data_dma: prepares a DMA descriptor to transfer data from the > - * controller's internal buffer to the buffer 'vaddr' > - * > - * @reg_off: offset within the controller's data buffer > - * @vaddr: virtual address of the buffer we want to write to > - * @size: DMA transaction size in bytes > - * @flags: flags to control DMA descriptor preparation > - */ > -static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, > - const u8 *vaddr, int size, unsigned int flags) > -{ > - if (nandc->props->is_bam) > - return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags); > - > - return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false); > -} > - > -/* > - * write_data_dma: prepares a DMA descriptor to transfer data from > - * 'vaddr' to the controller's internal buffer > - * > - * @reg_off: offset within the controller's data buffer > - * @vaddr: virtual address of the buffer we want to read from > - * @size: DMA transaction size in bytes > - * @flags: flags to control DMA descriptor preparation > - */ > -static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, > - const u8 *vaddr, int size, unsigned int flags) > -{ > - if (nandc->props->is_bam) > - return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags); > - > - return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false); > -} > - > -/* > - * Helper to prepare DMA descriptors for configuring registers > - * before reading a NAND page. > - */ > -static void config_nand_page_read(struct nand_chip *chip) > -{ > - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); > - > - write_reg_dma(nandc, NAND_ADDR0, 2, 0); > - write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); > - if (!nandc->props->qpic_v2) > - write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0); > - write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0); > - write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, > - NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL); > -} > - > /* > * Helper to prepare DMA descriptors for configuring registers > * before reading each codeword in NAND page. > @@ -1303,88 +318,6 @@ static void config_nand_cw_write(struct nand_chip *chip) > write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); > } > > -/* helpers to submit/free our list of dma descriptors */ > -static int submit_descs(struct qcom_nand_controller *nandc) > -{ > - struct desc_info *desc, *n; > - dma_cookie_t cookie = 0; > - struct bam_transaction *bam_txn = nandc->bam_txn; > - int ret = 0; > - > - if (nandc->props->is_bam) { > - if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { > - ret = prepare_bam_async_desc(nandc, nandc->rx_chan, 0); > - if (ret) > - goto err_unmap_free_desc; > - } > - > - if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) { > - ret = prepare_bam_async_desc(nandc, nandc->tx_chan, > - DMA_PREP_INTERRUPT); > - if (ret) > - goto err_unmap_free_desc; > - } > - > - if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) { > - ret = prepare_bam_async_desc(nandc, nandc->cmd_chan, > - DMA_PREP_CMD); > - if (ret) > - goto err_unmap_free_desc; > - } > - } > - > - list_for_each_entry(desc, &nandc->desc_list, node) > - cookie = dmaengine_submit(desc->dma_desc); > - > - if (nandc->props->is_bam) { > - bam_txn->last_cmd_desc->callback = qpic_bam_dma_done; > - bam_txn->last_cmd_desc->callback_param = bam_txn; > - if (bam_txn->last_data_desc) { > - bam_txn->last_data_desc->callback = qpic_bam_dma_done; > - bam_txn->last_data_desc->callback_param = bam_txn; > - bam_txn->wait_second_completion = true; > - } > - > - dma_async_issue_pending(nandc->tx_chan); > - dma_async_issue_pending(nandc->rx_chan); > - dma_async_issue_pending(nandc->cmd_chan); > - > - if (!wait_for_completion_timeout(&bam_txn->txn_done, > - QPIC_NAND_COMPLETION_TIMEOUT)) > - ret = -ETIMEDOUT; > - } else { > - if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) > - ret = -ETIMEDOUT; > - } > - > -err_unmap_free_desc: > - /* > - * Unmap the dma sg_list and free the desc allocated by both > - * prepare_bam_async_desc() and prep_adm_dma_desc() functions. > - */ > - list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { > - list_del(&desc->node); > - > - if (nandc->props->is_bam) > - dma_unmap_sg(nandc->dev, desc->bam_sgl, > - desc->sgl_cnt, desc->dir); > - else > - dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1, > - desc->dir); > - > - kfree(desc); > - } > - > - return ret; > -} > - > -/* reset the register read buffer for next NAND operation */ > -static void clear_read_regs(struct qcom_nand_controller *nandc) > -{ > - nandc->reg_read_pos = 0; > - nandc_read_buffer_sync(nandc, false); > -} > - > /* > * when using BCH ECC, the HW flags an error in NAND_FLASH_STATUS if it read > * an erased CW, and reports an erased CW in NAND_ERASED_CW_DETECT_STATUS. > @@ -3016,136 +1949,6 @@ static const struct nand_controller_ops qcom_nandc_ops = { > .exec_op = qcom_nand_exec_op, > }; > > -static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) > -{ > - if (nandc->props->is_bam) { > - if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) > - dma_unmap_single(nandc->dev, nandc->reg_read_dma, > - MAX_REG_RD * > - sizeof(*nandc->reg_read_buf), > - DMA_FROM_DEVICE); > - > - if (nandc->tx_chan) > - dma_release_channel(nandc->tx_chan); > - > - if (nandc->rx_chan) > - dma_release_channel(nandc->rx_chan); > - > - if (nandc->cmd_chan) > - dma_release_channel(nandc->cmd_chan); > - } else { > - if (nandc->chan) > - dma_release_channel(nandc->chan); > - } > -} > - > -static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) > -{ > - int ret; > - > - ret = dma_set_coherent_mask(nandc->dev, DMA_BIT_MASK(32)); > - if (ret) { > - dev_err(nandc->dev, "failed to set DMA mask\n"); > - return ret; > - } > - > - /* > - * we use the internal buffer for reading ONFI params, reading small > - * data like ID and status, and preforming read-copy-write operations > - * when writing to a codeword partially. 532 is the maximum possible > - * size of a codeword for our nand controller > - */ > - nandc->buf_size = 532; > - > - nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size, GFP_KERNEL); > - if (!nandc->data_buffer) > - return -ENOMEM; > - > - nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs), GFP_KERNEL); > - if (!nandc->regs) > - return -ENOMEM; > - > - nandc->reg_read_buf = devm_kcalloc(nandc->dev, MAX_REG_RD, > - sizeof(*nandc->reg_read_buf), > - GFP_KERNEL); > - if (!nandc->reg_read_buf) > - return -ENOMEM; > - > - if (nandc->props->is_bam) { > - nandc->reg_read_dma = > - dma_map_single(nandc->dev, nandc->reg_read_buf, > - MAX_REG_RD * > - sizeof(*nandc->reg_read_buf), > - DMA_FROM_DEVICE); > - if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) { > - dev_err(nandc->dev, "failed to DMA MAP reg buffer\n"); > - return -EIO; > - } > - > - nandc->tx_chan = dma_request_chan(nandc->dev, "tx"); > - if (IS_ERR(nandc->tx_chan)) { > - ret = PTR_ERR(nandc->tx_chan); > - nandc->tx_chan = NULL; > - dev_err_probe(nandc->dev, ret, > - "tx DMA channel request failed\n"); > - goto unalloc; > - } > - > - nandc->rx_chan = dma_request_chan(nandc->dev, "rx"); > - if (IS_ERR(nandc->rx_chan)) { > - ret = PTR_ERR(nandc->rx_chan); > - nandc->rx_chan = NULL; > - dev_err_probe(nandc->dev, ret, > - "rx DMA channel request failed\n"); > - goto unalloc; > - } > - > - nandc->cmd_chan = dma_request_chan(nandc->dev, "cmd"); > - if (IS_ERR(nandc->cmd_chan)) { > - ret = PTR_ERR(nandc->cmd_chan); > - nandc->cmd_chan = NULL; > - dev_err_probe(nandc->dev, ret, > - "cmd DMA channel request failed\n"); > - goto unalloc; > - } > - > - /* > - * Initially allocate BAM transaction to read ONFI param page. > - * After detecting all the devices, this BAM transaction will > - * be freed and the next BAM transaction will be allocated with > - * maximum codeword size > - */ > - nandc->max_cwperpage = 1; > - nandc->bam_txn = alloc_bam_transaction(nandc); > - if (!nandc->bam_txn) { > - dev_err(nandc->dev, > - "failed to allocate bam transaction\n"); > - ret = -ENOMEM; > - goto unalloc; > - } > - } else { > - nandc->chan = dma_request_chan(nandc->dev, "rxtx"); > - if (IS_ERR(nandc->chan)) { > - ret = PTR_ERR(nandc->chan); > - nandc->chan = NULL; > - dev_err_probe(nandc->dev, ret, > - "rxtx DMA channel request failed\n"); > - return ret; > - } > - } > - > - INIT_LIST_HEAD(&nandc->desc_list); > - INIT_LIST_HEAD(&nandc->host_list); > - > - nand_controller_init(&nandc->controller); > - nandc->controller.ops = &qcom_nandc_ops; > - > - return 0; > -unalloc: > - qcom_nandc_unalloc(nandc); > - return ret; > -} > - > /* one time setup of a few nand controller registers */ > static int qcom_nandc_setup(struct qcom_nand_controller *nandc) > { > @@ -3427,6 +2230,9 @@ static int qcom_nandc_probe(struct platform_device *pdev) > if (ret) > goto err_nandc_alloc; > > + nand_controller_init(&nandc->controller); > + nandc->controller.ops = &qcom_nandc_ops; > + > ret = qcom_nandc_setup(nandc); > if (ret) > goto err_setup; > @@ -3473,28 +2279,28 @@ static void qcom_nandc_remove(struct platform_device *pdev) > DMA_BIDIRECTIONAL, 0); > } > > -static const struct qcom_nandc_props ipq806x_nandc_props = { > +static struct qcom_nandc_props ipq806x_nandc_props = { > .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT), > .is_bam = false, > .use_codeword_fixup = true, > .dev_cmd_reg_start = 0x0, > }; > > -static const struct qcom_nandc_props ipq4019_nandc_props = { > +static struct qcom_nandc_props ipq4019_nandc_props = { > .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), > .is_bam = true, > .is_qpic = true, > .dev_cmd_reg_start = 0x0, > }; > > -static const struct qcom_nandc_props ipq8074_nandc_props = { > +static struct qcom_nandc_props ipq8074_nandc_props = { > .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), > .is_bam = true, > .is_qpic = true, > .dev_cmd_reg_start = 0x7000, > }; > > -static const struct qcom_nandc_props sdx55_nandc_props = { > +static struct qcom_nandc_props sdx55_nandc_props = { > .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), > .is_bam = true, > .is_qpic = true, > diff --git a/include/linux/mtd/nand-qpic-common.h b/include/linux/mtd/nand-qpic-common.h > new file mode 100644 > index 000000000000..891f975ca173 > --- /dev/null > +++ b/include/linux/mtd/nand-qpic-common.h > @@ -0,0 +1,488 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * QCOM QPIC common APIs header file > + * > + * Copyright (c) 2023 Qualcomm Inc. > + * Authors: Md sadre Alam > + * Sricharan R > + * Varadarajan Narayanan > + * > + */ > +#ifndef __MTD_NAND_QPIC_COMMON_H__ > +#define __MTD_NAND_QPIC_COMMON_H__ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* NANDc reg offsets */ > +#define NAND_FLASH_CMD 0x00 > +#define NAND_ADDR0 0x04 > +#define NAND_ADDR1 0x08 > +#define NAND_FLASH_CHIP_SELECT 0x0c > +#define NAND_EXEC_CMD 0x10 > +#define NAND_FLASH_STATUS 0x14 > +#define NAND_BUFFER_STATUS 0x18 > +#define NAND_DEV0_CFG0 0x20 > +#define NAND_DEV0_CFG1 0x24 > +#define NAND_DEV0_ECC_CFG 0x28 > +#define NAND_AUTO_STATUS_EN 0x2c > +#define NAND_DEV1_CFG0 0x30 > +#define NAND_DEV1_CFG1 0x34 > +#define NAND_READ_ID 0x40 > +#define NAND_READ_STATUS 0x44 > +#define NAND_DEV_CMD0 0xa0 > +#define NAND_DEV_CMD1 0xa4 > +#define NAND_DEV_CMD2 0xa8 > +#define NAND_DEV_CMD_VLD 0xac > +#define SFLASHC_BURST_CFG 0xe0 > +#define NAND_ERASED_CW_DETECT_CFG 0xe8 > +#define NAND_ERASED_CW_DETECT_STATUS 0xec > +#define NAND_EBI2_ECC_BUF_CFG 0xf0 > +#define FLASH_BUF_ACC 0x100 > + > +#define NAND_CTRL 0xf00 > +#define NAND_VERSION 0xf08 > +#define NAND_READ_LOCATION_0 0xf20 > +#define NAND_READ_LOCATION_1 0xf24 > +#define NAND_READ_LOCATION_2 0xf28 > +#define NAND_READ_LOCATION_3 0xf2c > +#define NAND_READ_LOCATION_LAST_CW_0 0xf40 > +#define NAND_READ_LOCATION_LAST_CW_1 0xf44 > +#define NAND_READ_LOCATION_LAST_CW_2 0xf48 > +#define NAND_READ_LOCATION_LAST_CW_3 0xf4c > + > +/* dummy register offsets, used by write_reg_dma */ > +#define NAND_DEV_CMD1_RESTORE 0xdead > +#define NAND_DEV_CMD_VLD_RESTORE 0xbeef > + > +/* NAND_FLASH_CMD bits */ > +#define PAGE_ACC BIT(4) > +#define LAST_PAGE BIT(5) > + > +/* NAND_FLASH_CHIP_SELECT bits */ > +#define NAND_DEV_SEL 0 > +#define DM_EN BIT(2) > + > +/* NAND_FLASH_STATUS bits */ > +#define FS_OP_ERR BIT(4) > +#define FS_READY_BSY_N BIT(5) > +#define FS_MPU_ERR BIT(8) > +#define FS_DEVICE_STS_ERR BIT(16) > +#define FS_DEVICE_WP BIT(23) > + > +/* NAND_BUFFER_STATUS bits */ > +#define BS_UNCORRECTABLE_BIT BIT(8) > +#define BS_CORRECTABLE_ERR_MSK 0x1f > + > +/* NAND_DEVn_CFG0 bits */ > +#define DISABLE_STATUS_AFTER_WRITE 4 > +#define CW_PER_PAGE 6 > +#define UD_SIZE_BYTES 9 > +#define UD_SIZE_BYTES_MASK GENMASK(18, 9) > +#define ECC_PARITY_SIZE_BYTES_RS 19 > +#define SPARE_SIZE_BYTES 23 > +#define SPARE_SIZE_BYTES_MASK GENMASK(26, 23) > +#define NUM_ADDR_CYCLES 27 > +#define STATUS_BFR_READ 30 > +#define SET_RD_MODE_AFTER_STATUS 31 > + > +/* NAND_DEVn_CFG0 bits */ > +#define DEV0_CFG1_ECC_DISABLE 0 > +#define WIDE_FLASH 1 > +#define NAND_RECOVERY_CYCLES 2 > +#define CS_ACTIVE_BSY 5 > +#define BAD_BLOCK_BYTE_NUM 6 > +#define BAD_BLOCK_IN_SPARE_AREA 16 > +#define WR_RD_BSY_GAP 17 > +#define ENABLE_BCH_ECC 27 > + > +/* NAND_DEV0_ECC_CFG bits */ > +#define ECC_CFG_ECC_DISABLE 0 > +#define ECC_SW_RESET 1 > +#define ECC_MODE 4 > +#define ECC_PARITY_SIZE_BYTES_BCH 8 > +#define ECC_NUM_DATA_BYTES 16 > +#define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16) > +#define ECC_FORCE_CLK_OPEN 30 > + > +/* NAND_DEV_CMD1 bits */ > +#define READ_ADDR 0 > + > +/* NAND_DEV_CMD_VLD bits */ > +#define READ_START_VLD BIT(0) > +#define READ_STOP_VLD BIT(1) > +#define WRITE_START_VLD BIT(2) > +#define ERASE_START_VLD BIT(3) > +#define SEQ_READ_START_VLD BIT(4) > + > +/* NAND_EBI2_ECC_BUF_CFG bits */ > +#define NUM_STEPS 0 > + > +/* NAND_ERASED_CW_DETECT_CFG bits */ > +#define ERASED_CW_ECC_MASK 1 > +#define AUTO_DETECT_RES 0 > +#define MASK_ECC BIT(ERASED_CW_ECC_MASK) > +#define RESET_ERASED_DET BIT(AUTO_DETECT_RES) > +#define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) > +#define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) > +#define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) > + > +/* NAND_ERASED_CW_DETECT_STATUS bits */ > +#define PAGE_ALL_ERASED BIT(7) > +#define CODEWORD_ALL_ERASED BIT(6) > +#define PAGE_ERASED BIT(5) > +#define CODEWORD_ERASED BIT(4) > +#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED) > +#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED) > + > +/* NAND_READ_LOCATION_n bits */ > +#define READ_LOCATION_OFFSET 0 > +#define READ_LOCATION_SIZE 16 > +#define READ_LOCATION_LAST 31 > + > +/* Version Mask */ > +#define NAND_VERSION_MAJOR_MASK 0xf0000000 > +#define NAND_VERSION_MAJOR_SHIFT 28 > +#define NAND_VERSION_MINOR_MASK 0x0fff0000 > +#define NAND_VERSION_MINOR_SHIFT 16 > + > +/* NAND OP_CMDs */ > +#define OP_PAGE_READ 0x2 > +#define OP_PAGE_READ_WITH_ECC 0x3 > +#define OP_PAGE_READ_WITH_ECC_SPARE 0x4 > +#define OP_PAGE_READ_ONFI_READ 0x5 > +#define OP_PROGRAM_PAGE 0x6 > +#define OP_PAGE_PROGRAM_WITH_ECC 0x7 > +#define OP_PROGRAM_PAGE_SPARE 0x9 > +#define OP_BLOCK_ERASE 0xa > +#define OP_CHECK_STATUS 0xc > +#define OP_FETCH_ID 0xb > +#define OP_RESET_DEVICE 0xd > + > +/* Default Value for NAND_DEV_CMD_VLD */ > +#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \ > + ERASE_START_VLD | SEQ_READ_START_VLD) > + > +/* NAND_CTRL bits */ > +#define BAM_MODE_EN BIT(0) > + > +/* > + * the NAND controller performs reads/writes with ECC in 516 byte chunks. > + * the driver calls the chunks 'step' or 'codeword' interchangeably > + */ > +#define NANDC_STEP_SIZE 512 > + > +/* > + * the largest page size we support is 8K, this will have 16 steps/codewords > + * of 512 bytes each > + */ > +#define MAX_NUM_STEPS (SZ_8K / NANDC_STEP_SIZE) > + > +/* we read at most 3 registers per codeword scan */ > +#define MAX_REG_RD (3 * MAX_NUM_STEPS) > + > +#define QPIC_PER_CW_CMD_ELEMENTS 32 > +#define QPIC_PER_CW_CMD_SGL 32 > +#define QPIC_PER_CW_DATA_SGL 8 > + > +#define QPIC_NAND_COMPLETION_TIMEOUT msecs_to_jiffies(2000) > + > +/* > + * Flags used in DMA descriptor preparation helper functions > + * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma) > + */ > +/* Don't set the EOT in current tx BAM sgl */ > +#define NAND_BAM_NO_EOT BIT(0) > +/* Set the NWD flag in current BAM sgl */ > +#define NAND_BAM_NWD BIT(1) > +/* Finish writing in the current BAM sgl and start writing in another BAM sgl */ > +#define NAND_BAM_NEXT_SGL BIT(2) > + > +/* > + * Returns the actual register address for all NAND_DEV_ registers > + * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD) > + */ > +#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg)) > + > +/* Returns the NAND register physical address */ > +#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset)) > + > +/* Returns the dma address for reg read buffer */ > +#define reg_buf_dma_addr(chip, vaddr) \ > + ((chip)->reg_read_dma + \ > + ((u8 *)(vaddr) - (u8 *)(chip)->reg_read_buf)) > + > +/* > + * Erased codeword status is being used two times in single transfer so this > + * flag will determine the current value of erased codeword status register > + */ > +#define NAND_ERASED_CW_SET BIT(4) > + > +#define MAX_ADDRESS_CYCLE 5 > + > +/* > + * This data type corresponds to the BAM transaction which will be used for all > + * NAND transfers. > + * @bam_ce - the array of BAM command elements > + * @cmd_sgl - sgl for NAND BAM command pipe > + * @data_sgl - sgl for NAND BAM consumer/producer pipe > + * @last_data_desc - last DMA desc in data channel (tx/rx). > + * @last_cmd_desc - last DMA desc in command channel. > + * @txn_done - completion for NAND transfer. > + * @bam_ce_pos - the index in bam_ce which is available for next sgl > + * @bam_ce_start - the index in bam_ce which marks the start position ce > + * for current sgl. It will be used for size calculation > + * for current sgl > + * @cmd_sgl_pos - current index in command sgl. > + * @cmd_sgl_start - start index in command sgl. > + * @tx_sgl_pos - current index in data sgl for tx. > + * @tx_sgl_start - start index in data sgl for tx. > + * @rx_sgl_pos - current index in data sgl for rx. > + * @rx_sgl_start - start index in data sgl for rx. > + * @wait_second_completion - wait for second DMA desc completion before making > + * the NAND transfer completion. > + */ > +struct bam_transaction { > + struct bam_cmd_element *bam_ce; > + struct scatterlist *cmd_sgl; > + struct scatterlist *data_sgl; > + struct dma_async_tx_descriptor *last_data_desc; > + struct dma_async_tx_descriptor *last_cmd_desc; > + struct completion txn_done; > + u32 bam_ce_pos; > + u32 bam_ce_start; > + u32 cmd_sgl_pos; > + u32 cmd_sgl_start; > + u32 tx_sgl_pos; > + u32 tx_sgl_start; > + u32 rx_sgl_pos; > + u32 rx_sgl_start; > + bool wait_second_completion; > +}; > + > +/* > + * This data type corresponds to the nand dma descriptor > + * @dma_desc - low level DMA engine descriptor > + * @list - list for desc_info > + * > + * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by > + * ADM > + * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM > + * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM > + * @dir - DMA transfer direction > + */ > +struct desc_info { > + struct dma_async_tx_descriptor *dma_desc; > + struct list_head node; > + > + union { > + struct scatterlist adm_sgl; > + struct { > + struct scatterlist *bam_sgl; > + int sgl_cnt; > + }; > + }; > + enum dma_data_direction dir; > +}; > + > +/* > + * holds the current register values that we want to write. acts as a contiguous > + * chunk of memory which we use to write the controller registers through DMA. > + */ > +struct nandc_regs { > + __le32 cmd; > + __le32 addr0; > + __le32 addr1; > + __le32 chip_sel; > + __le32 exec; > + > + __le32 cfg0; > + __le32 cfg1; > + __le32 ecc_bch_cfg; > + > + __le32 clrflashstatus; > + __le32 clrreadstatus; > + > + __le32 cmd1; > + __le32 vld; > + > + __le32 orig_cmd1; > + __le32 orig_vld; > + > + __le32 ecc_buf_cfg; > + __le32 read_location0; > + __le32 read_location1; > + __le32 read_location2; > + __le32 read_location3; > + __le32 read_location_last0; > + __le32 read_location_last1; > + __le32 read_location_last2; > + __le32 read_location_last3; > + > + __le32 erased_cw_detect_cfg_clr; > + __le32 erased_cw_detect_cfg_set; > +}; > + > +/* > + * NAND controller data struct > + * > + * @dev: parent device > + * > + * @base: MMIO base > + * > + * @core_clk: controller clock > + * @aon_clk: another controller clock > + * > + * @regs: a contiguous chunk of memory for DMA register > + * writes. contains the register values to be > + * written to controller > + * > + * @props: properties of current NAND controller, > + * initialized via DT match data > + * > + * @controller: base controller structure > + * @host_list: list containing all the chips attached to the > + * controller > + * > + * @chan: dma channel > + * @cmd_crci: ADM DMA CRCI for command flow control > + * @data_crci: ADM DMA CRCI for data flow control > + * > + * @desc_list: DMA descriptor list (list of desc_infos) > + * > + * @data_buffer: our local DMA buffer for page read/writes, > + * used when we can't use the buffer provided > + * by upper layers directly > + * @reg_read_buf: local buffer for reading back registers via DMA > + * > + * @base_phys: physical base address of controller registers > + * @base_dma: dma base address of controller registers > + * @reg_read_dma: contains dma address for register read buffer > + * > + * @buf_size/count/start: markers for chip->legacy.read_buf/write_buf > + * functions > + * @max_cwperpage: maximum QPIC codewords required. calculated > + * from all connected NAND devices pagesize > + * > + * @reg_read_pos: marker for data read in reg_read_buf > + * > + * @cmd1/vld: some fixed controller register values > + * > + * @exec_opwrite: flag to select correct number of code word > + * while reading status > + */ > +struct qcom_nand_controller { > + struct device *dev; > + > + void __iomem *base; > + > + struct clk *core_clk; > + struct clk *aon_clk; > + > + struct nandc_regs *regs; > + struct bam_transaction *bam_txn; > + > + const struct qcom_nandc_props *props; > + > + struct nand_controller controller; > + struct list_head host_list; > + > + union { > + /* will be used only by QPIC for BAM DMA */ > + struct { > + struct dma_chan *tx_chan; > + struct dma_chan *rx_chan; > + struct dma_chan *cmd_chan; > + }; > + > + /* will be used only by EBI2 for ADM DMA */ > + struct { > + struct dma_chan *chan; > + unsigned int cmd_crci; > + unsigned int data_crci; > + }; > + }; > + > + struct list_head desc_list; > + > + u8 *data_buffer; > + __le32 *reg_read_buf; > + > + phys_addr_t base_phys; > + dma_addr_t base_dma; > + dma_addr_t reg_read_dma; > + > + int buf_size; > + int buf_count; > + int buf_start; > + unsigned int max_cwperpage; > + > + int reg_read_pos; > + > + u32 cmd1, vld; > + bool exec_opwrite; > +}; > + > +/* > + * This data type corresponds to the NAND controller properties which varies > + * among different NAND controllers. > + * @ecc_modes - ecc mode for NAND > + * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset > + * @is_bam - whether NAND controller is using BAM > + * @is_qpic - whether NAND CTRL is part of qpic IP > + * @qpic_v2 - flag to indicate QPIC IP version 2 > + * @use_codeword_fixup - whether NAND has different layout for boot partitions > + */ > +struct qcom_nandc_props { > + u32 ecc_modes; > + u32 dev_cmd_reg_start; > + bool is_bam; > + bool is_qpic; > + bool qpic_v2; > + bool use_codeword_fixup; > +}; > + > +void config_nand_page_read(struct nand_chip *chip); > +void free_bam_transaction(struct qcom_nand_controller *nandc); > +void qpic_bam_dma_done(void *data); > +void nandc_read_buffer_sync(struct qcom_nand_controller *nandc, bool is_cpu); > +__le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset); > +void clear_read_regs(struct qcom_nand_controller *nandc); > +int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, > + int reg_off, const void *vaddr, int size, > + bool flow_control); > +int submit_descs(struct qcom_nand_controller *nandc); > +int prepare_bam_async_desc(struct qcom_nand_controller *nandc, > + struct dma_chan *chan, unsigned long flags); > +int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read, > + int reg_off, const void *vaddr, > + int size, unsigned int flags); > +int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read, > + const void *vaddr, > + int size, unsigned int flags); > +int read_reg_dma(struct qcom_nand_controller *nandc, int first, > + int num_regs, unsigned int flags); > +int write_reg_dma(struct qcom_nand_controller *nandc, int first, > + int num_regs, unsigned int flags); > +int read_data_dma(struct qcom_nand_controller *nandc, int reg_off, > + const u8 *vaddr, int size, unsigned int flags); > +int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, > + const u8 *vaddr, int size, unsigned int flags); > +struct bam_transaction *alloc_bam_transaction(struct qcom_nand_controller *nandc); > +void clear_bam_transaction(struct qcom_nand_controller *nandc); > +void qcom_nandc_unalloc(struct qcom_nand_controller *nandc); > +int qcom_nandc_alloc(struct qcom_nand_controller *nandc); > +struct qcom_nand_controller *get_qcom_nand_controller(struct nand_chip *chip); > + > +#endif