From patchwork Fri Jun 12 15:22:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christophe Kerello X-Patchwork-Id: 199146 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id F3D7EC433DF for ; Fri, 12 Jun 2020 15:23:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C738F208D5 for ; Fri, 12 Jun 2020 15:23:53 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=st.com header.i=@st.com header.b="gaRrWQAi" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726272AbgFLPXw (ORCPT ); Fri, 12 Jun 2020 11:23:52 -0400 Received: from mx07-00178001.pphosted.com ([62.209.51.94]:35520 "EHLO mx07-00178001.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726671AbgFLPXt (ORCPT ); Fri, 12 Jun 2020 11:23:49 -0400 Received: from pps.filterd (m0046668.ppops.net [127.0.0.1]) by mx07-00178001.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 05CFNQZp024862; Fri, 12 Jun 2020 17:23:33 +0200 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=st.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-type; s=STMicroelectronics; bh=Q101sfsSV+OoKcj+H/16Nupp8EZzYMk3qWBT15hQdK8=; b=gaRrWQAidH6+Q5VUfVAKskyfs5TYPsU9SXZzo2MJUZ2wtco+BWWoVeoeRpJN3at7agAV cYVkjKnABN4QJefK0UaijC3m/ByqpV+JaCbcruHlli+y9n7oYVRJOWqbREyx+x7ZGJ4m H0+myUeTAhdzLZb+2tKUnz7W4bB0jvU71c+KMw0f3rAkrVuHNvwAjKvui9uIWWgxY3Bd ZHGcrVW2Ga/eDP/om12WpzDVnJ5r+NhaKZa31mih73MExwdnmuAQLmGzoelOaH63qkEX ySTdMIeIcnJRM47BFeyhMb5G32zZyedcJCmktN6pHtpT0xEUTljqTr/KsuKzAjrhByHz ew== Received: from beta.dmz-eu.st.com (beta.dmz-eu.st.com [164.129.1.35]) by mx07-00178001.pphosted.com with ESMTP id 31jppp6g1k-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 12 Jun 2020 17:23:33 +0200 Received: from euls16034.sgp.st.com (euls16034.sgp.st.com [10.75.44.20]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id 5A64F100034; Fri, 12 Jun 2020 17:23:33 +0200 (CEST) Received: from Webmail-eu.st.com (sfhdag6node2.st.com [10.75.127.17]) by euls16034.sgp.st.com (STMicroelectronics) with ESMTP id 4BA252B9918; Fri, 12 Jun 2020 17:23:33 +0200 (CEST) Received: from localhost (10.75.127.51) by SFHDAG6NODE2.st.com (10.75.127.17) with Microsoft SMTP Server (TLS) id 15.0.1347.2; Fri, 12 Jun 2020 17:23:32 +0200 From: Christophe Kerello To: , , , , , , CC: , , , , , Christophe Kerello Subject: [PATCH v5 3/6] dt-bindings: memory-controller: add STM32 FMC2 EBI controller documentation Date: Fri, 12 Jun 2020 17:22:39 +0200 Message-ID: <1591975362-22009-4-git-send-email-christophe.kerello@st.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1591975362-22009-1-git-send-email-christophe.kerello@st.com> References: <1591975362-22009-1-git-send-email-christophe.kerello@st.com> MIME-Version: 1.0 X-Originating-IP: [10.75.127.51] X-ClientProxiedBy: SFHDAG1NODE2.st.com (10.75.127.2) To SFHDAG6NODE2.st.com (10.75.127.17) X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.216, 18.0.687 definitions=2020-06-12_11:2020-06-12,2020-06-12 signatures=0 Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org This patch adds the documentation of the device tree bindings for the STM32 FMC2 EBI controller. Signed-off-by: Christophe Kerello Reviewed-by: Rob Herring --- Changes in v5: - fix indent descriptions - add Rob reviewed-by tag Changes in v4: - fix filename: st,stm32-fmc2-ebi.yaml Changes in v3: - pattern name has been modified - vendor properties have been modified - s/_/-/ - add unit suffix (-ns) on timing properties .../memory-controllers/st,stm32-fmc2-ebi.yaml | 252 +++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 Documentation/devicetree/bindings/memory-controllers/st,stm32-fmc2-ebi.yaml diff --git a/Documentation/devicetree/bindings/memory-controllers/st,stm32-fmc2-ebi.yaml b/Documentation/devicetree/bindings/memory-controllers/st,stm32-fmc2-ebi.yaml new file mode 100644 index 0000000..70eaf73 --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/st,stm32-fmc2-ebi.yaml @@ -0,0 +1,252 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/st,stm32-fmc2-ebi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics Flexible Memory Controller 2 (FMC2) Bindings + +description: | + The FMC2 functional block makes the interface with: synchronous and + asynchronous static devices (such as PSNOR, PSRAM or other memory-mapped + peripherals) and NAND flash memories. + Its main purposes are: + - to translate AXI transactions into the appropriate external device + protocol + - to meet the access time requirements of the external devices + All external devices share the addresses, data and control signals with the + controller. Each external device is accessed by means of a unique Chip + Select. The FMC2 performs only one access at a time to an external device. + +maintainers: + - Christophe Kerello + +properties: + compatible: + const: st,stm32mp1-fmc2-ebi + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + "#address-cells": + const: 2 + + "#size-cells": + const: 1 + + ranges: + description: | + Reflects the memory layout with four integer values per bank. Format: + 0
+ +patternProperties: + "^.*@[0-4],[a-f0-9]+$": + type: object + + properties: + reg: + description: Bank number, base address and size of the device. + + st,fmc2-ebi-cs-transaction-type: + description: | + Select one of the transactions type supported + 0: Asynchronous mode 1 SRAM/FRAM. + 1: Asynchronous mode 1 PSRAM. + 2: Asynchronous mode A SRAM/FRAM. + 3: Asynchronous mode A PSRAM. + 4: Asynchronous mode 2 NOR. + 5: Asynchronous mode B NOR. + 6: Asynchronous mode C NOR. + 7: Asynchronous mode D NOR. + 8: Synchronous read synchronous write PSRAM. + 9: Synchronous read asynchronous write PSRAM. + 10: Synchronous read synchronous write NOR. + 11: Synchronous read asynchronous write NOR. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 11 + + st,fmc2-ebi-cs-cclk-enable: + description: Continuous clock enable (first bank must be configured + in synchronous mode). The FMC_CLK is generated continuously + during asynchronous and synchronous access. By default, the + FMC_CLK is only generated during synchronous access. + $ref: /schemas/types.yaml#/definitions/flag + + st,fmc2-ebi-cs-mux-enable: + description: Address/Data multiplexed on databus (valid only with + NOR and PSRAM transactions type). By default, Address/Data + are not multiplexed. + $ref: /schemas/types.yaml#/definitions/flag + + st,fmc2-ebi-cs-buswidth: + description: Data bus width + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 8, 16 ] + default: 16 + + st,fmc2-ebi-cs-waitpol-high: + description: Wait signal polarity (NWAIT signal active high). + By default, NWAIT is active low. + $ref: /schemas/types.yaml#/definitions/flag + + st,fmc2-ebi-cs-waitcfg-enable: + description: The NWAIT signal indicates wheither the data from the + device are valid or if a wait state must be inserted when accessing + the device in synchronous mode. By default, the NWAIT signal is + active one data cycle before wait state. + $ref: /schemas/types.yaml#/definitions/flag + + st,fmc2-ebi-cs-wait-enable: + description: The NWAIT signal is enabled (its level is taken into + account after the programmed latency period to insert wait states + if asserted). By default, the NWAIT signal is disabled. + $ref: /schemas/types.yaml#/definitions/flag + + st,fmc2-ebi-cs-asyncwait-enable: + description: The NWAIT signal is taken into account during asynchronous + transactions. By default, the NWAIT signal is not taken into account + during asynchronous transactions. + $ref: /schemas/types.yaml#/definitions/flag + + st,fmc2-ebi-cs-cpsize: + description: CRAM page size. The controller splits the burst access + when the memory page is reached. By default, no burst split when + crossing page boundary. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 128, 256, 512, 1024 ] + default: 0 + + st,fmc2-ebi-cs-byte-lane-setup-ns: + description: This property configures the byte lane setup timing + defined in nanoseconds from NBLx low to Chip Select NEx low. + + st,fmc2-ebi-cs-address-setup-ns: + description: This property defines the duration of the address setup + phase in nanoseconds used for asynchronous read/write transactions. + + st,fmc2-ebi-cs-address-hold-ns: + description: This property defines the duration of the address hold + phase in nanoseconds used for asynchronous multiplexed read/write + transactions. + + st,fmc2-ebi-cs-data-setup-ns: + description: This property defines the duration of the data setup phase + in nanoseconds used for asynchronous read/write transactions. + + st,fmc2-ebi-cs-bus-turnaround-ns: + description: This property defines the delay in nanoseconds between the + end of current read/write transaction and the next transaction. + + st,fmc2-ebi-cs-data-hold-ns: + description: This property defines the duration of the data hold phase + in nanoseconds used for asynchronous read/write transactions. + + st,fmc2-ebi-cs-clk-period-ns: + description: This property defines the FMC_CLK output signal period in + nanoseconds. + + st,fmc2-ebi-cs-data-latency-ns: + description: This property defines the data latency before reading or + writing the first data in nanoseconds. + + st,fmc2_ebi-cs-write-address-setup-ns: + description: This property defines the duration of the address setup + phase in nanoseconds used for asynchronous write transactions. + + st,fmc2-ebi-cs-write-address-hold-ns: + description: This property defines the duration of the address hold + phase in nanoseconds used for asynchronous multiplexed write + transactions. + + st,fmc2-ebi-cs-write-data-setup-ns: + description: This property defines the duration of the data setup + phase in nanoseconds used for asynchronous write transactions. + + st,fmc2-ebi-cs-write-bus-turnaround-ns: + description: This property defines the delay between the end of current + write transaction and the next transaction in nanoseconds. + + st,fmc2-ebi-cs-write-data-hold-ns: + description: This property defines the duration of the data hold phase + in nanoseconds used for asynchronous write transactions. + + st,fmc2-ebi-cs-max-low-pulse-ns: + description: This property defines the maximum chip select low pulse + duration in nanoseconds for synchronous transactions. When this timing + reaches 0, the controller splits the current access, toggles NE to + allow device refresh and restarts a new access. + + required: + - reg + +required: + - "#address-cells" + - "#size-cells" + - compatible + - reg + - clocks + - ranges + +examples: + - | + #include + #include + #include + memory-controller@58002000 { + #address-cells = <2>; + #size-cells = <1>; + compatible = "st,stm32mp1-fmc2-ebi"; + reg = <0x58002000 0x1000>; + clocks = <&rcc FMC_K>; + resets = <&rcc FMC_R>; + + ranges = <0 0 0x60000000 0x04000000>, /* EBI CS 1 */ + <1 0 0x64000000 0x04000000>, /* EBI CS 2 */ + <2 0 0x68000000 0x04000000>, /* EBI CS 3 */ + <3 0 0x6c000000 0x04000000>, /* EBI CS 4 */ + <4 0 0x80000000 0x10000000>; /* NAND */ + + psram@0,0 { + compatible = "mtd-ram"; + reg = <0 0x00000000 0x100000>; + bank-width = <2>; + + st,fmc2-ebi-cs-transaction-type = <1>; + st,fmc2-ebi-cs-address-setup-ns = <60>; + st,fmc2-ebi-cs-data-setup-ns = <30>; + st,fmc2-ebi-cs-bus-turnaround-ns = <5>; + }; + + nand-controller@4,0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "st,stm32mp1-fmc2-nfc"; + reg = <4 0x00000000 0x1000>, + <4 0x08010000 0x1000>, + <4 0x08020000 0x1000>, + <4 0x01000000 0x1000>, + <4 0x09010000 0x1000>, + <4 0x09020000 0x1000>; + interrupts = ; + dmas = <&mdma1 20 0x2 0x12000a02 0x0 0x0>, + <&mdma1 20 0x2 0x12000a08 0x0 0x0>, + <&mdma1 21 0x2 0x12000a0a 0x0 0x0>; + dma-names = "tx", "rx", "ecc"; + + nand@0 { + reg = <0>; + nand-on-flash-bbt; + #address-cells = <1>; + #size-cells = <1>; + }; + }; + }; + +... From patchwork Fri Jun 12 15:22:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christophe Kerello X-Patchwork-Id: 199145 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 60A14C433E0 for ; Fri, 12 Jun 2020 15:24:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 2306920835 for ; Fri, 12 Jun 2020 15:24:09 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=st.com header.i=@st.com header.b="L0VZQfLP" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726709AbgFLPYI (ORCPT ); Fri, 12 Jun 2020 11:24:08 -0400 Received: from mx08-00178001.pphosted.com ([91.207.212.93]:26468 "EHLO mx07-00178001.pphosted.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726414AbgFLPYH (ORCPT ); Fri, 12 Jun 2020 11:24:07 -0400 Received: from pps.filterd (m0046660.ppops.net [127.0.0.1]) by mx07-00178001.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 05CFNQlc016626; Fri, 12 Jun 2020 17:23:41 +0200 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=st.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-type; s=STMicroelectronics; bh=+L58tj92AnEr887ceku37zQdzATdKW6vBO+H/1yq8fg=; b=L0VZQfLPhA7Aigweo+oUfNQfNwlSLZwnWnv7sOnNtaSAkzg4eUS06jxWFit78eWtGJNX qGZUuDvqFeNhfM0kvaAuuDAZgg5okfojB6q0HQhU+0To8GhfbpHdhU2LHVaU96Ms8InN QGtzKvmWu1Xjm4Txtmq/v3x4f/QxBDIgGE1HLnkKqig4Gn5KdxQCnV12bjT4maWzft4t bzSigLqt7hppASXOi5+wyANCrBRl+mM1Kz9RFV16dQVXEsADq8J4gULeirJmlYtewdw0 /sX8IE+jAXmnr/1SxNWxbXI/l0/3L36IbHTsAT08dcVyHILvJCDS1iU9C7+sX8FdgZvc rQ== Received: from beta.dmz-eu.st.com (beta.dmz-eu.st.com [164.129.1.35]) by mx07-00178001.pphosted.com with ESMTP id 31g097s1m3-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 12 Jun 2020 17:23:41 +0200 Received: from euls16034.sgp.st.com (euls16034.sgp.st.com [10.75.44.20]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id B03CE10002A; Fri, 12 Jun 2020 17:23:40 +0200 (CEST) Received: from Webmail-eu.st.com (sfhdag6node2.st.com [10.75.127.17]) by euls16034.sgp.st.com (STMicroelectronics) with ESMTP id 9EFC62B9918; Fri, 12 Jun 2020 17:23:40 +0200 (CEST) Received: from localhost (10.75.127.50) by SFHDAG6NODE2.st.com (10.75.127.17) with Microsoft SMTP Server (TLS) id 15.0.1347.2; Fri, 12 Jun 2020 17:23:37 +0200 From: Christophe Kerello To: , , , , , , CC: , , , , , Christophe Kerello Subject: [PATCH v5 4/6] memory: stm32-fmc2-ebi: add STM32 FMC2 EBI controller driver Date: Fri, 12 Jun 2020 17:22:40 +0200 Message-ID: <1591975362-22009-5-git-send-email-christophe.kerello@st.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1591975362-22009-1-git-send-email-christophe.kerello@st.com> References: <1591975362-22009-1-git-send-email-christophe.kerello@st.com> MIME-Version: 1.0 X-Originating-IP: [10.75.127.50] X-ClientProxiedBy: SFHDAG2NODE3.st.com (10.75.127.6) To SFHDAG6NODE2.st.com (10.75.127.17) X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.216, 18.0.687 definitions=2020-06-12_11:2020-06-12,2020-06-12 signatures=0 Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org The driver adds the support for the STMicroelectronics FMC2 EBI controller found on STM32MP SOCs. Signed-off-by: Christophe Kerello --- Changes in v3: - Move in memory folder - Merge MFD and BUS drivers to avoid a MFD driver drivers/memory/Kconfig | 10 + drivers/memory/Makefile | 1 + drivers/memory/stm32-fmc2-ebi.c | 1206 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 1217 insertions(+) create mode 100644 drivers/memory/stm32-fmc2-ebi.c diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 9bddca2..c651aaf 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -163,6 +163,16 @@ config PL353_SMC This driver is for the ARM PL351/PL353 Static Memory Controller(SMC) module. +config STM32_FMC2_EBI + tristate "Support for FMC2 External Bus Interface on STM32MP SoCs" + depends on MACH_STM32MP157 || COMPILE_TEST + select MFD_SYSCON + help + Select this option to enable the STM32 FMC2 External Bus Interface + controller. This driver configures the transactions with external + devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on + SOCs containing the FMC2 External Bus Interface. + source "drivers/memory/samsung/Kconfig" source "drivers/memory/tegra/Kconfig" diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index 27b4934..c7d36db 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_JZ4780_NEMC) += jz4780-nemc.o obj-$(CONFIG_MTK_SMI) += mtk-smi.o obj-$(CONFIG_DA8XX_DDRCTL) += da8xx-ddrctl.o obj-$(CONFIG_PL353_SMC) += pl353-smc.o +obj-$(CONFIG_STM32_FMC2_EBI) += stm32-fmc2-ebi.o obj-$(CONFIG_SAMSUNG_MC) += samsung/ obj-$(CONFIG_TEGRA_MC) += tegra/ diff --git a/drivers/memory/stm32-fmc2-ebi.c b/drivers/memory/stm32-fmc2-ebi.c new file mode 100644 index 0000000..4d5758c4 --- /dev/null +++ b/drivers/memory/stm32-fmc2-ebi.c @@ -0,0 +1,1206 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) STMicroelectronics 2020 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* FMC2 Controller Registers */ +#define FMC2_BCR1 0x0 +#define FMC2_BTR1 0x4 +#define FMC2_BCR(x) ((x) * 0x8 + FMC2_BCR1) +#define FMC2_BTR(x) ((x) * 0x8 + FMC2_BTR1) +#define FMC2_PCSCNTR 0x20 +#define FMC2_BWTR1 0x104 +#define FMC2_BWTR(x) ((x) * 0x8 + FMC2_BWTR1) + +/* Register: FMC2_BCR1 */ +#define FMC2_BCR1_CCLKEN BIT(20) +#define FMC2_BCR1_FMC2EN BIT(31) + +/* Register: FMC2_BCRx */ +#define FMC2_BCR_MBKEN BIT(0) +#define FMC2_BCR_MUXEN BIT(1) +#define FMC2_BCR_MTYP GENMASK(3, 2) +#define FMC2_BCR_MWID GENMASK(5, 4) +#define FMC2_BCR_FACCEN BIT(6) +#define FMC2_BCR_BURSTEN BIT(8) +#define FMC2_BCR_WAITPOL BIT(9) +#define FMC2_BCR_WAITCFG BIT(11) +#define FMC2_BCR_WREN BIT(12) +#define FMC2_BCR_WAITEN BIT(13) +#define FMC2_BCR_EXTMOD BIT(14) +#define FMC2_BCR_ASYNCWAIT BIT(15) +#define FMC2_BCR_CPSIZE GENMASK(18, 16) +#define FMC2_BCR_CBURSTRW BIT(19) +#define FMC2_BCR_NBLSET GENMASK(23, 22) + +/* Register: FMC2_BTRx/FMC2_BWTRx */ +#define FMC2_BXTR_ADDSET GENMASK(3, 0) +#define FMC2_BXTR_ADDHLD GENMASK(7, 4) +#define FMC2_BXTR_DATAST GENMASK(15, 8) +#define FMC2_BXTR_BUSTURN GENMASK(19, 16) +#define FMC2_BTR_CLKDIV GENMASK(23, 20) +#define FMC2_BTR_DATLAT GENMASK(27, 24) +#define FMC2_BXTR_ACCMOD GENMASK(29, 28) +#define FMC2_BXTR_DATAHLD GENMASK(31, 30) + +/* Register: FMC2_PCSCNTR */ +#define FMC2_PCSCNTR_CSCOUNT GENMASK(15, 0) +#define FMC2_PCSCNTR_CNTBEN(x) BIT((x) + 16) + +#define FMC2_MAX_EBI_CE 4 +#define FMC2_MAX_BANKS 5 + +#define FMC2_BCR_CPSIZE_0 0x0 +#define FMC2_BCR_CPSIZE_128 0x1 +#define FMC2_BCR_CPSIZE_256 0x2 +#define FMC2_BCR_CPSIZE_512 0x3 +#define FMC2_BCR_CPSIZE_1024 0x4 + +#define FMC2_BCR_MWID_8 0x0 +#define FMC2_BCR_MWID_16 0x1 + +#define FMC2_BCR_MTYP_SRAM 0x0 +#define FMC2_BCR_MTYP_PSRAM 0x1 +#define FMC2_BCR_MTYP_NOR 0x2 + +#define FMC2_BXTR_EXTMOD_A 0x0 +#define FMC2_BXTR_EXTMOD_B 0x1 +#define FMC2_BXTR_EXTMOD_C 0x2 +#define FMC2_BXTR_EXTMOD_D 0x3 + +#define FMC2_BCR_NBLSET_MAX 0x3 +#define FMC2_BXTR_ADDSET_MAX 0xf +#define FMC2_BXTR_ADDHLD_MAX 0xf +#define FMC2_BXTR_DATAST_MAX 0xff +#define FMC2_BXTR_BUSTURN_MAX 0xf +#define FMC2_BXTR_DATAHLD_MAX 0x3 +#define FMC2_BTR_CLKDIV_MAX 0xf +#define FMC2_BTR_DATLAT_MAX 0xf +#define FMC2_PCSCNTR_CSCOUNT_MAX 0xff + +enum stm32_fmc2_ebi_bank { + FMC2_EBI1 = 0, + FMC2_EBI2, + FMC2_EBI3, + FMC2_EBI4, + FMC2_NAND +}; + +enum stm32_fmc2_ebi_register_type { + FMC2_REG_BCR = 1, + FMC2_REG_BTR, + FMC2_REG_BWTR, + FMC2_REG_PCSCNTR +}; + +enum stm32_fmc2_ebi_transaction_type { + FMC2_ASYNC_MODE_1_SRAM = 0, + FMC2_ASYNC_MODE_1_PSRAM, + FMC2_ASYNC_MODE_A_SRAM, + FMC2_ASYNC_MODE_A_PSRAM, + FMC2_ASYNC_MODE_2_NOR, + FMC2_ASYNC_MODE_B_NOR, + FMC2_ASYNC_MODE_C_NOR, + FMC2_ASYNC_MODE_D_NOR, + FMC2_SYNC_READ_SYNC_WRITE_PSRAM, + FMC2_SYNC_READ_ASYNC_WRITE_PSRAM, + FMC2_SYNC_READ_SYNC_WRITE_NOR, + FMC2_SYNC_READ_ASYNC_WRITE_NOR +}; + +enum stm32_fmc2_ebi_buswidth { + FMC2_BUSWIDTH_8 = 8, + FMC2_BUSWIDTH_16 = 16 +}; + +enum stm32_fmc2_ebi_cpsize { + FMC2_CPSIZE_0 = 0, + FMC2_CPSIZE_128 = 128, + FMC2_CPSIZE_256 = 256, + FMC2_CPSIZE_512 = 512, + FMC2_CPSIZE_1024 = 1024 +}; + +struct stm32_fmc2_ebi { + struct device *dev; + struct clk *clk; + struct regmap *regmap; + u8 bank_assigned; + + u32 bcr[FMC2_MAX_EBI_CE]; + u32 btr[FMC2_MAX_EBI_CE]; + u32 bwtr[FMC2_MAX_EBI_CE]; + u32 pcscntr; +}; + +/* + * struct stm32_fmc2_prop - STM32 FMC2 EBI property + * @name: the device tree binding name of the property + * @bprop: indicate that it is a boolean property + * @mprop: indicate that it is a mandatory property + * @reg_type: the register that have to be modified + * @reg_mask: the bit that have to be modified in the selected register + * in case of it is a boolean property + * @reset_val: the default value that have to be set in case the property + * has not been defined in the device tree + * @check: this callback ckecks that the property is compliant with the + * transaction type selected + * @calculate: this callback is called to calculate for exemple a timing + * set in nanoseconds in the device tree in clock cycles or in + * clock period + * @set: this callback applies the values in the registers + */ +struct stm32_fmc2_prop { + const char *name; + bool bprop; + bool mprop; + int reg_type; + u32 reg_mask; + u32 reset_val; + int (*check)(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, int cs); + u32 (*calculate)(struct stm32_fmc2_ebi *ebi, int cs, u32 setup); + int (*set)(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup); +}; + +static int stm32_fmc2_ebi_check_mux(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs) +{ + u32 bcr; + + regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + + if (bcr & FMC2_BCR_MTYP) + return 0; + + return -EINVAL; +} + +static int stm32_fmc2_ebi_check_waitcfg(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs) +{ + u32 bcr, val = FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR); + + regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + + if ((bcr & FMC2_BCR_MTYP) == val && bcr & FMC2_BCR_BURSTEN) + return 0; + + return -EINVAL; +} + +static int stm32_fmc2_ebi_check_sync_trans(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs) +{ + u32 bcr; + + regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + + if (bcr & FMC2_BCR_BURSTEN) + return 0; + + return -EINVAL; +} + +static int stm32_fmc2_ebi_check_async_trans(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs) +{ + u32 bcr; + + regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + + if (!(bcr & FMC2_BCR_BURSTEN) || !(bcr & FMC2_BCR_CBURSTRW)) + return 0; + + return -EINVAL; +} + +static int stm32_fmc2_ebi_check_cpsize(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs) +{ + u32 bcr, val = FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM); + + regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + + if ((bcr & FMC2_BCR_MTYP) == val && bcr & FMC2_BCR_BURSTEN) + return 0; + + return -EINVAL; +} + +static int stm32_fmc2_ebi_check_address_hold(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs) +{ + u32 bcr, bxtr, val = FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D); + + regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + if (prop->reg_type == FMC2_REG_BWTR) + regmap_read(ebi->regmap, FMC2_BWTR(cs), &bxtr); + else + regmap_read(ebi->regmap, FMC2_BTR(cs), &bxtr); + + if ((!(bcr & FMC2_BCR_BURSTEN) || !(bcr & FMC2_BCR_CBURSTRW)) && + ((bxtr & FMC2_BXTR_ACCMOD) == val || bcr & FMC2_BCR_MUXEN)) + return 0; + + return -EINVAL; +} + +static int stm32_fmc2_ebi_check_clk_period(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs) +{ + u32 bcr, bcr1; + + regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + if (cs) + regmap_read(ebi->regmap, FMC2_BCR1, &bcr1); + else + bcr1 = bcr; + + if (bcr & FMC2_BCR_BURSTEN && (!cs || !(bcr1 & FMC2_BCR1_CCLKEN))) + return 0; + + return -EINVAL; +} + +static int stm32_fmc2_ebi_check_cclk(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs) +{ + if (cs) + return -EINVAL; + + return stm32_fmc2_ebi_check_sync_trans(ebi, prop, cs); +} + +static u32 stm32_fmc2_ebi_ns_to_clock_cycles(struct stm32_fmc2_ebi *ebi, + int cs, u32 setup) +{ + unsigned long hclk = clk_get_rate(ebi->clk); + unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000); + + return DIV_ROUND_UP(setup * 1000, hclkp); +} + +static u32 stm32_fmc2_ebi_ns_to_clk_period(struct stm32_fmc2_ebi *ebi, + int cs, u32 setup) +{ + u32 nb_clk_cycles = stm32_fmc2_ebi_ns_to_clock_cycles(ebi, cs, setup); + u32 bcr, btr, clk_period; + + regmap_read(ebi->regmap, FMC2_BCR1, &bcr); + if (bcr & FMC2_BCR1_CCLKEN || !cs) + regmap_read(ebi->regmap, FMC2_BTR1, &btr); + else + regmap_read(ebi->regmap, FMC2_BTR(cs), &btr); + + clk_period = FIELD_GET(FMC2_BTR_CLKDIV, btr) + 1; + + return DIV_ROUND_UP(nb_clk_cycles, clk_period); +} + +static int stm32_fmc2_ebi_get_reg(int reg_type, int cs, u32 *reg) +{ + switch (reg_type) { + case FMC2_REG_BCR: + *reg = FMC2_BCR(cs); + break; + case FMC2_REG_BTR: + *reg = FMC2_BTR(cs); + break; + case FMC2_REG_BWTR: + *reg = FMC2_BWTR(cs); + break; + case FMC2_REG_PCSCNTR: + *reg = FMC2_PCSCNTR; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int stm32_fmc2_ebi_set_bit_field(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup) +{ + u32 reg; + int ret; + + ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, ®); + if (ret) + return ret; + + regmap_update_bits(ebi->regmap, reg, prop->reg_mask, + setup ? prop->reg_mask : 0); + + return 0; +} + +static int stm32_fmc2_ebi_set_trans_type(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup) +{ + u32 bcr_mask, bcr = FMC2_BCR_WREN; + u32 btr_mask, btr = 0; + u32 bwtr_mask, bwtr = 0; + + bwtr_mask = FMC2_BXTR_ACCMOD; + btr_mask = FMC2_BXTR_ACCMOD; + bcr_mask = FMC2_BCR_MUXEN | FMC2_BCR_MTYP | FMC2_BCR_FACCEN | + FMC2_BCR_WREN | FMC2_BCR_WAITEN | FMC2_BCR_BURSTEN | + FMC2_BCR_EXTMOD | FMC2_BCR_CBURSTRW; + + switch (setup) { + case FMC2_ASYNC_MODE_1_SRAM: + bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_SRAM); + /* + * MUXEN = 0, MTYP = 0, FACCEN = 0, BURSTEN = 0, WAITEN = 0, + * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0 + */ + break; + case FMC2_ASYNC_MODE_1_PSRAM: + /* + * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 0, WAITEN = 0, + * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0 + */ + bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM); + break; + case FMC2_ASYNC_MODE_A_SRAM: + /* + * MUXEN = 0, MTYP = 0, FACCEN = 0, BURSTEN = 0, WAITEN = 0, + * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 0 + */ + bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_SRAM); + bcr |= FMC2_BCR_EXTMOD; + btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A); + bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A); + break; + case FMC2_ASYNC_MODE_A_PSRAM: + /* + * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 0, WAITEN = 0, + * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 0 + */ + bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM); + bcr |= FMC2_BCR_EXTMOD; + btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A); + bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A); + break; + case FMC2_ASYNC_MODE_2_NOR: + /* + * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0, + * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0 + */ + bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR); + bcr |= FMC2_BCR_FACCEN; + break; + case FMC2_ASYNC_MODE_B_NOR: + /* + * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0, + * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 1 + */ + bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR); + bcr |= FMC2_BCR_FACCEN | FMC2_BCR_EXTMOD; + btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_B); + bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_B); + break; + case FMC2_ASYNC_MODE_C_NOR: + /* + * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0, + * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 2 + */ + bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR); + bcr |= FMC2_BCR_FACCEN | FMC2_BCR_EXTMOD; + btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_C); + bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_C); + break; + case FMC2_ASYNC_MODE_D_NOR: + /* + * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0, + * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 3 + */ + bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR); + bcr |= FMC2_BCR_FACCEN | FMC2_BCR_EXTMOD; + btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D); + bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D); + break; + case FMC2_SYNC_READ_SYNC_WRITE_PSRAM: + /* + * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 1, WAITEN = 0, + * WREN = 1, EXTMOD = 0, CBURSTRW = 1, ACCMOD = 0 + */ + bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM); + bcr |= FMC2_BCR_BURSTEN | FMC2_BCR_CBURSTRW; + break; + case FMC2_SYNC_READ_ASYNC_WRITE_PSRAM: + /* + * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 1, WAITEN = 0, + * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0 + */ + bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM); + bcr |= FMC2_BCR_BURSTEN; + break; + case FMC2_SYNC_READ_SYNC_WRITE_NOR: + /* + * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 1, WAITEN = 0, + * WREN = 1, EXTMOD = 0, CBURSTRW = 1, ACCMOD = 0 + */ + bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR); + bcr |= FMC2_BCR_FACCEN | FMC2_BCR_BURSTEN | FMC2_BCR_CBURSTRW; + break; + case FMC2_SYNC_READ_ASYNC_WRITE_NOR: + /* + * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 1, WAITEN = 0, + * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0 + */ + bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR); + bcr |= FMC2_BCR_FACCEN | FMC2_BCR_BURSTEN; + break; + default: + /* Type of transaction not supported */ + return -EINVAL; + } + + if (bcr & FMC2_BCR_EXTMOD) + regmap_update_bits(ebi->regmap, FMC2_BWTR(cs), + bwtr_mask, bwtr); + regmap_update_bits(ebi->regmap, FMC2_BTR(cs), btr_mask, btr); + regmap_update_bits(ebi->regmap, FMC2_BCR(cs), bcr_mask, bcr); + + return 0; +} + +static int stm32_fmc2_ebi_set_buswidth(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup) +{ + u32 val; + + switch (setup) { + case FMC2_BUSWIDTH_8: + val = FIELD_PREP(FMC2_BCR_MWID, FMC2_BCR_MWID_8); + break; + case FMC2_BUSWIDTH_16: + val = FIELD_PREP(FMC2_BCR_MWID, FMC2_BCR_MWID_16); + break; + default: + /* Buswidth not supported */ + return -EINVAL; + } + + regmap_update_bits(ebi->regmap, FMC2_BCR(cs), FMC2_BCR_MWID, val); + + return 0; +} + +static int stm32_fmc2_ebi_set_cpsize(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup) +{ + u32 val; + + switch (setup) { + case FMC2_CPSIZE_0: + val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_0); + break; + case FMC2_CPSIZE_128: + val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_128); + break; + case FMC2_CPSIZE_256: + val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_256); + break; + case FMC2_CPSIZE_512: + val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_512); + break; + case FMC2_CPSIZE_1024: + val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_1024); + break; + default: + /* Cpsize not supported */ + return -EINVAL; + } + + regmap_update_bits(ebi->regmap, FMC2_BCR(cs), FMC2_BCR_CPSIZE, val); + + return 0; +} + +static int stm32_fmc2_ebi_set_bl_setup(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup) +{ + u32 val; + + val = min_t(u32, setup, FMC2_BCR_NBLSET_MAX); + val = FIELD_PREP(FMC2_BCR_NBLSET, val); + regmap_update_bits(ebi->regmap, FMC2_BCR(cs), FMC2_BCR_NBLSET, val); + + return 0; +} + +static int stm32_fmc2_ebi_set_address_setup(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup) +{ + u32 bcr, bxtr, reg; + u32 val = FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D); + int ret; + + ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, ®); + if (ret) + return ret; + + regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + if (prop->reg_type == FMC2_REG_BWTR) + regmap_read(ebi->regmap, FMC2_BWTR(cs), &bxtr); + else + regmap_read(ebi->regmap, FMC2_BTR(cs), &bxtr); + + if ((bxtr & FMC2_BXTR_ACCMOD) == val || bcr & FMC2_BCR_MUXEN) + val = clamp_val(setup, 1, FMC2_BXTR_ADDSET_MAX); + else + val = min_t(u32, setup, FMC2_BXTR_ADDSET_MAX); + val = FIELD_PREP(FMC2_BXTR_ADDSET, val); + regmap_update_bits(ebi->regmap, reg, FMC2_BXTR_ADDSET, val); + + return 0; +} + +static int stm32_fmc2_ebi_set_address_hold(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup) +{ + u32 val, reg; + int ret; + + ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, ®); + if (ret) + return ret; + + val = clamp_val(setup, 1, FMC2_BXTR_ADDHLD_MAX); + val = FIELD_PREP(FMC2_BXTR_ADDHLD, val); + regmap_update_bits(ebi->regmap, reg, FMC2_BXTR_ADDHLD, val); + + return 0; +} + +static int stm32_fmc2_ebi_set_data_setup(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup) +{ + u32 val, reg; + int ret; + + ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, ®); + if (ret) + return ret; + + val = clamp_val(setup, 1, FMC2_BXTR_DATAST_MAX); + val = FIELD_PREP(FMC2_BXTR_DATAST, val); + regmap_update_bits(ebi->regmap, reg, FMC2_BXTR_DATAST, val); + + return 0; +} + +static int stm32_fmc2_ebi_set_bus_turnaround(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup) +{ + u32 val, reg; + int ret; + + ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, ®); + if (ret) + return ret; + + val = setup ? min_t(u32, setup - 1, FMC2_BXTR_BUSTURN_MAX) : 0; + val = FIELD_PREP(FMC2_BXTR_BUSTURN, val); + regmap_update_bits(ebi->regmap, reg, FMC2_BXTR_BUSTURN, val); + + return 0; +} + +static int stm32_fmc2_ebi_set_data_hold(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup) +{ + u32 val, reg; + int ret; + + ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, ®); + if (ret) + return ret; + + if (prop->reg_type == FMC2_REG_BWTR) + val = setup ? min_t(u32, setup - 1, FMC2_BXTR_DATAHLD_MAX) : 0; + else + val = min_t(u32, setup, FMC2_BXTR_DATAHLD_MAX); + val = FIELD_PREP(FMC2_BXTR_DATAHLD, val); + regmap_update_bits(ebi->regmap, reg, FMC2_BXTR_DATAHLD, val); + + return 0; +} + +static int stm32_fmc2_ebi_set_clk_period(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup) +{ + u32 val; + + val = setup ? clamp_val(setup - 1, 1, FMC2_BTR_CLKDIV_MAX) : 1; + val = FIELD_PREP(FMC2_BTR_CLKDIV, val); + regmap_update_bits(ebi->regmap, FMC2_BTR(cs), FMC2_BTR_CLKDIV, val); + + return 0; +} + +static int stm32_fmc2_ebi_set_data_latency(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup) +{ + u32 val; + + val = setup > 1 ? min_t(u32, setup - 2, FMC2_BTR_DATLAT_MAX) : 0; + val = FIELD_PREP(FMC2_BTR_DATLAT, val); + regmap_update_bits(ebi->regmap, FMC2_BTR(cs), FMC2_BTR_DATLAT, val); + + return 0; +} + +static int stm32_fmc2_ebi_set_max_low_pulse(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup) +{ + u32 old_val, new_val, pcscntr; + + if (setup < 1) + return 0; + + regmap_read(ebi->regmap, FMC2_PCSCNTR, &pcscntr); + + /* Enable counter for the bank */ + regmap_update_bits(ebi->regmap, FMC2_PCSCNTR, + FMC2_PCSCNTR_CNTBEN(cs), + FMC2_PCSCNTR_CNTBEN(cs)); + + new_val = min_t(u32, setup - 1, FMC2_PCSCNTR_CSCOUNT_MAX); + old_val = FIELD_GET(FMC2_PCSCNTR_CSCOUNT, pcscntr); + if (old_val && new_val > old_val) + /* Keep current counter value */ + return 0; + + new_val = FIELD_PREP(FMC2_PCSCNTR_CSCOUNT, new_val); + regmap_update_bits(ebi->regmap, FMC2_PCSCNTR, + FMC2_PCSCNTR_CSCOUNT, new_val); + + return 0; +} + +static const struct stm32_fmc2_prop stm32_fmc2_child_props[] = { + /* st,fmc2-ebi-cs-trans-type must be the first property */ + { + .name = "st,fmc2-ebi-cs-transaction-type", + .mprop = true, + .set = stm32_fmc2_ebi_set_trans_type, + }, + { + .name = "st,fmc2-ebi-cs-cclk-enable", + .bprop = true, + .reg_type = FMC2_REG_BCR, + .reg_mask = FMC2_BCR1_CCLKEN, + .check = stm32_fmc2_ebi_check_cclk, + .set = stm32_fmc2_ebi_set_bit_field, + }, + { + .name = "st,fmc2-ebi-cs-mux-enable", + .bprop = true, + .reg_type = FMC2_REG_BCR, + .reg_mask = FMC2_BCR_MUXEN, + .check = stm32_fmc2_ebi_check_mux, + .set = stm32_fmc2_ebi_set_bit_field, + }, + { + .name = "st,fmc2-ebi-cs-buswidth", + .reset_val = FMC2_BUSWIDTH_16, + .set = stm32_fmc2_ebi_set_buswidth, + }, + { + .name = "st,fmc2-ebi-cs-waitpol-high", + .bprop = true, + .reg_type = FMC2_REG_BCR, + .reg_mask = FMC2_BCR_WAITPOL, + .set = stm32_fmc2_ebi_set_bit_field, + }, + { + .name = "st,fmc2-ebi-cs-waitcfg-enable", + .bprop = true, + .reg_type = FMC2_REG_BCR, + .reg_mask = FMC2_BCR_WAITCFG, + .check = stm32_fmc2_ebi_check_waitcfg, + .set = stm32_fmc2_ebi_set_bit_field, + }, + { + .name = "st,fmc2-ebi-cs-wait-enable", + .bprop = true, + .reg_type = FMC2_REG_BCR, + .reg_mask = FMC2_BCR_WAITEN, + .check = stm32_fmc2_ebi_check_sync_trans, + .set = stm32_fmc2_ebi_set_bit_field, + }, + { + .name = "st,fmc2-ebi-cs-asyncwait-enable", + .bprop = true, + .reg_type = FMC2_REG_BCR, + .reg_mask = FMC2_BCR_ASYNCWAIT, + .check = stm32_fmc2_ebi_check_async_trans, + .set = stm32_fmc2_ebi_set_bit_field, + }, + { + .name = "st,fmc2-ebi-cs-cpsize", + .check = stm32_fmc2_ebi_check_cpsize, + .set = stm32_fmc2_ebi_set_cpsize, + }, + { + .name = "st,fmc2-ebi-cs-byte-lane-setup-ns", + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_bl_setup, + }, + { + .name = "st,fmc2-ebi-cs-address-setup-ns", + .reg_type = FMC2_REG_BTR, + .reset_val = FMC2_BXTR_ADDSET_MAX, + .check = stm32_fmc2_ebi_check_async_trans, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_address_setup, + }, + { + .name = "st,fmc2-ebi-cs-address-hold-ns", + .reg_type = FMC2_REG_BTR, + .reset_val = FMC2_BXTR_ADDHLD_MAX, + .check = stm32_fmc2_ebi_check_address_hold, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_address_hold, + }, + { + .name = "st,fmc2-ebi-cs-data-setup-ns", + .reg_type = FMC2_REG_BTR, + .reset_val = FMC2_BXTR_DATAST_MAX, + .check = stm32_fmc2_ebi_check_async_trans, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_data_setup, + }, + { + .name = "st,fmc2-ebi-cs-bus-turnaround-ns", + .reg_type = FMC2_REG_BTR, + .reset_val = FMC2_BXTR_BUSTURN_MAX + 1, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_bus_turnaround, + }, + { + .name = "st,fmc2-ebi-cs-data-hold-ns", + .reg_type = FMC2_REG_BTR, + .check = stm32_fmc2_ebi_check_async_trans, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_data_hold, + }, + { + .name = "st,fmc2-ebi-cs-clk-period-ns", + .reset_val = FMC2_BTR_CLKDIV_MAX + 1, + .check = stm32_fmc2_ebi_check_clk_period, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_clk_period, + }, + { + .name = "st,fmc2-ebi-cs-data-latency-ns", + .check = stm32_fmc2_ebi_check_sync_trans, + .calculate = stm32_fmc2_ebi_ns_to_clk_period, + .set = stm32_fmc2_ebi_set_data_latency, + }, + { + .name = "st,fmc2-ebi-cs-write-address-setup-ns", + .reg_type = FMC2_REG_BWTR, + .reset_val = FMC2_BXTR_ADDSET_MAX, + .check = stm32_fmc2_ebi_check_async_trans, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_address_setup, + }, + { + .name = "st,fmc2-ebi-cs-write-address-hold-ns", + .reg_type = FMC2_REG_BWTR, + .reset_val = FMC2_BXTR_ADDHLD_MAX, + .check = stm32_fmc2_ebi_check_address_hold, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_address_hold, + }, + { + .name = "st,fmc2-ebi-cs-write-data-setup-ns", + .reg_type = FMC2_REG_BWTR, + .reset_val = FMC2_BXTR_DATAST_MAX, + .check = stm32_fmc2_ebi_check_async_trans, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_data_setup, + }, + { + .name = "st,fmc2-ebi-cs-write-bus-turnaround-ns", + .reg_type = FMC2_REG_BWTR, + .reset_val = FMC2_BXTR_BUSTURN_MAX + 1, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_bus_turnaround, + }, + { + .name = "st,fmc2-ebi-cs-write-data-hold-ns", + .reg_type = FMC2_REG_BWTR, + .check = stm32_fmc2_ebi_check_async_trans, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_data_hold, + }, + { + .name = "st,fmc2-ebi-cs-max-low-pulse-ns", + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_max_low_pulse, + }, +}; + +static int stm32_fmc2_ebi_parse_prop(struct stm32_fmc2_ebi *ebi, + struct device_node *dev_node, + const struct stm32_fmc2_prop *prop, + int cs) +{ + struct device *dev = ebi->dev; + u32 setup = 0; + + if (!prop->set) { + dev_err(dev, "property %s is not well defined\n", prop->name); + return -EINVAL; + } + + if (prop->check && prop->check(ebi, prop, cs)) + /* Skeep this property */ + return 0; + + if (prop->bprop) { + bool bprop; + + bprop = of_property_read_bool(dev_node, prop->name); + if (prop->mprop && !bprop) { + dev_err(dev, "mandatory property %s not defined in the device tree\n", + prop->name); + return -EINVAL; + } + + if (bprop) + setup = 1; + } else { + u32 val; + int ret; + + ret = of_property_read_u32(dev_node, prop->name, &val); + if (prop->mprop && ret) { + dev_err(dev, "mandatory property %s not defined in the device tree\n", + prop->name); + return ret; + } + + if (ret) + setup = prop->reset_val; + else if (prop->calculate) + setup = prop->calculate(ebi, cs, val); + else + setup = val; + } + + return prop->set(ebi, prop, cs, setup); +} + +static void stm32_fmc2_ebi_enable_bank(struct stm32_fmc2_ebi *ebi, int cs) +{ + regmap_update_bits(ebi->regmap, FMC2_BCR(cs), + FMC2_BCR_MBKEN, FMC2_BCR_MBKEN); +} + +static void stm32_fmc2_ebi_disable_bank(struct stm32_fmc2_ebi *ebi, int cs) +{ + regmap_update_bits(ebi->regmap, FMC2_BCR(cs), FMC2_BCR_MBKEN, 0); +} + +static void stm32_fmc2_ebi_save_setup(struct stm32_fmc2_ebi *ebi) +{ + unsigned int cs; + + for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) { + regmap_read(ebi->regmap, FMC2_BCR(cs), &ebi->bcr[cs]); + regmap_read(ebi->regmap, FMC2_BTR(cs), &ebi->btr[cs]); + regmap_read(ebi->regmap, FMC2_BWTR(cs), &ebi->bwtr[cs]); + } + + regmap_read(ebi->regmap, FMC2_PCSCNTR, &ebi->pcscntr); +} + +static void stm32_fmc2_ebi_set_setup(struct stm32_fmc2_ebi *ebi) +{ + unsigned int cs; + + for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) { + regmap_write(ebi->regmap, FMC2_BCR(cs), ebi->bcr[cs]); + regmap_write(ebi->regmap, FMC2_BTR(cs), ebi->btr[cs]); + regmap_write(ebi->regmap, FMC2_BWTR(cs), ebi->bwtr[cs]); + } + + regmap_write(ebi->regmap, FMC2_PCSCNTR, ebi->pcscntr); +} + +static void stm32_fmc2_ebi_disable_banks(struct stm32_fmc2_ebi *ebi) +{ + unsigned int cs; + + for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) { + if (!(ebi->bank_assigned & BIT(cs))) + continue; + + stm32_fmc2_ebi_disable_bank(ebi, cs); + } +} + +/* NWAIT signal can not be connected to EBI controller and NAND controller */ +static bool stm32_fmc2_ebi_nwait_used_by_ctrls(struct stm32_fmc2_ebi *ebi) +{ + unsigned int cs; + u32 bcr; + + for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) { + if (!(ebi->bank_assigned & BIT(cs))) + continue; + + regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + if ((bcr & FMC2_BCR_WAITEN || bcr & FMC2_BCR_ASYNCWAIT) && + ebi->bank_assigned & BIT(FMC2_NAND)) + return true; + } + + return false; +} + +static void stm32_fmc2_ebi_enable(struct stm32_fmc2_ebi *ebi) +{ + regmap_update_bits(ebi->regmap, FMC2_BCR1, + FMC2_BCR1_FMC2EN, FMC2_BCR1_FMC2EN); +} + +static void stm32_fmc2_ebi_disable(struct stm32_fmc2_ebi *ebi) +{ + regmap_update_bits(ebi->regmap, FMC2_BCR1, FMC2_BCR1_FMC2EN, 0); +} + +static int stm32_fmc2_ebi_setup_cs(struct stm32_fmc2_ebi *ebi, + struct device_node *dev_node, + u32 cs) +{ + unsigned int i; + int ret; + + stm32_fmc2_ebi_disable_bank(ebi, cs); + + for (i = 0; i < ARRAY_SIZE(stm32_fmc2_child_props); i++) { + const struct stm32_fmc2_prop *p = &stm32_fmc2_child_props[i]; + + ret = stm32_fmc2_ebi_parse_prop(ebi, dev_node, p, cs); + if (ret) { + dev_err(ebi->dev, "property %s could not be set: %d\n", + p->name, ret); + return ret; + } + } + + stm32_fmc2_ebi_enable_bank(ebi, cs); + + return 0; +} + +static int stm32_fmc2_ebi_parse_dt(struct stm32_fmc2_ebi *ebi) +{ + struct device *dev = ebi->dev; + struct device_node *child; + bool child_found = false; + u32 bank; + int ret; + + for_each_available_child_of_node(dev->of_node, child) { + ret = of_property_read_u32(child, "reg", &bank); + if (ret) { + dev_err(dev, "could not retrieve reg property: %d\n", + ret); + return ret; + } + + if (bank >= FMC2_MAX_BANKS) { + dev_err(dev, "invalid reg value: %d\n", bank); + return -EINVAL; + } + + if (ebi->bank_assigned & BIT(bank)) { + dev_err(dev, "bank already assigned: %d\n", bank); + return -EINVAL; + } + + if (bank < FMC2_MAX_EBI_CE) { + ret = stm32_fmc2_ebi_setup_cs(ebi, child, bank); + if (ret) { + dev_err(dev, "setup chip select %d failed: %d\n", + bank, ret); + return ret; + } + } + + ebi->bank_assigned |= BIT(bank); + child_found = true; + } + + if (!child_found) { + dev_warn(dev, "no subnodes found, disable the driver.\n"); + return -ENODEV; + } + + if (stm32_fmc2_ebi_nwait_used_by_ctrls(ebi)) { + dev_err(dev, "NWAIT signal connected to EBI and NAND controllers\n"); + return -EINVAL; + } + + stm32_fmc2_ebi_enable(ebi); + + return of_platform_populate(dev->of_node, NULL, NULL, dev); +} + +static int stm32_fmc2_ebi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct stm32_fmc2_ebi *ebi; + struct reset_control *rstc; + int ret; + + ebi = devm_kzalloc(&pdev->dev, sizeof(*ebi), GFP_KERNEL); + if (!ebi) + return -ENOMEM; + + ebi->dev = dev; + + ebi->regmap = device_node_to_regmap(dev->of_node); + if (IS_ERR(ebi->regmap)) + return PTR_ERR(ebi->regmap); + + ebi->clk = devm_clk_get(dev, NULL); + if (IS_ERR(ebi->clk)) + return PTR_ERR(ebi->clk); + + rstc = devm_reset_control_get(dev, NULL); + if (PTR_ERR(rstc) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + ret = clk_prepare_enable(ebi->clk); + if (ret) + return ret; + + if (!IS_ERR(rstc)) { + reset_control_assert(rstc); + reset_control_deassert(rstc); + } + + ret = stm32_fmc2_ebi_parse_dt(ebi); + if (ret) + goto err_release; + + stm32_fmc2_ebi_save_setup(ebi); + platform_set_drvdata(pdev, ebi); + + return 0; + +err_release: + stm32_fmc2_ebi_disable_banks(ebi); + stm32_fmc2_ebi_disable(ebi); + clk_disable_unprepare(ebi->clk); + + return ret; +} + +static int stm32_fmc2_ebi_remove(struct platform_device *pdev) +{ + struct stm32_fmc2_ebi *ebi = platform_get_drvdata(pdev); + + of_platform_depopulate(&pdev->dev); + stm32_fmc2_ebi_disable_banks(ebi); + stm32_fmc2_ebi_disable(ebi); + clk_disable_unprepare(ebi->clk); + + return 0; +} + +static int __maybe_unused stm32_fmc2_ebi_suspend(struct device *dev) +{ + struct stm32_fmc2_ebi *ebi = dev_get_drvdata(dev); + + stm32_fmc2_ebi_disable(ebi); + clk_disable_unprepare(ebi->clk); + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static int __maybe_unused stm32_fmc2_ebi_resume(struct device *dev) +{ + struct stm32_fmc2_ebi *ebi = dev_get_drvdata(dev); + int ret; + + pinctrl_pm_select_default_state(dev); + + ret = clk_prepare_enable(ebi->clk); + if (ret) + return ret; + + stm32_fmc2_ebi_set_setup(ebi); + stm32_fmc2_ebi_enable(ebi); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(stm32_fmc2_ebi_pm_ops, stm32_fmc2_ebi_suspend, + stm32_fmc2_ebi_resume); + +static const struct of_device_id stm32_fmc2_ebi_match[] = { + {.compatible = "st,stm32mp1-fmc2-ebi"}, + {} +}; +MODULE_DEVICE_TABLE(of, stm32_fmc2_ebi_match); + +static struct platform_driver stm32_fmc2_ebi_driver = { + .probe = stm32_fmc2_ebi_probe, + .remove = stm32_fmc2_ebi_remove, + .driver = { + .name = "stm32_fmc2_ebi", + .of_match_table = stm32_fmc2_ebi_match, + .pm = &stm32_fmc2_ebi_pm_ops, + }, +}; +module_platform_driver(stm32_fmc2_ebi_driver); + +MODULE_ALIAS("platform:stm32_fmc2_ebi"); +MODULE_AUTHOR("Christophe Kerello "); +MODULE_DESCRIPTION("STMicroelectronics STM32 FMC2 ebi driver"); +MODULE_LICENSE("GPL v2"); From patchwork Fri Jun 12 15:22:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christophe Kerello X-Patchwork-Id: 199144 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BD650C433E1 for ; Fri, 12 Jun 2020 15:24:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 845EE208A7 for ; Fri, 12 Jun 2020 15:24:19 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=st.com header.i=@st.com header.b="ZUz9Gmn7" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726750AbgFLPYS (ORCPT ); Fri, 12 Jun 2020 11:24:18 -0400 Received: from mx07-00178001.pphosted.com ([62.209.51.94]:9820 "EHLO mx07-00178001.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726527AbgFLPYI (ORCPT ); Fri, 12 Jun 2020 11:24:08 -0400 Received: from pps.filterd (m0046668.ppops.net [127.0.0.1]) by mx07-00178001.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 05CFNRn4024867; Fri, 12 Jun 2020 17:23:46 +0200 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=st.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-type; s=STMicroelectronics; bh=o8HiYtOJSCyz/54lDaHclDmaZWp3D/F/raUa+1QicDM=; b=ZUz9Gmn79TbIZdRkVBZ12plBB7jBeJ0o41yCZXWvMl46Dlewk1L/zdkNPM1Zb5sUvKXy 6iQlZBGG8Jma9cfbRyFkmVNbUXtaXDNUxUOExgBGQS0GUMEvVCWtDqUZ73OKtxT38LJR q1vb2iAJheWSAY24NhJuNxmUwBvmDCFng18GXGveGnAYXB3s6y4aWrtaxCFxAROMxgRx dTXmJP97ZakFbFNmXAio+IvLKcWCSN2xnL0DPn1VAoRr9kvZcIoPjE5HogTC8Y6HrFwD cI58uJjyM6Pl1SCX5pvdRZUMntKv3uIlqDZZuQOhWnUpsNJmTA6Z1lq35CDDY98OpPf7 +w== Received: from beta.dmz-eu.st.com (beta.dmz-eu.st.com [164.129.1.35]) by mx07-00178001.pphosted.com with ESMTP id 31jppp6g2t-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 12 Jun 2020 17:23:46 +0200 Received: from euls16034.sgp.st.com (euls16034.sgp.st.com [10.75.44.20]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id D298A10002A; Fri, 12 Jun 2020 17:23:45 +0200 (CEST) Received: from Webmail-eu.st.com (sfhdag6node2.st.com [10.75.127.17]) by euls16034.sgp.st.com (STMicroelectronics) with ESMTP id C316C2B9918; Fri, 12 Jun 2020 17:23:45 +0200 (CEST) Received: from localhost (10.75.127.50) by SFHDAG6NODE2.st.com (10.75.127.17) with Microsoft SMTP Server (TLS) id 15.0.1347.2; Fri, 12 Jun 2020 17:23:44 +0200 From: Christophe Kerello To: , , , , , , CC: , , , , , Christophe Kerello Subject: [PATCH v5 5/6] mtd: rawnand: stm32_fmc2: use regmap APIs Date: Fri, 12 Jun 2020 17:22:41 +0200 Message-ID: <1591975362-22009-6-git-send-email-christophe.kerello@st.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1591975362-22009-1-git-send-email-christophe.kerello@st.com> References: <1591975362-22009-1-git-send-email-christophe.kerello@st.com> MIME-Version: 1.0 X-Originating-IP: [10.75.127.50] X-ClientProxiedBy: SFHDAG7NODE2.st.com (10.75.127.20) To SFHDAG6NODE2.st.com (10.75.127.17) X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.216, 18.0.687 definitions=2020-06-12_11:2020-06-12,2020-06-12 signatures=0 Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org This patch uses regmap APIs to access all FMC2 registers. Signed-off-by: Christophe Kerello Reviewed-by: Miquel Raynal --- Changes in v3: - add Miquel reviewed-by tag drivers/mtd/nand/raw/Kconfig | 2 + drivers/mtd/nand/raw/stm32_fmc2_nand.c | 268 +++++++++++++++------------------ 2 files changed, 127 insertions(+), 143 deletions(-) diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 113f610..0a03ebf 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -415,6 +415,8 @@ config MTD_NAND_TEGRA config MTD_NAND_STM32_FMC2 tristate "Support for NAND controller on STM32MP SoCs" depends on MACH_STM32MP157 || COMPILE_TEST + select REGMAP + select REGMAP_MMIO help Enables support for NAND Flash chips on SoCs containing the FMC2 NAND controller. This controller is found on STM32MP SoCs. diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c index e7b706b..6aa3695 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -15,6 +15,7 @@ #include #include #include +#include #include /* Bad block marker length */ @@ -203,6 +204,16 @@ #define FMC2_BCHDSR4_EBP7 GENMASK(12, 0) #define FMC2_BCHDSR4_EBP8 GENMASK(28, 16) +/* Regmap registers configuration */ +#define FMC2_MAX_REGISTER 0x3fc + +static const struct regmap_config stm32_fmc2_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = sizeof(u32), + .max_register = FMC2_MAX_REGISTER, +}; + enum stm32_fmc2_ecc { FMC2_ECC_HAM = 1, FMC2_ECC_BCH4 = 4, @@ -242,7 +253,7 @@ struct stm32_fmc2_nfc { struct nand_controller base; struct stm32_fmc2_nand nand; struct device *dev; - void __iomem *io_base; + struct regmap *regmap; void __iomem *data_base[FMC2_MAX_CE]; void __iomem *cmd_base[FMC2_MAX_CE]; void __iomem *addr_base[FMC2_MAX_CE]; @@ -277,40 +288,37 @@ static void stm32_fmc2_nfc_timings_init(struct nand_chip *chip) struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); struct stm32_fmc2_nand *nand = to_fmc2_nand(chip); struct stm32_fmc2_timings *timings = &nand->timings; - u32 pcr = readl_relaxed(nfc->io_base + FMC2_PCR); u32 pmem, patt; /* Set tclr/tar timings */ - pcr &= ~FMC2_PCR_TCLR; - pcr |= FIELD_PREP(FMC2_PCR_TCLR, timings->tclr); - pcr &= ~FMC2_PCR_TAR; - pcr |= FIELD_PREP(FMC2_PCR_TAR, timings->tar); + regmap_update_bits(nfc->regmap, FMC2_PCR, + FMC2_PCR_TCLR | FMC2_PCR_TAR, + FIELD_PREP(FMC2_PCR_TCLR, timings->tclr) | + FIELD_PREP(FMC2_PCR_TAR, timings->tar)); /* Set tset/twait/thold/thiz timings in common bank */ pmem = FIELD_PREP(FMC2_PMEM_MEMSET, timings->tset_mem); pmem |= FIELD_PREP(FMC2_PMEM_MEMWAIT, timings->twait); pmem |= FIELD_PREP(FMC2_PMEM_MEMHOLD, timings->thold_mem); pmem |= FIELD_PREP(FMC2_PMEM_MEMHIZ, timings->thiz); + regmap_write(nfc->regmap, FMC2_PMEM, pmem); /* Set tset/twait/thold/thiz timings in attribut bank */ patt = FIELD_PREP(FMC2_PATT_ATTSET, timings->tset_att); patt |= FIELD_PREP(FMC2_PATT_ATTWAIT, timings->twait); patt |= FIELD_PREP(FMC2_PATT_ATTHOLD, timings->thold_att); patt |= FIELD_PREP(FMC2_PATT_ATTHIZ, timings->thiz); - - writel_relaxed(pcr, nfc->io_base + FMC2_PCR); - writel_relaxed(pmem, nfc->io_base + FMC2_PMEM); - writel_relaxed(patt, nfc->io_base + FMC2_PATT); + regmap_write(nfc->regmap, FMC2_PATT, patt); } static void stm32_fmc2_nfc_setup(struct nand_chip *chip) { struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); - u32 pcr = readl_relaxed(nfc->io_base + FMC2_PCR); + u32 pcr = 0, pcr_mask; /* Configure ECC algorithm (default configuration is Hamming) */ - pcr &= ~FMC2_PCR_ECCALG; - pcr &= ~FMC2_PCR_BCHECC; + pcr_mask = FMC2_PCR_ECCALG; + pcr_mask |= FMC2_PCR_BCHECC; if (chip->ecc.strength == FMC2_ECC_BCH8) { pcr |= FMC2_PCR_ECCALG; pcr |= FMC2_PCR_BCHECC; @@ -319,15 +327,15 @@ static void stm32_fmc2_nfc_setup(struct nand_chip *chip) } /* Set buswidth */ - pcr &= ~FMC2_PCR_PWID; + pcr_mask |= FMC2_PCR_PWID; if (chip->options & NAND_BUSWIDTH_16) pcr |= FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_16); /* Set ECC sector size */ - pcr &= ~FMC2_PCR_ECCSS; + pcr_mask |= FMC2_PCR_ECCSS; pcr |= FIELD_PREP(FMC2_PCR_ECCSS, FMC2_PCR_ECCSS_512); - writel_relaxed(pcr, nfc->io_base + FMC2_PCR); + regmap_update_bits(nfc->regmap, FMC2_PCR, pcr_mask, pcr); } static int stm32_fmc2_nfc_select_chip(struct nand_chip *chip, int chipnr) @@ -393,81 +401,63 @@ static int stm32_fmc2_nfc_select_chip(struct nand_chip *chip, int chipnr) static void stm32_fmc2_nfc_set_buswidth_16(struct stm32_fmc2_nfc *nfc, bool set) { - u32 pcr = readl_relaxed(nfc->io_base + FMC2_PCR); + u32 pcr; - pcr &= ~FMC2_PCR_PWID; - if (set) - pcr |= FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_16); - writel_relaxed(pcr, nfc->io_base + FMC2_PCR); + pcr = set ? FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_16) : + FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_8); + + regmap_update_bits(nfc->regmap, FMC2_PCR, FMC2_PCR_PWID, pcr); } static void stm32_fmc2_nfc_set_ecc(struct stm32_fmc2_nfc *nfc, bool enable) { - u32 pcr = readl(nfc->io_base + FMC2_PCR); - - pcr &= ~FMC2_PCR_ECCEN; - if (enable) - pcr |= FMC2_PCR_ECCEN; - writel(pcr, nfc->io_base + FMC2_PCR); + regmap_update_bits(nfc->regmap, FMC2_PCR, FMC2_PCR_ECCEN, + enable ? FMC2_PCR_ECCEN : 0); } -static inline void stm32_fmc2_nfc_enable_seq_irq(struct stm32_fmc2_nfc *nfc) +static void stm32_fmc2_nfc_enable_seq_irq(struct stm32_fmc2_nfc *nfc) { - u32 csqier = readl_relaxed(nfc->io_base + FMC2_CSQIER); - - csqier |= FMC2_CSQIER_TCIE; - nfc->irq_state = FMC2_IRQ_SEQ; - writel_relaxed(csqier, nfc->io_base + FMC2_CSQIER); + regmap_update_bits(nfc->regmap, FMC2_CSQIER, + FMC2_CSQIER_TCIE, FMC2_CSQIER_TCIE); } -static inline void stm32_fmc2_nfc_disable_seq_irq(struct stm32_fmc2_nfc *nfc) +static void stm32_fmc2_nfc_disable_seq_irq(struct stm32_fmc2_nfc *nfc) { - u32 csqier = readl_relaxed(nfc->io_base + FMC2_CSQIER); - - csqier &= ~FMC2_CSQIER_TCIE; - - writel_relaxed(csqier, nfc->io_base + FMC2_CSQIER); + regmap_update_bits(nfc->regmap, FMC2_CSQIER, FMC2_CSQIER_TCIE, 0); nfc->irq_state = FMC2_IRQ_UNKNOWN; } -static inline void stm32_fmc2_nfc_clear_seq_irq(struct stm32_fmc2_nfc *nfc) +static void stm32_fmc2_nfc_clear_seq_irq(struct stm32_fmc2_nfc *nfc) { - writel_relaxed(FMC2_CSQICR_CLEAR_IRQ, nfc->io_base + FMC2_CSQICR); + regmap_write(nfc->regmap, FMC2_CSQICR, FMC2_CSQICR_CLEAR_IRQ); } -static inline void stm32_fmc2_nfc_enable_bch_irq(struct stm32_fmc2_nfc *nfc, - int mode) +static void stm32_fmc2_nfc_enable_bch_irq(struct stm32_fmc2_nfc *nfc, int mode) { - u32 bchier = readl_relaxed(nfc->io_base + FMC2_BCHIER); + nfc->irq_state = FMC2_IRQ_BCH; if (mode == NAND_ECC_WRITE) - bchier |= FMC2_BCHIER_EPBRIE; + regmap_update_bits(nfc->regmap, FMC2_BCHIER, + FMC2_BCHIER_EPBRIE, FMC2_BCHIER_EPBRIE); else - bchier |= FMC2_BCHIER_DERIE; - - nfc->irq_state = FMC2_IRQ_BCH; - - writel_relaxed(bchier, nfc->io_base + FMC2_BCHIER); + regmap_update_bits(nfc->regmap, FMC2_BCHIER, + FMC2_BCHIER_DERIE, FMC2_BCHIER_DERIE); } -static inline void stm32_fmc2_nfc_disable_bch_irq(struct stm32_fmc2_nfc *nfc) +static void stm32_fmc2_nfc_disable_bch_irq(struct stm32_fmc2_nfc *nfc) { - u32 bchier = readl_relaxed(nfc->io_base + FMC2_BCHIER); - - bchier &= ~FMC2_BCHIER_DERIE; - bchier &= ~FMC2_BCHIER_EPBRIE; - - writel_relaxed(bchier, nfc->io_base + FMC2_BCHIER); + regmap_update_bits(nfc->regmap, FMC2_BCHIER, + FMC2_BCHIER_DERIE | FMC2_BCHIER_EPBRIE, 0); nfc->irq_state = FMC2_IRQ_UNKNOWN; } -static inline void stm32_fmc2_nfc_clear_bch_irq(struct stm32_fmc2_nfc *nfc) +static void stm32_fmc2_nfc_clear_bch_irq(struct stm32_fmc2_nfc *nfc) { - writel_relaxed(FMC2_BCHICR_CLEAR_IRQ, nfc->io_base + FMC2_BCHICR); + regmap_write(nfc->regmap, FMC2_BCHICR, FMC2_BCHICR_CLEAR_IRQ); } /* @@ -481,13 +471,8 @@ static void stm32_fmc2_nfc_hwctl(struct nand_chip *chip, int mode) stm32_fmc2_nfc_set_ecc(nfc, false); if (chip->ecc.strength != FMC2_ECC_HAM) { - u32 pcr = readl_relaxed(nfc->io_base + FMC2_PCR); - - if (mode == NAND_ECC_WRITE) - pcr |= FMC2_PCR_WEN; - else - pcr &= ~FMC2_PCR_WEN; - writel_relaxed(pcr, nfc->io_base + FMC2_PCR); + regmap_update_bits(nfc->regmap, FMC2_PCR, FMC2_PCR_WEN, + mode == NAND_ECC_WRITE ? FMC2_PCR_WEN : 0); reinit_completion(&nfc->complete); stm32_fmc2_nfc_clear_bch_irq(nfc); @@ -502,7 +487,7 @@ static void stm32_fmc2_nfc_hwctl(struct nand_chip *chip, int mode) * ECC is 3 bytes for 512 bytes of data (supports error correction up to * max of 1-bit) */ -static inline void stm32_fmc2_nfc_ham_set_ecc(const u32 ecc_sta, u8 *ecc) +static void stm32_fmc2_nfc_ham_set_ecc(const u32 ecc_sta, u8 *ecc) { ecc[0] = ecc_sta; ecc[1] = ecc_sta >> 8; @@ -516,15 +501,15 @@ static int stm32_fmc2_nfc_ham_calculate(struct nand_chip *chip, const u8 *data, u32 sr, heccr; int ret; - ret = readl_relaxed_poll_timeout(nfc->io_base + FMC2_SR, - sr, sr & FMC2_SR_NWRF, 1, - 1000 * FMC2_TIMEOUT_MS); + ret = regmap_read_poll_timeout(nfc->regmap, FMC2_SR, sr, + sr & FMC2_SR_NWRF, 1, + 1000 * FMC2_TIMEOUT_MS); if (ret) { dev_err(nfc->dev, "ham timeout\n"); return ret; } - heccr = readl_relaxed(nfc->io_base + FMC2_HECCR); + regmap_read(nfc->regmap, FMC2_HECCR, &heccr); stm32_fmc2_nfc_ham_set_ecc(heccr, ecc); stm32_fmc2_nfc_set_ecc(nfc, false); @@ -603,13 +588,13 @@ static int stm32_fmc2_nfc_bch_calculate(struct nand_chip *chip, const u8 *data, } /* Read parity bits */ - bchpbr = readl_relaxed(nfc->io_base + FMC2_BCHPBR1); + regmap_read(nfc->regmap, FMC2_BCHPBR1, &bchpbr); ecc[0] = bchpbr; ecc[1] = bchpbr >> 8; ecc[2] = bchpbr >> 16; ecc[3] = bchpbr >> 24; - bchpbr = readl_relaxed(nfc->io_base + FMC2_BCHPBR2); + regmap_read(nfc->regmap, FMC2_BCHPBR2, &bchpbr); ecc[4] = bchpbr; ecc[5] = bchpbr >> 8; ecc[6] = bchpbr >> 16; @@ -617,13 +602,13 @@ static int stm32_fmc2_nfc_bch_calculate(struct nand_chip *chip, const u8 *data, if (chip->ecc.strength == FMC2_ECC_BCH8) { ecc[7] = bchpbr >> 24; - bchpbr = readl_relaxed(nfc->io_base + FMC2_BCHPBR3); + regmap_read(nfc->regmap, FMC2_BCHPBR3, &bchpbr); ecc[8] = bchpbr; ecc[9] = bchpbr >> 8; ecc[10] = bchpbr >> 16; ecc[11] = bchpbr >> 24; - bchpbr = readl_relaxed(nfc->io_base + FMC2_BCHPBR4); + regmap_read(nfc->regmap, FMC2_BCHPBR4, &bchpbr); ecc[12] = bchpbr; } @@ -685,11 +670,7 @@ static int stm32_fmc2_nfc_bch_correct(struct nand_chip *chip, u8 *dat, return -ETIMEDOUT; } - ecc_sta[0] = readl_relaxed(nfc->io_base + FMC2_BCHDSR0); - ecc_sta[1] = readl_relaxed(nfc->io_base + FMC2_BCHDSR1); - ecc_sta[2] = readl_relaxed(nfc->io_base + FMC2_BCHDSR2); - ecc_sta[3] = readl_relaxed(nfc->io_base + FMC2_BCHDSR3); - ecc_sta[4] = readl_relaxed(nfc->io_base + FMC2_BCHDSR4); + regmap_bulk_read(nfc->regmap, FMC2_BCHDSR0, ecc_sta, 5); stm32_fmc2_nfc_set_ecc(nfc, false); @@ -764,30 +745,29 @@ static void stm32_fmc2_nfc_rw_page_init(struct nand_chip *chip, int page, { struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); struct mtd_info *mtd = nand_to_mtd(chip); - u32 csqcfgr1, csqcfgr2, csqcfgr3; - u32 csqar1, csqar2; u32 ecc_offset = mtd->writesize + FMC2_BBM_LEN; - u32 pcr = readl_relaxed(nfc->io_base + FMC2_PCR); + /* + * cfg[0] => csqcfgr1, cfg[1] => csqcfgr2, cfg[2] => csqcfgr3 + * cfg[3] => csqar1, cfg[4] => csqar2 + */ + u32 cfg[5]; - if (write_data) - pcr |= FMC2_PCR_WEN; - else - pcr &= ~FMC2_PCR_WEN; - writel_relaxed(pcr, nfc->io_base + FMC2_PCR); + regmap_update_bits(nfc->regmap, FMC2_PCR, FMC2_PCR_WEN, + write_data ? FMC2_PCR_WEN : 0); /* * - Set Program Page/Page Read command * - Enable DMA request data * - Set timings */ - csqcfgr1 = FMC2_CSQCFGR1_DMADEN | FMC2_CSQCFGR1_CMD1T; + cfg[0] = FMC2_CSQCFGR1_DMADEN | FMC2_CSQCFGR1_CMD1T; if (write_data) - csqcfgr1 |= FIELD_PREP(FMC2_CSQCFGR1_CMD1, NAND_CMD_SEQIN); + cfg[0] |= FIELD_PREP(FMC2_CSQCFGR1_CMD1, NAND_CMD_SEQIN); else - csqcfgr1 |= FIELD_PREP(FMC2_CSQCFGR1_CMD1, NAND_CMD_READ0) | - FMC2_CSQCFGR1_CMD2EN | - FIELD_PREP(FMC2_CSQCFGR1_CMD2, NAND_CMD_READSTART) | - FMC2_CSQCFGR1_CMD2T; + cfg[0] |= FIELD_PREP(FMC2_CSQCFGR1_CMD1, NAND_CMD_READ0) | + FMC2_CSQCFGR1_CMD2EN | + FIELD_PREP(FMC2_CSQCFGR1_CMD2, NAND_CMD_READSTART) | + FMC2_CSQCFGR1_CMD2T; /* * - Set Random Data Input/Random Data Read command @@ -796,30 +776,29 @@ static void stm32_fmc2_nfc_rw_page_init(struct nand_chip *chip, int page, * - Set timings */ if (write_data) - csqcfgr2 = FIELD_PREP(FMC2_CSQCFGR2_RCMD1, NAND_CMD_RNDIN); + cfg[1] = FIELD_PREP(FMC2_CSQCFGR2_RCMD1, NAND_CMD_RNDIN); else - csqcfgr2 = FIELD_PREP(FMC2_CSQCFGR2_RCMD1, NAND_CMD_RNDOUT) | - FMC2_CSQCFGR2_RCMD2EN | - FIELD_PREP(FMC2_CSQCFGR2_RCMD2, - NAND_CMD_RNDOUTSTART) | - FMC2_CSQCFGR2_RCMD1T | - FMC2_CSQCFGR2_RCMD2T; + cfg[1] = FIELD_PREP(FMC2_CSQCFGR2_RCMD1, NAND_CMD_RNDOUT) | + FMC2_CSQCFGR2_RCMD2EN | + FIELD_PREP(FMC2_CSQCFGR2_RCMD2, NAND_CMD_RNDOUTSTART) | + FMC2_CSQCFGR2_RCMD1T | + FMC2_CSQCFGR2_RCMD2T; if (!raw) { - csqcfgr2 |= write_data ? 0 : FMC2_CSQCFGR2_DMASEN; - csqcfgr2 |= FMC2_CSQCFGR2_SQSDTEN; + cfg[1] |= write_data ? 0 : FMC2_CSQCFGR2_DMASEN; + cfg[1] |= FMC2_CSQCFGR2_SQSDTEN; } /* * - Set the number of sectors to be written * - Set timings */ - csqcfgr3 = FIELD_PREP(FMC2_CSQCFGR3_SNBR, chip->ecc.steps - 1); + cfg[2] = FIELD_PREP(FMC2_CSQCFGR3_SNBR, chip->ecc.steps - 1); if (write_data) { - csqcfgr3 |= FMC2_CSQCFGR3_RAC2T; + cfg[2] |= FMC2_CSQCFGR3_RAC2T; if (chip->options & NAND_ROW_ADDR_3) - csqcfgr3 |= FMC2_CSQCFGR3_AC5T; + cfg[2] |= FMC2_CSQCFGR3_AC5T; else - csqcfgr3 |= FMC2_CSQCFGR3_AC4T; + cfg[2] |= FMC2_CSQCFGR3_AC4T; } /* @@ -827,8 +806,8 @@ static void stm32_fmc2_nfc_rw_page_init(struct nand_chip *chip, int page, * Byte 1 and byte 2 => column, we start at 0x0 * Byte 3 and byte 4 => page */ - csqar1 = FIELD_PREP(FMC2_CSQCAR1_ADDC3, page); - csqar1 |= FIELD_PREP(FMC2_CSQCAR1_ADDC4, page >> 8); + cfg[3] = FIELD_PREP(FMC2_CSQCAR1_ADDC3, page); + cfg[3] |= FIELD_PREP(FMC2_CSQCAR1_ADDC4, page >> 8); /* * - Set chip enable number @@ -836,23 +815,19 @@ static void stm32_fmc2_nfc_rw_page_init(struct nand_chip *chip, int page, * - Calculate the number of address cycles to be issued * - Set byte 5 of address cycle if needed */ - csqar2 = FIELD_PREP(FMC2_CSQCAR2_NANDCEN, nfc->cs_sel); + cfg[4] = FIELD_PREP(FMC2_CSQCAR2_NANDCEN, nfc->cs_sel); if (chip->options & NAND_BUSWIDTH_16) - csqar2 |= FIELD_PREP(FMC2_CSQCAR2_SAO, ecc_offset >> 1); + cfg[4] |= FIELD_PREP(FMC2_CSQCAR2_SAO, ecc_offset >> 1); else - csqar2 |= FIELD_PREP(FMC2_CSQCAR2_SAO, ecc_offset); + cfg[4] |= FIELD_PREP(FMC2_CSQCAR2_SAO, ecc_offset); if (chip->options & NAND_ROW_ADDR_3) { - csqcfgr1 |= FIELD_PREP(FMC2_CSQCFGR1_ACYNBR, 5); - csqar2 |= FIELD_PREP(FMC2_CSQCAR2_ADDC5, page >> 16); + cfg[0] |= FIELD_PREP(FMC2_CSQCFGR1_ACYNBR, 5); + cfg[4] |= FIELD_PREP(FMC2_CSQCAR2_ADDC5, page >> 16); } else { - csqcfgr1 |= FIELD_PREP(FMC2_CSQCFGR1_ACYNBR, 4); + cfg[0] |= FIELD_PREP(FMC2_CSQCFGR1_ACYNBR, 4); } - writel_relaxed(csqcfgr1, nfc->io_base + FMC2_CSQCFGR1); - writel_relaxed(csqcfgr2, nfc->io_base + FMC2_CSQCFGR2); - writel_relaxed(csqcfgr3, nfc->io_base + FMC2_CSQCFGR3); - writel_relaxed(csqar1, nfc->io_base + FMC2_CSQAR1); - writel_relaxed(csqar2, nfc->io_base + FMC2_CSQAR2); + regmap_bulk_write(nfc->regmap, FMC2_CSQCFGR1, cfg, 5); } static void stm32_fmc2_nfc_dma_callback(void *arg) @@ -870,7 +845,6 @@ static int stm32_fmc2_nfc_xfer(struct nand_chip *chip, const u8 *buf, struct dma_chan *dma_ch = nfc->dma_rx_ch; enum dma_data_direction dma_data_dir = DMA_FROM_DEVICE; enum dma_transfer_direction dma_transfer_dir = DMA_DEV_TO_MEM; - u32 csqcr = readl_relaxed(nfc->io_base + FMC2_CSQCR); int eccsteps = chip->ecc.steps; int eccsize = chip->ecc.size; unsigned long timeout = msecs_to_jiffies(FMC2_TIMEOUT_MS); @@ -948,8 +922,8 @@ static int stm32_fmc2_nfc_xfer(struct nand_chip *chip, const u8 *buf, stm32_fmc2_nfc_enable_seq_irq(nfc); /* Start the transfer */ - csqcr |= FMC2_CSQCR_CSQSTART; - writel_relaxed(csqcr, nfc->io_base + FMC2_CSQCR); + regmap_update_bits(nfc->regmap, FMC2_CSQCR, + FMC2_CSQCR_CSQSTART, FMC2_CSQCR_CSQSTART); /* Wait end of sequencer transfer */ if (!wait_for_completion_timeout(&nfc->complete, timeout)) { @@ -1042,11 +1016,13 @@ static int stm32_fmc2_nfc_seq_write_page_raw(struct nand_chip *chip, } /* Get a status indicating which sectors have errors */ -static inline u16 stm32_fmc2_nfc_get_mapping_status(struct stm32_fmc2_nfc *nfc) +static u16 stm32_fmc2_nfc_get_mapping_status(struct stm32_fmc2_nfc *nfc) { - u32 csqemsr = readl_relaxed(nfc->io_base + FMC2_CSQEMSR); + u32 csqemsr; + + regmap_read(nfc->regmap, FMC2_CSQEMSR, &csqemsr); - return csqemsr & FMC2_CSQEMSR_SEM; + return FIELD_GET(FMC2_CSQEMSR_SEM, csqemsr); } static int stm32_fmc2_nfc_seq_correct(struct nand_chip *chip, u8 *dat, @@ -1302,9 +1278,9 @@ static int stm32_fmc2_nfc_waitrdy(struct nand_chip *chip, u32 isr, sr; /* Check if there is no pending requests to the NAND flash */ - if (readl_relaxed_poll_timeout_atomic(nfc->io_base + FMC2_SR, sr, - sr & FMC2_SR_NWRF, 1, - 1000 * FMC2_TIMEOUT_MS)) + if (regmap_read_poll_timeout(nfc->regmap, FMC2_SR, sr, + sr & FMC2_SR_NWRF, 1, + 1000 * FMC2_TIMEOUT_MS)) dev_warn(nfc->dev, "Waitrdy timeout\n"); /* Wait tWB before R/B# signal is low */ @@ -1312,12 +1288,12 @@ static int stm32_fmc2_nfc_waitrdy(struct nand_chip *chip, ndelay(PSEC_TO_NSEC(timings->tWB_max)); /* R/B# signal is low, clear high level flag */ - writel_relaxed(FMC2_ICR_CIHLF, nfc->io_base + FMC2_ICR); + regmap_write(nfc->regmap, FMC2_ICR, FMC2_ICR_CIHLF); /* Wait R/B# signal is high */ - return readl_relaxed_poll_timeout_atomic(nfc->io_base + FMC2_ISR, - isr, isr & FMC2_ISR_IHLF, - 5, 1000 * timeout_ms); + return regmap_read_poll_timeout(nfc->regmap, FMC2_ISR, isr, + isr & FMC2_ISR_IHLF, 5, + 1000 * FMC2_TIMEOUT_MS); } static int stm32_fmc2_nfc_exec_op(struct nand_chip *chip, @@ -1375,8 +1351,9 @@ static int stm32_fmc2_nfc_exec_op(struct nand_chip *chip, static void stm32_fmc2_nfc_init(struct stm32_fmc2_nfc *nfc) { - u32 pcr = readl_relaxed(nfc->io_base + FMC2_PCR); - u32 bcr1 = readl_relaxed(nfc->io_base + FMC2_BCR1); + u32 pcr; + + regmap_read(nfc->regmap, FMC2_PCR, &pcr); /* Set CS used to undefined */ nfc->cs_sel = -1; @@ -1407,12 +1384,12 @@ static void stm32_fmc2_nfc_init(struct stm32_fmc2_nfc *nfc) pcr |= FIELD_PREP(FMC2_PCR_TAR, FMC2_PCR_TAR_DEFAULT); /* Enable FMC2 controller */ - bcr1 |= FMC2_BCR1_FMC2EN; + regmap_update_bits(nfc->regmap, FMC2_BCR1, + FMC2_BCR1_FMC2EN, FMC2_BCR1_FMC2EN); - writel_relaxed(bcr1, nfc->io_base + FMC2_BCR1); - writel_relaxed(pcr, nfc->io_base + FMC2_PCR); - writel_relaxed(FMC2_PMEM_DEFAULT, nfc->io_base + FMC2_PMEM); - writel_relaxed(FMC2_PATT_DEFAULT, nfc->io_base + FMC2_PATT); + regmap_write(nfc->regmap, FMC2_PCR, pcr); + regmap_write(nfc->regmap, FMC2_PMEM, FMC2_PMEM_DEFAULT); + regmap_write(nfc->regmap, FMC2_PATT, FMC2_PATT_DEFAULT); } static void stm32_fmc2_nfc_calc_timings(struct nand_chip *chip, @@ -1847,6 +1824,7 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev) struct resource *res; struct mtd_info *mtd; struct nand_chip *chip; + void __iomem *mmio; int chip_cs, mem_region, ret, irq; nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); @@ -1862,9 +1840,13 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev) return ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nfc->io_base = devm_ioremap_resource(dev, res); - if (IS_ERR(nfc->io_base)) - return PTR_ERR(nfc->io_base); + mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(mmio)) + return PTR_ERR(mmio); + + nfc->regmap = devm_regmap_init_mmio(dev, mmio, &stm32_fmc2_regmap_cfg); + if (IS_ERR(nfc->regmap)) + return PTR_ERR(nfc->regmap); nfc->io_phys_addr = res->start;