* [PATCH v3 0/3] Add add SPI-NAND Flash controller driver for EN7581 @ 2024-04-23 10:16 Lorenzo Bianconi 2024-04-23 10:16 ` [PATCH v3 1/3] dt-bindings: spi: airoha: Add YAML schema for SNFI controller Lorenzo Bianconi ` (2 more replies) 0 siblings, 3 replies; 12+ messages in thread From: Lorenzo Bianconi @ 2024-04-23 10:16 UTC (permalink / raw) To: linux-spi Cc: conor, broonie, lorenzo.bianconi83, linux-arm-kernel, robh+dt, krzysztof.kozlowski+dt, conor+dt, devicetree, nbd, john, dd, catalin.marinas, will, upstream, angelogioacchino.delregno Introduce support for SPI-NAND driver of the Airoha NAND Flash Interface found on Airoha ARM EN7581 SoCs. Changes since v2: - Fix compilation warnings - Remove interrupt entry in dts since it is not connected so far Changes since v1: - Introduce spi clock dependency Lorenzo Bianconi (3): dt-bindings: spi: airoha: Add YAML schema for SNFI controller arm64: dts: airoha: add EN7581 spi-nand node spi: airoha: add SPI-NAND Flash controller driver .../bindings/spi/airoha,en7581-snand.yaml | 65 + MAINTAINERS | 9 + arch/arm64/boot/dts/airoha/en7581.dtsi | 19 + drivers/spi/Kconfig | 10 + drivers/spi/Makefile | 1 + drivers/spi/spi-airoha-snfi.c | 1156 +++++++++++++++++ 6 files changed, 1260 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/airoha,en7581-snand.yaml create mode 100644 drivers/spi/spi-airoha-snfi.c -- 2.44.0 ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 1/3] dt-bindings: spi: airoha: Add YAML schema for SNFI controller 2024-04-23 10:16 [PATCH v3 0/3] Add add SPI-NAND Flash controller driver for EN7581 Lorenzo Bianconi @ 2024-04-23 10:16 ` Lorenzo Bianconi 2024-04-23 10:44 ` AngeloGioacchino Del Regno 2024-04-23 10:16 ` [PATCH v3 2/3] arm64: dts: airoha: add EN7581 spi-nand node Lorenzo Bianconi 2024-04-23 10:16 ` [PATCH v3 3/3] spi: airoha: add SPI-NAND Flash controller driver Lorenzo Bianconi 2 siblings, 1 reply; 12+ messages in thread From: Lorenzo Bianconi @ 2024-04-23 10:16 UTC (permalink / raw) To: linux-spi Cc: conor, broonie, lorenzo.bianconi83, linux-arm-kernel, robh+dt, krzysztof.kozlowski+dt, conor+dt, devicetree, nbd, john, dd, catalin.marinas, will, upstream, angelogioacchino.delregno Introduce Airoha EN7581 SPI NAND controller binding Reviewed-by: Conor Dooley <conor.dooley@microchip.com> Tested-by: Rajeev Kumar <Rajeev.Kumar@airoha.com> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> --- .../bindings/spi/airoha,en7581-snand.yaml | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/airoha,en7581-snand.yaml diff --git a/Documentation/devicetree/bindings/spi/airoha,en7581-snand.yaml b/Documentation/devicetree/bindings/spi/airoha,en7581-snand.yaml new file mode 100644 index 000000000000..b820c5613dcc --- /dev/null +++ b/Documentation/devicetree/bindings/spi/airoha,en7581-snand.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/airoha,en7581-snand.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SPI-NAND flash controller for Airoha ARM SoCs + +maintainers: + - Lorenzo Bianconi <lorenzo@kernel.org> + +allOf: + - $ref: spi-controller.yaml# + +properties: + compatible: + const: airoha,en7581-snand + + reg: + items: + - description: spi base address + - description: nfi2spi base address + + clocks: + maxItems: 1 + + clock-names: + items: + - const: spi + +required: + - compatible + - reg + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/en7523-clk.h> + + soc { + #address-cells = <2>; + #size-cells = <2>; + + spi@1fa10000 { + compatible = "airoha,en7581-snand"; + reg = <0x0 0x1fa10000 0x0 0x140>, + <0x0 0x1fa11000 0x0 0x160>; + + clocks = <&scuclk EN7523_CLK_SPI>; + clock-names = "spi"; + + #address-cells = <1>; + #size-cells = <0>; + + flash@0 { + compatible = "spi-nand"; + reg = <0>; + spi-tx-bus-width = <1>; + spi-rx-bus-width = <2>; + }; + }; + }; -- 2.44.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/3] dt-bindings: spi: airoha: Add YAML schema for SNFI controller 2024-04-23 10:16 ` [PATCH v3 1/3] dt-bindings: spi: airoha: Add YAML schema for SNFI controller Lorenzo Bianconi @ 2024-04-23 10:44 ` AngeloGioacchino Del Regno 0 siblings, 0 replies; 12+ messages in thread From: AngeloGioacchino Del Regno @ 2024-04-23 10:44 UTC (permalink / raw) To: Lorenzo Bianconi, linux-spi Cc: conor, broonie, lorenzo.bianconi83, linux-arm-kernel, robh+dt, krzysztof.kozlowski+dt, conor+dt, devicetree, nbd, john, dd, catalin.marinas, will, upstream Il 23/04/24 12:16, Lorenzo Bianconi ha scritto: > Introduce Airoha EN7581 SPI NAND controller binding > > Reviewed-by: Conor Dooley <conor.dooley@microchip.com> > Tested-by: Rajeev Kumar <Rajeev.Kumar@airoha.com> > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 2/3] arm64: dts: airoha: add EN7581 spi-nand node 2024-04-23 10:16 [PATCH v3 0/3] Add add SPI-NAND Flash controller driver for EN7581 Lorenzo Bianconi 2024-04-23 10:16 ` [PATCH v3 1/3] dt-bindings: spi: airoha: Add YAML schema for SNFI controller Lorenzo Bianconi @ 2024-04-23 10:16 ` Lorenzo Bianconi 2024-04-23 10:44 ` AngeloGioacchino Del Regno 2024-04-23 10:16 ` [PATCH v3 3/3] spi: airoha: add SPI-NAND Flash controller driver Lorenzo Bianconi 2 siblings, 1 reply; 12+ messages in thread From: Lorenzo Bianconi @ 2024-04-23 10:16 UTC (permalink / raw) To: linux-spi Cc: conor, broonie, lorenzo.bianconi83, linux-arm-kernel, robh+dt, krzysztof.kozlowski+dt, conor+dt, devicetree, nbd, john, dd, catalin.marinas, will, upstream, angelogioacchino.delregno Introduce the Airoha EN7581 spi-nand node in Airoha EN7581 dtsi Tested-by: Rajeev Kumar <Rajeev.Kumar@airoha.com> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> --- arch/arm64/boot/dts/airoha/en7581.dtsi | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/arch/arm64/boot/dts/airoha/en7581.dtsi b/arch/arm64/boot/dts/airoha/en7581.dtsi index 5c4bfe3e1e5a..98c2a86086e1 100644 --- a/arch/arm64/boot/dts/airoha/en7581.dtsi +++ b/arch/arm64/boot/dts/airoha/en7581.dtsi @@ -159,5 +159,24 @@ scuclk: clock-controller@1fa20000 { <0x0 0x1fbe3400 0x0 0xfc>; #clock-cells = <1>; }; + + snfi: spi@1fa10000 { + compatible = "airoha,en7581-snand"; + reg = <0x0 0x1fa10000 0x0 0x140>, + <0x0 0x1fa11000 0x0 0x160>; + + clocks = <&scuclk EN7523_CLK_SPI>; + clock-names = "spi"; + + #address-cells = <1>; + #size-cells = <0>; + spi_nand: nand@0 { + compatible = "spi-nand"; + reg = <0>; + spi-max-frequency = <50000000>; + spi-tx-bus-width = <1>; + spi-rx-bus-width = <2>; + }; + }; }; }; -- 2.44.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v3 2/3] arm64: dts: airoha: add EN7581 spi-nand node 2024-04-23 10:16 ` [PATCH v3 2/3] arm64: dts: airoha: add EN7581 spi-nand node Lorenzo Bianconi @ 2024-04-23 10:44 ` AngeloGioacchino Del Regno 0 siblings, 0 replies; 12+ messages in thread From: AngeloGioacchino Del Regno @ 2024-04-23 10:44 UTC (permalink / raw) To: Lorenzo Bianconi, linux-spi Cc: conor, broonie, lorenzo.bianconi83, linux-arm-kernel, robh+dt, krzysztof.kozlowski+dt, conor+dt, devicetree, nbd, john, dd, catalin.marinas, will, upstream Il 23/04/24 12:16, Lorenzo Bianconi ha scritto: > Introduce the Airoha EN7581 spi-nand node in Airoha EN7581 dtsi > > Tested-by: Rajeev Kumar <Rajeev.Kumar@airoha.com> > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 3/3] spi: airoha: add SPI-NAND Flash controller driver 2024-04-23 10:16 [PATCH v3 0/3] Add add SPI-NAND Flash controller driver for EN7581 Lorenzo Bianconi 2024-04-23 10:16 ` [PATCH v3 1/3] dt-bindings: spi: airoha: Add YAML schema for SNFI controller Lorenzo Bianconi 2024-04-23 10:16 ` [PATCH v3 2/3] arm64: dts: airoha: add EN7581 spi-nand node Lorenzo Bianconi @ 2024-04-23 10:16 ` Lorenzo Bianconi 2024-04-23 10:44 ` AngeloGioacchino Del Regno 2024-04-23 23:51 ` Andy Shevchenko 2 siblings, 2 replies; 12+ messages in thread From: Lorenzo Bianconi @ 2024-04-23 10:16 UTC (permalink / raw) To: linux-spi Cc: conor, broonie, lorenzo.bianconi83, linux-arm-kernel, robh+dt, krzysztof.kozlowski+dt, conor+dt, devicetree, nbd, john, dd, catalin.marinas, will, upstream, angelogioacchino.delregno Introduce support for SPI-NAND driver of the Airoha NAND Flash Interface found on Airoha ARM SoCs. Tested-by: Rajeev Kumar <Rajeev.Kumar@airoha.com> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> --- MAINTAINERS | 9 + drivers/spi/Kconfig | 10 + drivers/spi/Makefile | 1 + drivers/spi/spi-airoha-snfi.c | 1156 +++++++++++++++++++++++++++++++++ 4 files changed, 1176 insertions(+) create mode 100644 drivers/spi/spi-airoha-snfi.c diff --git a/MAINTAINERS b/MAINTAINERS index aa3b947fb080..ce9fac46f741 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -653,6 +653,15 @@ S: Supported F: fs/aio.c F: include/linux/*aio*.h +AIROHA SPI SNFI DRIVER +M: Lorenzo Bianconi <lorenzo@kernel.org> +M: Ray Liu <ray.liu@airoha.com> +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +L: linux-spi@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/spi/airoha,en7581-snand.yaml +F: drivers/spi/spi-airoha.c + AIRSPY MEDIA DRIVER L: linux-media@vger.kernel.org S: Orphan diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index bc7021da2fe9..6fa91775f334 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -57,6 +57,16 @@ config SPI_MEM comment "SPI Master Controller Drivers" +config SPI_AIROHA_SNFI + tristate "Airoha SPI NAND Flash Interface" + depends on ARCH_AIROHA || COMPILE_TEST + depends on SPI_MASTER + select REGMAP_MMIO + help + This enables support for SPI-NAND mode on the Airoha NAND + Flash Interface found on Airoha ARM SoCs. This controller + is implemented as a SPI-MEM controller. + config SPI_ALTERA tristate "Altera SPI Controller platform driver" select SPI_ALTERA_CORE diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 4ff8d725ba5e..e694254dec04 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_SPIDEV) += spidev.o obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o # SPI master controller drivers (bus) +obj-$(CONFIG_SPI_AIROHA_SNFI) += spi-airoha-snfi.o obj-$(CONFIG_SPI_ALTERA) += spi-altera-platform.o obj-$(CONFIG_SPI_ALTERA_CORE) += spi-altera-core.o obj-$(CONFIG_SPI_ALTERA_DFL) += spi-altera-dfl.o diff --git a/drivers/spi/spi-airoha-snfi.c b/drivers/spi/spi-airoha-snfi.c new file mode 100644 index 000000000000..eacb32d6e28e --- /dev/null +++ b/drivers/spi/spi-airoha-snfi.c @@ -0,0 +1,1156 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 AIROHA Inc + * Author: Lorenzo Bianconi <lorenzo@kernel.org> + * Author: Ray Liu <ray.liu@airoha.com> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi-mem.h> + +/* SPI */ +#define REG_SPI_CTRL_BASE 0x1FA10000 + +#define REG_SPI_CTRL_READ_MODE 0x0000 +#define REG_SPI_CTRL_READ_IDLE_EN 0x0004 +#define REG_SPI_CTRL_SIDLY 0x0008 +#define REG_SPI_CTRL_CSHEXT 0x000c +#define REG_SPI_CTRL_CSLEXT 0x0010 + +#define REG_SPI_CTRL_MTX_MODE_TOG 0x0014 +#define SPI_CTRL_MTX_MODE_TOG GENMASK(3, 0) + +#define REG_SPI_CTRL_RDCTL_FSM 0x0018 +#define SPI_CTRL_RDCTL_FSM GENMASK(3, 0) + +#define REG_SPI_CTRL_MACMUX_SEL 0x001c + +#define REG_SPI_CTRL_MANUAL_EN 0x0020 +#define SPI_CTRL_MANUAL_EN BIT(0) + +#define REG_SPI_CTRL_OPFIFO_EMPTY 0x0024 +#define SPI_CTRL_OPFIFO_EMPTY BIT(0) + +#define REG_SPI_CTRL_OPFIFO_WDATA 0x0028 +#define SPI_CTRL_OPFIFO_LEN GENMASK(8, 0) +#define SPI_CTRL_OPFIFO_OP GENMASK(13, 9) + +#define REG_SPI_CTRL_OPFIFO_FULL 0x002c +#define SPI_CTRL_OPFIFO_FULL BIT(0) + +#define REG_SPI_CTRL_OPFIFO_WR 0x0030 +#define SPI_CTRL_OPFIFO_WR BIT(0) + +#define REG_SPI_CTRL_DFIFO_FULL 0x0034 +#define SPI_CTRL_DFIFO_FULL BIT(0) + +#define REG_SPI_CTRL_DFIFO_WDATA 0x0038 +#define SPI_CTRL_DFIFO_WDATA GENMASK(7, 0) + +#define REG_SPI_CTRL_DFIFO_EMPTY 0x003c +#define SPI_CTRL_DFIFO_EMPTY BIT(0) + +#define REG_SPI_CTRL_DFIFO_RD 0x0040 +#define SPI_CTRL_DFIFO_RD BIT(0) + +#define REG_SPI_CTRL_DFIFO_RDATA 0x0044 +#define SPI_CTRL_DFIFO_RDATA GENMASK(7, 0) + +#define REG_SPI_CTRL_DUMMY 0x0080 +#define SPI_CTRL_CTRL_DUMMY GENMASK(3, 0) + +#define REG_SPI_CTRL_PROBE_SEL 0x0088 +#define REG_SPI_CTRL_INTERRUPT 0x0090 +#define REG_SPI_CTRL_INTERRUPT_EN 0x0094 +#define REG_SPI_CTRL_SI_CK_SEL 0x009c +#define REG_SPI_CTRL_SW_CFGNANDADDR_VAL 0x010c +#define REG_SPI_CTRL_SW_CFGNANDADDR_EN 0x0110 +#define REG_SPI_CTRL_SFC_STRAP 0x0114 + +#define REG_SPI_CTRL_NFI2SPI_EN 0x0130 +#define SPI_CTRL_NFI2SPI_EN BIT(0) + +/* NFI2SPI */ +#define REG_SPI_NFI_CNFG 0x0000 +#define SPI_NFI_DMA_MODE BIT(0) +#define SPI_NFI_READ_MODE BIT(1) +#define SPI_NFI_DMA_BURST_EN BIT(2) +#define SPI_NFI_HW_ECC_EN BIT(8) +#define SPI_NFI_AUTO_FDM_EN BIT(9) +#define SPI_NFI_OPMODE GENMASK(14, 12) + +#define REG_SPI_NFI_PAGEFMT 0x0004 +#define SPI_NFI_PAGE_SIZE GENMASK(1, 0) +#define SPI_NFI_SPARE_SIZE GENMASK(5, 4) + +#define REG_SPI_NFI_CON 0x0008 +#define SPI_NFI_FIFO_FLUSH BIT(0) +#define SPI_NFI_RST BIT(1) +#define SPI_NFI_RD_TRIG BIT(8) +#define SPI_NFI_WR_TRIG BIT(9) +#define SPI_NFI_SEC_NUM GENMASK(15, 12) + +#define REG_SPI_NFI_INTR_EN 0x0010 +#define SPI_NFI_RD_DONE_EN BIT(0) +#define SPI_NFI_WR_DONE_EN BIT(1) +#define SPI_NFI_RST_DONE_EN BIT(2) +#define SPI_NFI_ERASE_DONE_EN BIT(3) +#define SPI_NFI_BUSY_RETURN_EN BIT(4) +#define SPI_NFI_ACCESS_LOCK_EN BIT(5) +#define SPI_NFI_AHB_DONE_EN BIT(6) +#define SPI_NFI_ALL_IRQ_EN (SPI_NFI_RD_DONE_EN | \ + SPI_NFI_WR_DONE_EN | \ + SPI_NFI_RST_DONE_EN | \ + SPI_NFI_ERASE_DONE_EN | \ + SPI_NFI_BUSY_RETURN_EN | \ + SPI_NFI_ACCESS_LOCK_EN | \ + SPI_NFI_AHB_DONE_EN) + +#define REG_SPI_NFI_INTR 0x0014 +#define SPI_NFI_AHB_DONE BIT(6) + +#define REG_SPI_NFI_CMD 0x0020 + +#define REG_SPI_NFI_ADDR_NOB 0x0030 +#define SPI_NFI_ROW_ADDR_NOB GENMASK(6, 4) + +#define REG_SPI_NFI_STA 0x0060 +#define REG_SPI_NFI_FIFOSTA 0x0064 +#define REG_SPI_NFI_STRADDR 0x0080 +#define REG_SPI_NFI_FDM0L 0x00a0 +#define REG_SPI_NFI_FDM0M 0x00a4 +#define REG_SPI_NFI_FDM7L 0x00d8 +#define REG_SPI_NFI_FDM7M 0x00dc +#define REG_SPI_NFI_FIFODATA0 0x0190 +#define REG_SPI_NFI_FIFODATA1 0x0194 +#define REG_SPI_NFI_FIFODATA2 0x0198 +#define REG_SPI_NFI_FIFODATA3 0x019c +#define REG_SPI_NFI_MASTERSTA 0x0224 + +#define REG_SPI_NFI_SECCUS_SIZE 0x022c +#define SPI_NFI_CUS_SEC_SIZE GENMASK(12, 0) +#define SPI_NFI_CUS_SEC_SIZE_EN BIT(16) + +#define REG_SPI_NFI_RD_CTL2 0x0510 +#define REG_SPI_NFI_RD_CTL3 0x0514 + +#define REG_SPI_NFI_PG_CTL1 0x0524 +#define SPI_NFI_PG_LOAD_CMD GENMASK(15, 8) + +#define REG_SPI_NFI_PG_CTL2 0x0528 +#define REG_SPI_NFI_NOR_PROG_ADDR 0x052c +#define REG_SPI_NFI_NOR_RD_ADDR 0x0534 + +#define REG_SPI_NFI_SNF_MISC_CTL 0x0538 +#define SPI_NFI_DATA_READ_WR_MODE GENMASK(18, 16) + +#define REG_SPI_NFI_SNF_MISC_CTL2 0x053c +#define SPI_NFI_READ_DATA_BYTE_NUM GENMASK(12, 0) +#define SPI_NFI_PROG_LOAD_BYTE_NUM GENMASK(28, 16) + +#define REG_SPI_NFI_SNF_STA_CTL1 0x0550 +#define SPI_NFI_READ_FROM_CACHE_DONE BIT(25) +#define SPI_NFI_LOAD_TO_CACHE_DONE BIT(26) + +#define REG_SPI_NFI_SNF_STA_CTL2 0x0554 + +#define REG_SPI_NFI_SNF_NFI_CNFG 0x055c +#define SPI_NFI_SPI_MODE BIT(0) + +/* SPI NAND Protocol OP */ +#define SPI_NAND_OP_GET_FEATURE 0x0f +#define SPI_NAND_OP_SET_FEATURE 0x1f +#define SPI_NAND_OP_PAGE_READ 0x13 +#define SPI_NAND_OP_READ_FROM_CACHE_SINGLE 0x03 +#define SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST 0x0b +#define SPI_NAND_OP_READ_FROM_CACHE_DUAL 0x3b +#define SPI_NAND_OP_READ_FROM_CACHE_QUAD 0x6b +#define SPI_NAND_OP_WRITE_ENABLE 0x06 +#define SPI_NAND_OP_WRITE_DISABLE 0x04 +#define SPI_NAND_OP_PROGRAM_LOAD_SINGLE 0x02 +#define SPI_NAND_OP_PROGRAM_LOAD_QUAD 0x32 +#define SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE 0x84 +#define SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD 0x34 +#define SPI_NAND_OP_PROGRAM_EXECUTE 0x10 +#define SPI_NAND_OP_READ_ID 0x9f +#define SPI_NAND_OP_BLOCK_ERASE 0xd8 +#define SPI_NAND_OP_RESET 0xff +#define SPI_NAND_OP_DIE_SELECT 0xc2 + +enum airoha_snand_mode { + SPI_MODE_AUTO, + SPI_MODE_MANUAL, + SPI_MODE_DMA, + SPI_MODE_NO +}; + +enum airoha_snand_cs { + SPI_CHIP_SEL_HIGH, + SPI_CHIP_SEL_LOW, +}; + +struct airoha_snand_dev { + size_t buf_len; + + u8 *txrx_buf; + dma_addr_t dma_addr; + + bool data_need_update; + u64 cur_page_num; +}; + +struct airoha_snand_ctrl { + struct device *dev; + struct regmap *regmap_ctrl; + struct regmap *regmap_nfi; + struct clk *spi_clk; + + struct { + size_t page_size; + size_t sec_size; + unsigned char sec_num; + unsigned char spare_size; + } nfi_cfg; +}; + +static int airoha_snand_set_fifo_op(struct airoha_snand_ctrl *as_ctrl, + u8 op_cmd, int op_len) +{ + int err; + u32 val; + + err = regmap_write(as_ctrl->regmap_ctrl, REG_SPI_CTRL_OPFIFO_WDATA, + FIELD_PREP(SPI_CTRL_OPFIFO_LEN, op_len) | + FIELD_PREP(SPI_CTRL_OPFIFO_OP, op_cmd)); + if (err) + return err; + + err = regmap_read_poll_timeout(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_OPFIFO_FULL, + val, !(val & SPI_CTRL_OPFIFO_FULL), + 0, 250 * USEC_PER_MSEC); + if (err) + return err; + + err = regmap_write(as_ctrl->regmap_ctrl, REG_SPI_CTRL_OPFIFO_WR, + SPI_CTRL_OPFIFO_WR); + if (err) + return err; + + return regmap_read_poll_timeout(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_OPFIFO_EMPTY, + val, (val & SPI_CTRL_OPFIFO_EMPTY), + 0, 250 * USEC_PER_MSEC); +} + +static int airoha_snand_set_cs(struct airoha_snand_ctrl *as_ctrl, u8 cs) +{ + return airoha_snand_set_fifo_op(as_ctrl, cs, sizeof(cs)); +} + +static int airoha_snand_write_data_to_fifo(struct airoha_snand_ctrl *as_ctrl, + const u8 *data, int len) +{ + int i; + + for (i = 0; i < len; i++) { + int err; + u32 val; + + /* 1. Wait until dfifo is not full */ + err = regmap_read_poll_timeout(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_DFIFO_FULL, val, + !(val & SPI_CTRL_DFIFO_FULL), + 0, 250 * USEC_PER_MSEC); + if (err) + return err; + + /* 2. Write data to register DFIFO_WDATA */ + err = regmap_write(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_DFIFO_WDATA, + FIELD_PREP(SPI_CTRL_DFIFO_WDATA, data[i])); + if (err) + return err; + + /* 3. Wait until dfifo is not full */ + err = regmap_read_poll_timeout(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_DFIFO_FULL, val, + !(val & SPI_CTRL_DFIFO_FULL), + 0, 250 * USEC_PER_MSEC); + if (err) + return err; + } + + return 0; +} + +static int airoha_snand_read_data_from_fifo(struct airoha_snand_ctrl *as_ctrl, + u8 *ptr, int len) +{ + int i; + + for (i = 0; i < len; i++) { + int err; + u32 val; + + /* 1. wait until dfifo is not empty */ + err = regmap_read_poll_timeout(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_DFIFO_EMPTY, val, + !(val & SPI_CTRL_DFIFO_EMPTY), + 0, 250 * USEC_PER_MSEC); + if (err) + return err; + + /* 2. read from dfifo to register DFIFO_RDATA */ + err = regmap_read(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_DFIFO_RDATA, &val); + if (err) + return err; + + ptr[i] = FIELD_GET(SPI_CTRL_DFIFO_RDATA, val); + /* 3. enable register DFIFO_RD to read next byte */ + err = regmap_write(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_DFIFO_RD, SPI_CTRL_DFIFO_RD); + if (err) + return err; + } + + return 0; +} + +static int airoha_snand_set_mode(struct airoha_snand_ctrl *as_ctrl, + enum airoha_snand_mode mode) +{ + int err; + + switch (mode) { + case SPI_MODE_MANUAL: { + u32 val; + + err = regmap_write(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_NFI2SPI_EN, 0); + if (err) + return err; + + err = regmap_write(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_READ_IDLE_EN, 0); + if (err) + return err; + + err = regmap_read_poll_timeout(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_RDCTL_FSM, val, + !(val & SPI_CTRL_RDCTL_FSM), + 0, 250 * USEC_PER_MSEC); + if (err) + return err; + + err = regmap_write(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_MTX_MODE_TOG, 9); + if (err) + return err; + + err = regmap_write(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_MANUAL_EN, SPI_CTRL_MANUAL_EN); + if (err) + return err; + break; + } + case SPI_MODE_DMA: + err = regmap_write(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_NFI2SPI_EN, + SPI_CTRL_MANUAL_EN); + if (err < 0) + return err; + + err = regmap_write(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_MTX_MODE_TOG, 0x0); + if (err < 0) + return err; + + err = regmap_write(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_MANUAL_EN, 0x0); + if (err < 0) + return err; + break; + case SPI_MODE_AUTO: + default: + break; + } + + return regmap_write(as_ctrl->regmap_ctrl, REG_SPI_CTRL_DUMMY, 0); +} + +#define MAX_TRANSFER_SIZE 511 +static int airoha_snand_write_data(struct airoha_snand_ctrl *as_ctrl, u8 cmd, + const u8 *data, int len) +{ + int i = 0; + + while (i < len) { + int data_len = min(len, MAX_TRANSFER_SIZE); + int err; + + err = airoha_snand_set_fifo_op(as_ctrl, cmd, data_len); + if (err) + return err; + + err = airoha_snand_write_data_to_fifo(as_ctrl, &data[i], + data_len); + if (err < 0) + return err; + + i += data_len; + } + + return 0; +} + +static int airoha_snand_read_data(struct airoha_snand_ctrl *as_ctrl, u8 *data, + int len) +{ + int i = 0; + + while (i < len) { + int err, data_len = min(len, MAX_TRANSFER_SIZE); + + err = airoha_snand_set_fifo_op(as_ctrl, 0xc, data_len); + if (err) + return err; + + err = airoha_snand_read_data_from_fifo(as_ctrl, &data[i], + data_len); + if (err < 0) + return err; + + i += data_len; + } + + return 0; +} + +static int airoha_snand_nfi_init(struct airoha_snand_ctrl *as_ctrl) +{ + int err; + + /* switch to SNFI mode */ + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_NFI_CNFG, + SPI_NFI_SPI_MODE); + if (err) + return err; + + /* Enable DMA */ + return regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_INTR_EN, + SPI_NFI_ALL_IRQ_EN, SPI_NFI_AHB_DONE_EN); +} + +static int airoha_snand_nfi_config(struct airoha_snand_ctrl *as_ctrl) +{ + int err; + u32 val; + + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_FIFO_FLUSH | SPI_NFI_RST); + if (err) + return err; + + /* auto FDM */ + err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_AUTO_FDM_EN); + if (err) + return err; + + /* HW ECC */ + err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_HW_ECC_EN); + if (err) + return err; + + /* DMA Burst */ + err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_DMA_BURST_EN); + if (err) + return err; + + /* page format */ + switch (as_ctrl->nfi_cfg.spare_size) { + case 26: + val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x1); + break; + case 27: + val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x2); + break; + case 28: + val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x3); + break; + default: + val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x0); + break; + } + + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT, + SPI_NFI_SPARE_SIZE, val); + if (err) + return err; + + switch (as_ctrl->nfi_cfg.page_size) { + case 2048: + val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x1); + break; + case 4096: + val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x2); + break; + default: + val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x0); + break; + } + + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT, + SPI_NFI_PAGE_SIZE, val); + if (err) + return err; + + /* sec num */ + val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num); + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_SEC_NUM, val); + if (err) + return err; + + /* enable cust sec size */ + err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, + SPI_NFI_CUS_SEC_SIZE_EN); + if (err) + return err; + + /* set cust sec size */ + val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, as_ctrl->nfi_cfg.sec_size); + return regmap_update_bits(as_ctrl->regmap_nfi, + REG_SPI_NFI_SECCUS_SIZE, + SPI_NFI_CUS_SEC_SIZE, val); +} + +static bool airoha_snand_is_page_ops(const struct spi_mem_op *op) +{ + if (op->addr.nbytes != 2) + return false; + + if (op->addr.buswidth != 1 && op->addr.buswidth != 2 && + op->addr.buswidth != 4) + return false; + + switch (op->data.dir) { + case SPI_MEM_DATA_IN: + /* check dummy cycle first */ + if (op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth > 0xf) + return false; + + /* quad io / quad out */ + if ((op->addr.buswidth == 4 || op->addr.buswidth == 1) && + op->data.buswidth == 4) + return true; + + /* dual io / dual out */ + if ((op->addr.buswidth == 2 || op->addr.buswidth == 1) && + op->data.buswidth == 2) + return true; + + /* standard spi */ + if (op->addr.buswidth == 1 && op->data.buswidth == 1) + return true; + break; + case SPI_MEM_DATA_OUT: + /* check dummy cycle first */ + if (op->dummy.nbytes) + return false; + + /* program load quad out */ + if (op->addr.buswidth == 1 && op->data.buswidth == 4) + return true; + + /* standard spi */ + if (op->addr.buswidth == 1 && op->data.buswidth == 1) + return true; + default: + break; + } + + return false; +} + +static int airoha_snand_adjust_op_size(struct spi_mem *mem, + struct spi_mem_op *op) +{ + struct airoha_snand_ctrl *as_ctrl; + size_t len; + + as_ctrl = spi_controller_get_devdata(mem->spi->controller); + if (airoha_snand_is_page_ops(op)) { + len = as_ctrl->nfi_cfg.sec_size; + len += as_ctrl->nfi_cfg.spare_size; + len *= as_ctrl->nfi_cfg.sec_num; + op->data.nbytes = min_t(size_t, op->data.nbytes, len); + } else { + len = 1 + op->addr.nbytes + op->dummy.nbytes; + if (len >= 160) + return -EOPNOTSUPP; + + op->data.nbytes = min_t(size_t, op->data.nbytes, 160 - len); + } + + return 0; +} + +static bool airoha_snand_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + if (!spi_mem_default_supports_op(mem, op)) + return false; + + if (op->cmd.buswidth != 1) + return false; + + if (airoha_snand_is_page_ops(op)) + return true; + + return (!op->addr.nbytes || op->addr.buswidth == 1) && + (!op->dummy.nbytes || op->dummy.buswidth == 1) && + (!op->data.nbytes || op->data.buswidth == 1); +} + +static int airoha_snand_dirmap_create(struct spi_mem_dirmap_desc *desc) +{ + struct airoha_snand_dev *as_dev = spi_get_ctldata(desc->mem->spi); + + if (!as_dev->txrx_buf) + return -EINVAL; + + if (desc->info.offset + desc->info.length > U32_MAX) + return -EINVAL; + + if (!airoha_snand_supports_op(desc->mem, &desc->info.op_tmpl)) + return -EOPNOTSUPP; + + return 0; +} + +static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, void *buf) +{ + struct spi_device *spi = desc->mem->spi; + struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); + struct spi_mem_op *op = &desc->info.op_tmpl; + struct airoha_snand_ctrl *as_ctrl; + u32 val, rd_mode; + int err; + + if (!as_dev->data_need_update) + return len; + + as_dev->data_need_update = false; + + switch (op->cmd.opcode) { + case SPI_NAND_OP_READ_FROM_CACHE_DUAL: + rd_mode = 1; + break; + case SPI_NAND_OP_READ_FROM_CACHE_QUAD: + rd_mode = 2; + break; + default: + rd_mode = 0; + break; + } + + as_ctrl = spi_controller_get_devdata(spi->controller); + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA); + if (err < 0) + return err; + + err = airoha_snand_nfi_config(as_ctrl); + if (err) + return err; + + dma_sync_single_for_device(as_ctrl->dev, as_dev->dma_addr, + as_dev->buf_len, DMA_BIDIRECTIONAL); + mb(); + + /* set dma addr */ + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR, + as_dev->dma_addr); + if (err) + return err; + + /* set cust sec size */ + val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num; + val = FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, val); + err = regmap_update_bits(as_ctrl->regmap_nfi, + REG_SPI_NFI_SNF_MISC_CTL2, + SPI_NFI_READ_DATA_BYTE_NUM, val); + if (err) + return err; + + /* set read command */ + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL2, + op->cmd.opcode); + if (err) + return err; + + /* set read mode */ + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL, + FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, rd_mode)); + if (err) + return err; + + /* set read addr */ + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL3, 0x0); + if (err) + return err; + + /* set nfi read */ + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_OPMODE, + FIELD_PREP(SPI_NFI_OPMODE, 6)); + if (err) + return err; + + err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_READ_MODE | SPI_NFI_DMA_MODE); + if (err) + return err; + + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x0); + if (err) + return err; + + /* trigger dma start read */ + err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_RD_TRIG); + if (err) + return err; + + err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_RD_TRIG); + if (err) + return err; + + err = regmap_read_poll_timeout(as_ctrl->regmap_nfi, + REG_SPI_NFI_SNF_STA_CTL1, val, + (val & SPI_NFI_READ_FROM_CACHE_DONE), + 0, USEC_PER_SEC); + if (err) + return err; + + err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_STA_CTL1, + SPI_NFI_READ_FROM_CACHE_DONE); + if (err) + return err; + + err = regmap_read_poll_timeout(as_ctrl->regmap_nfi, REG_SPI_NFI_INTR, + val, (val & SPI_NFI_AHB_DONE), 0, + USEC_PER_SEC); + if (err) + return err; + + /* DMA read need delay for data ready from controller to DRAM */ + udelay(1); + + dma_sync_single_for_cpu(as_ctrl->dev, as_dev->dma_addr, + as_dev->buf_len, DMA_BIDIRECTIONAL); + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); + if (err < 0) + return err; + + memcpy(buf, as_dev->txrx_buf + offs, len); + + return len; +} + +static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, const void *buf) +{ + struct spi_device *spi = desc->mem->spi; + struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); + struct spi_mem_op *op = &desc->info.op_tmpl; + struct airoha_snand_ctrl *as_ctrl; + u32 wr_mode, val; + int err; + + as_ctrl = spi_controller_get_devdata(spi->controller); + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); + if (err < 0) + return err; + + dma_sync_single_for_cpu(as_ctrl->dev, as_dev->dma_addr, + as_dev->buf_len, DMA_BIDIRECTIONAL); + memcpy(as_dev->txrx_buf + offs, buf, len); + dma_sync_single_for_device(as_ctrl->dev, as_dev->dma_addr, + as_dev->buf_len, DMA_BIDIRECTIONAL); + mb(); + + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA); + if (err < 0) + return err; + + err = airoha_snand_nfi_config(as_ctrl); + if (err) + return err; + + if (op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_QUAD || + op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD) + wr_mode = 2; + else + wr_mode = 0; + + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR, + as_dev->dma_addr); + if (err) + return err; + + val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num; + val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, val); + err = regmap_update_bits(as_ctrl->regmap_nfi, + REG_SPI_NFI_SNF_MISC_CTL2, + SPI_NFI_PROG_LOAD_BYTE_NUM, val); + if (err) + return err; + + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL1, + FIELD_PREP(SPI_NFI_PG_LOAD_CMD, + op->cmd.opcode)); + if (err) + return err; + + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL, + FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, wr_mode)); + if (err) + return err; + + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL2, 0x0); + if (err) + return err; + + err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_READ_MODE); + if (err) + return err; + + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_OPMODE, + FIELD_PREP(SPI_NFI_OPMODE, 3)); + if (err) + return err; + + err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_DMA_MODE); + if (err) + return err; + + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x80); + if (err) + return err; + + err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_WR_TRIG); + if (err) + return err; + + err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_WR_TRIG); + if (err) + return err; + + err = regmap_read_poll_timeout(as_ctrl->regmap_nfi, REG_SPI_NFI_INTR, + val, (val & SPI_NFI_AHB_DONE), 0, + USEC_PER_SEC); + if (err) + return err; + + err = regmap_read_poll_timeout(as_ctrl->regmap_nfi, + REG_SPI_NFI_SNF_STA_CTL1, val, + (val & SPI_NFI_LOAD_TO_CACHE_DONE), + 0, USEC_PER_SEC); + if (err) + return err; + + err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_STA_CTL1, + SPI_NFI_LOAD_TO_CACHE_DONE); + if (err) + return err; + + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); + if (err < 0) + return err; + + return len; +} + +static int airoha_snand_exec_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct airoha_snand_dev *as_dev = spi_get_ctldata(mem->spi); + struct airoha_snand_ctrl *as_ctrl; + u8 data, opcode = op->cmd.opcode; + int i, err; + + as_ctrl = spi_controller_get_devdata(mem->spi->controller); + if (opcode == SPI_NAND_OP_PROGRAM_EXECUTE && + op->addr.val == as_dev->cur_page_num) { + as_dev->data_need_update = true; + } else if (opcode == SPI_NAND_OP_PAGE_READ) { + if (!as_dev->data_need_update && + op->addr.val == as_dev->cur_page_num) + return 0; + + as_dev->data_need_update = true; + as_dev->cur_page_num = op->addr.val; + } + + /* switch to manual mode */ + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); + if (err < 0) + return err; + + err = airoha_snand_set_cs(as_ctrl, SPI_CHIP_SEL_LOW); + if (err < 0) + return err; + + /* opcode */ + err = airoha_snand_write_data(as_ctrl, 0x8, &opcode, sizeof(opcode)); + if (err) + return err; + + /* addr part */ + for (i = 0; i < op->addr.nbytes; i++) { + u8 cmd = opcode == SPI_NAND_OP_GET_FEATURE ? 0x11 : 0x8; + + data = op->addr.val >> ((op->addr.nbytes - i - 1) * 8); + err = airoha_snand_write_data(as_ctrl, cmd, &data, + sizeof(data)); + if (err) + return err; + } + + /* dummy */ + for (i = 0; i < op->dummy.nbytes; i++) { + data = 0xff; + err = airoha_snand_write_data(as_ctrl, 0x8, &data, + sizeof(data)); + if (err) + return err; + } + + /* data */ + if (op->data.dir == SPI_MEM_DATA_IN) { + err = airoha_snand_read_data(as_ctrl, op->data.buf.in, + op->data.nbytes); + if (err) + return err; + } else { + err = airoha_snand_write_data(as_ctrl, 0x8, op->data.buf.out, + op->data.nbytes); + if (err) + return err; + } + + return airoha_snand_set_cs(as_ctrl, SPI_CHIP_SEL_HIGH); +} + +static const struct spi_controller_mem_ops airoha_snand_mem_ops = { + .adjust_op_size = airoha_snand_adjust_op_size, + .supports_op = airoha_snand_supports_op, + .exec_op = airoha_snand_exec_op, + .dirmap_create = airoha_snand_dirmap_create, + .dirmap_read = airoha_snand_dirmap_read, + .dirmap_write = airoha_snand_dirmap_write, +}; + +#define SPI_NAND_CACHE_SIZE (4096 + 256) + +static int airoha_snand_setup(struct spi_device *spi) +{ + struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); + struct airoha_snand_ctrl *as_ctrl; + + as_dev = kzalloc(sizeof(*as_dev), GFP_KERNEL); + if (!as_dev) + return -ENOMEM; + + spi_set_ctldata(spi, as_dev); + as_dev->data_need_update = true; + + /* prepare device buffer */ + as_dev->buf_len = SPI_NAND_CACHE_SIZE; + as_dev->txrx_buf = kzalloc(as_dev->buf_len, GFP_KERNEL); + if (!as_dev->txrx_buf) + goto error_dev_free; + + as_ctrl = spi_controller_get_devdata(spi->controller); + as_dev->dma_addr = dma_map_single(as_ctrl->dev, as_dev->txrx_buf, + as_dev->buf_len, DMA_BIDIRECTIONAL); + if (dma_mapping_error(as_ctrl->dev, as_dev->dma_addr)) + goto error_buf_free; + + return 0; + +error_buf_free: + kfree(as_dev->txrx_buf); +error_dev_free: + kfree(as_dev); + + return -EINVAL; +} + +static void airoha_snand_cleanup(struct spi_device *spi) +{ + struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); + struct airoha_snand_ctrl *as_ctrl; + + as_ctrl = spi_controller_get_devdata(spi->controller); + dma_unmap_single(as_ctrl->dev, as_dev->dma_addr, + as_dev->buf_len, DMA_BIDIRECTIONAL); + kfree(as_dev->txrx_buf); + + spi_set_ctldata(spi, NULL); +} + +static int airoha_snand_nfi_setup(struct airoha_snand_ctrl *as_ctrl) +{ + u32 val, sec_size, sec_num; + int err; + + err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, &val); + if (err) + return err; + + sec_num = FIELD_GET(SPI_NFI_SEC_NUM, val); + + err = regmap_read(as_ctrl->regmap_nfi, + REG_SPI_NFI_SECCUS_SIZE, &val); + if (err) + return err; + + sec_size = FIELD_GET(SPI_NFI_CUS_SEC_SIZE, val); + + /* init default value */ + as_ctrl->nfi_cfg.sec_size = sec_size; + as_ctrl->nfi_cfg.sec_num = sec_num; + as_ctrl->nfi_cfg.page_size = rounddown(sec_size * sec_num, 1024); + as_ctrl->nfi_cfg.spare_size = 16; + + err = airoha_snand_nfi_init(as_ctrl); + if (err) + return err; + + return airoha_snand_nfi_config(as_ctrl); +} + +static const struct regmap_config spi_ctrl_regmap_config = { + .name = "ctrl", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = REG_SPI_CTRL_NFI2SPI_EN, +}; + +static const struct regmap_config spi_nfi_regmap_config = { + .name = "nfi", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = REG_SPI_NFI_SNF_NFI_CNFG, +}; + +static const struct of_device_id airoha_snand_ids[] = { + { .compatible = "airoha,en7581-snand" }, + { } +}; +MODULE_DEVICE_TABLE(of, airoha_snand_ids); + +static int airoha_snand_probe(struct platform_device *pdev) +{ + struct airoha_snand_ctrl *as_ctrl; + struct spi_controller *ctrl; + struct resource *res; + void __iomem *base; + int err; + + ctrl = devm_spi_alloc_host(&pdev->dev, sizeof(*as_ctrl)); + if (!ctrl) + return -ENOMEM; + + as_ctrl = spi_controller_get_devdata(ctrl); + as_ctrl->dev = &pdev->dev; + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + + as_ctrl->regmap_ctrl = devm_regmap_init_mmio(&pdev->dev, base, + &spi_ctrl_regmap_config); + if (IS_ERR(as_ctrl->regmap_ctrl)) { + dev_err(&pdev->dev, "failed to init spi ctrl regmap: %ld\n", + PTR_ERR(as_ctrl->regmap_ctrl)); + return PTR_ERR(as_ctrl->regmap_ctrl); + } + + base = devm_platform_get_and_ioremap_resource(pdev, 1, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + + as_ctrl->regmap_nfi = devm_regmap_init_mmio(&pdev->dev, base, + &spi_nfi_regmap_config); + if (IS_ERR(as_ctrl->regmap_nfi)) { + dev_err(&pdev->dev, "failed to init spi nfi regmap: %ld\n", + PTR_ERR(as_ctrl->regmap_nfi)); + return PTR_ERR(as_ctrl->regmap_nfi); + } + + as_ctrl->spi_clk = devm_clk_get_enabled(&pdev->dev, "spi"); + if (IS_ERR(as_ctrl->spi_clk)) { + dev_err(&pdev->dev, "unable to get spi clk"); + return PTR_ERR(as_ctrl->spi_clk); + } + + err = dma_set_mask(as_ctrl->dev, DMA_BIT_MASK(32)); + if (err) + return err; + + ctrl->num_chipselect = 2; + ctrl->mem_ops = &airoha_snand_mem_ops; + ctrl->bits_per_word_mask = SPI_BPW_MASK(8); + ctrl->mode_bits = SPI_RX_DUAL; + ctrl->dev.of_node = pdev->dev.of_node; + ctrl->setup = airoha_snand_setup; + ctrl->cleanup = airoha_snand_cleanup; + + err = airoha_snand_nfi_setup(as_ctrl); + if (err) + return err; + + return devm_spi_register_controller(&pdev->dev, ctrl); +} + +static struct platform_driver airoha_snand_driver = { + .driver = { + .name = "airoha-spi", + .of_match_table = airoha_snand_ids, + }, + .probe = airoha_snand_probe, +}; + +module_platform_driver(airoha_snand_driver); + +MODULE_DESCRIPTION("Airoha SPI-NAND Flash Controller Driver"); +MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>"); +MODULE_AUTHOR("Ray Liu <ray.liu@airoha.com>"); +MODULE_LICENSE("GPL"); -- 2.44.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v3 3/3] spi: airoha: add SPI-NAND Flash controller driver 2024-04-23 10:16 ` [PATCH v3 3/3] spi: airoha: add SPI-NAND Flash controller driver Lorenzo Bianconi @ 2024-04-23 10:44 ` AngeloGioacchino Del Regno 2024-04-23 11:02 ` Lorenzo Bianconi 2024-04-23 23:51 ` Andy Shevchenko 1 sibling, 1 reply; 12+ messages in thread From: AngeloGioacchino Del Regno @ 2024-04-23 10:44 UTC (permalink / raw) To: Lorenzo Bianconi, linux-spi Cc: conor, broonie, lorenzo.bianconi83, linux-arm-kernel, robh+dt, krzysztof.kozlowski+dt, conor+dt, devicetree, nbd, john, dd, catalin.marinas, will, upstream Il 23/04/24 12:16, Lorenzo Bianconi ha scritto: > Introduce support for SPI-NAND driver of the Airoha NAND Flash Interface > found on Airoha ARM SoCs. > > Tested-by: Rajeev Kumar <Rajeev.Kumar@airoha.com> > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> > --- > MAINTAINERS | 9 + > drivers/spi/Kconfig | 10 + > drivers/spi/Makefile | 1 + > drivers/spi/spi-airoha-snfi.c | 1156 +++++++++++++++++++++++++++++++++ > 4 files changed, 1176 insertions(+) > create mode 100644 drivers/spi/spi-airoha-snfi.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index aa3b947fb080..ce9fac46f741 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -653,6 +653,15 @@ S: Supported > F: fs/aio.c > F: include/linux/*aio*.h > > +AIROHA SPI SNFI DRIVER > +M: Lorenzo Bianconi <lorenzo@kernel.org> > +M: Ray Liu <ray.liu@airoha.com> > +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) > +L: linux-spi@vger.kernel.org > +S: Maintained > +F: Documentation/devicetree/bindings/spi/airoha,en7581-snand.yaml > +F: drivers/spi/spi-airoha.c > + > AIRSPY MEDIA DRIVER > L: linux-media@vger.kernel.org > S: Orphan > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig > index bc7021da2fe9..6fa91775f334 100644 > --- a/drivers/spi/Kconfig > +++ b/drivers/spi/Kconfig > @@ -57,6 +57,16 @@ config SPI_MEM > > comment "SPI Master Controller Drivers" > > +config SPI_AIROHA_SNFI > + tristate "Airoha SPI NAND Flash Interface" > + depends on ARCH_AIROHA || COMPILE_TEST > + depends on SPI_MASTER > + select REGMAP_MMIO > + help > + This enables support for SPI-NAND mode on the Airoha NAND > + Flash Interface found on Airoha ARM SoCs. This controller > + is implemented as a SPI-MEM controller. > + > config SPI_ALTERA > tristate "Altera SPI Controller platform driver" > select SPI_ALTERA_CORE > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile > index 4ff8d725ba5e..e694254dec04 100644 > --- a/drivers/spi/Makefile > +++ b/drivers/spi/Makefile > @@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_SPIDEV) += spidev.o > obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o > > # SPI master controller drivers (bus) > +obj-$(CONFIG_SPI_AIROHA_SNFI) += spi-airoha-snfi.o > obj-$(CONFIG_SPI_ALTERA) += spi-altera-platform.o > obj-$(CONFIG_SPI_ALTERA_CORE) += spi-altera-core.o > obj-$(CONFIG_SPI_ALTERA_DFL) += spi-altera-dfl.o > diff --git a/drivers/spi/spi-airoha-snfi.c b/drivers/spi/spi-airoha-snfi.c > new file mode 100644 > index 000000000000..eacb32d6e28e > --- /dev/null > +++ b/drivers/spi/spi-airoha-snfi.c > @@ -0,0 +1,1156 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright (c) 2024 AIROHA Inc > + * Author: Lorenzo Bianconi <lorenzo@kernel.org> > + * Author: Ray Liu <ray.liu@airoha.com> > + */ > + ..snip... > +static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc, > + u64 offs, size_t len, void *buf) > +{ > + struct spi_device *spi = desc->mem->spi; > + struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); > + struct spi_mem_op *op = &desc->info.op_tmpl; > + struct airoha_snand_ctrl *as_ctrl; > + u32 val, rd_mode; > + int err; > + > + if (!as_dev->data_need_update) > + return len; > + > + as_dev->data_need_update = false; > + > + switch (op->cmd.opcode) { > + case SPI_NAND_OP_READ_FROM_CACHE_DUAL: > + rd_mode = 1; > + break; > + case SPI_NAND_OP_READ_FROM_CACHE_QUAD: > + rd_mode = 2; > + break; > + default: > + rd_mode = 0; > + break; > + } > + > + as_ctrl = spi_controller_get_devdata(spi->controller); > + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA); > + if (err < 0) > + return err; > + > + err = airoha_snand_nfi_config(as_ctrl); > + if (err) > + return err; > + > + dma_sync_single_for_device(as_ctrl->dev, as_dev->dma_addr, > + as_dev->buf_len, DMA_BIDIRECTIONAL); > + mb(); Are you sure you need this memory barrier here? P.S.: Just in case... any other write or read will anyway add a barrier ;-) > + > + /* set dma addr */ > + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR, > + as_dev->dma_addr); > + if (err) > + return err; > + ..snip.. > + > +static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc, > + u64 offs, size_t len, const void *buf) > +{ > + struct spi_device *spi = desc->mem->spi; > + struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); > + struct spi_mem_op *op = &desc->info.op_tmpl; > + struct airoha_snand_ctrl *as_ctrl; > + u32 wr_mode, val; > + int err; > + > + as_ctrl = spi_controller_get_devdata(spi->controller); > + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); > + if (err < 0) > + return err; > + > + dma_sync_single_for_cpu(as_ctrl->dev, as_dev->dma_addr, > + as_dev->buf_len, DMA_BIDIRECTIONAL); > + memcpy(as_dev->txrx_buf + offs, buf, len); > + dma_sync_single_for_device(as_ctrl->dev, as_dev->dma_addr, > + as_dev->buf_len, DMA_BIDIRECTIONAL); > + mb(); > + > + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA); > + if (err < 0) > + return err; > + > + err = airoha_snand_nfi_config(as_ctrl); > + if (err) > + return err; > + > + if (op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_QUAD || > + op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD) > + wr_mode = 2; I'm mostly sure that this '2' is supposed to be BIT(1) instead > + else > + wr_mode = 0; > + > + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR, > + as_dev->dma_addr); > + if (err) > + return err; > + > + val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num; > + val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, val); val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num); Being this 100 cols eventually, you wouldn't even need two lines - but if you don't want to go 100, it's still more readable (imo) like this, even if in two lines. > + err = regmap_update_bits(as_ctrl->regmap_nfi, > + REG_SPI_NFI_SNF_MISC_CTL2, > + SPI_NFI_PROG_LOAD_BYTE_NUM, val); > + if (err) > + return err; > + > + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL1, > + FIELD_PREP(SPI_NFI_PG_LOAD_CMD, > + op->cmd.opcode)); > + if (err) > + return err; > + > + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL, > + FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, wr_mode)); > + if (err) > + return err; > + ..snip.. > + > +#define SPI_NAND_CACHE_SIZE (4096 + 256) #include <linux/sizes.h> <--- at the beginning #define SPI_NAND_CACHE_SIZE (SZ_4K + SZ_256) ...there are macros for that, might as well use them, right? :-) > + > +static int airoha_snand_setup(struct spi_device *spi) > +{ > + struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); > + struct airoha_snand_ctrl *as_ctrl; > + > + as_dev = kzalloc(sizeof(*as_dev), GFP_KERNEL); > + if (!as_dev) > + return -ENOMEM; > + > + spi_set_ctldata(spi, as_dev); > + as_dev->data_need_update = true; > + > + /* prepare device buffer */ > + as_dev->buf_len = SPI_NAND_CACHE_SIZE; > + as_dev->txrx_buf = kzalloc(as_dev->buf_len, GFP_KERNEL); > + if (!as_dev->txrx_buf) > + goto error_dev_free; > + > + as_ctrl = spi_controller_get_devdata(spi->controller); > + as_dev->dma_addr = dma_map_single(as_ctrl->dev, as_dev->txrx_buf, > + as_dev->buf_len, DMA_BIDIRECTIONAL); > + if (dma_mapping_error(as_ctrl->dev, as_dev->dma_addr)) > + goto error_buf_free; > + > + return 0; > + > +error_buf_free: > + kfree(as_dev->txrx_buf); > +error_dev_free: > + kfree(as_dev); > + > + return -EINVAL; > +} > + > +static void airoha_snand_cleanup(struct spi_device *spi) > +{ > + struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); > + struct airoha_snand_ctrl *as_ctrl; > + > + as_ctrl = spi_controller_get_devdata(spi->controller); > + dma_unmap_single(as_ctrl->dev, as_dev->dma_addr, > + as_dev->buf_len, DMA_BIDIRECTIONAL); > + kfree(as_dev->txrx_buf); > + > + spi_set_ctldata(spi, NULL); > +} > + > +static int airoha_snand_nfi_setup(struct airoha_snand_ctrl *as_ctrl) > +{ > + u32 val, sec_size, sec_num; > + int err; > + > + err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, &val); > + if (err) > + return err; > + > + sec_num = FIELD_GET(SPI_NFI_SEC_NUM, val); > + > + err = regmap_read(as_ctrl->regmap_nfi, > + REG_SPI_NFI_SECCUS_SIZE, &val); > + if (err) > + return err; > + > + sec_size = FIELD_GET(SPI_NFI_CUS_SEC_SIZE, val); > + > + /* init default value */ > + as_ctrl->nfi_cfg.sec_size = sec_size; > + as_ctrl->nfi_cfg.sec_num = sec_num; > + as_ctrl->nfi_cfg.page_size = rounddown(sec_size * sec_num, 1024); > + as_ctrl->nfi_cfg.spare_size = 16; > + > + err = airoha_snand_nfi_init(as_ctrl); > + if (err) > + return err; > + > + return airoha_snand_nfi_config(as_ctrl); > +} > + > +static const struct regmap_config spi_ctrl_regmap_config = { > + .name = "ctrl", > + .reg_bits = 32, > + .val_bits = 32, > + .reg_stride = 4, > + .max_register = REG_SPI_CTRL_NFI2SPI_EN, > +}; > + > +static const struct regmap_config spi_nfi_regmap_config = { > + .name = "nfi", > + .reg_bits = 32, > + .val_bits = 32, > + .reg_stride = 4, > + .max_register = REG_SPI_NFI_SNF_NFI_CNFG, > +}; > + > +static const struct of_device_id airoha_snand_ids[] = { > + { .compatible = "airoha,en7581-snand" }, > + { } { /* sentinel */ } ...please! > +}; > +MODULE_DEVICE_TABLE(of, airoha_snand_ids); > + Cheers, Angelo ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 3/3] spi: airoha: add SPI-NAND Flash controller driver 2024-04-23 10:44 ` AngeloGioacchino Del Regno @ 2024-04-23 11:02 ` Lorenzo Bianconi 0 siblings, 0 replies; 12+ messages in thread From: Lorenzo Bianconi @ 2024-04-23 11:02 UTC (permalink / raw) To: AngeloGioacchino Del Regno Cc: linux-spi, conor, broonie, lorenzo.bianconi83, linux-arm-kernel, robh+dt, krzysztof.kozlowski+dt, conor+dt, devicetree, nbd, john, dd, catalin.marinas, will, upstream [-- Attachment #1: Type: text/plain, Size: 6654 bytes --] > Il 23/04/24 12:16, Lorenzo Bianconi ha scritto: [...] > > + > > + err = airoha_snand_nfi_config(as_ctrl); > > + if (err) > > + return err; > > + > > + dma_sync_single_for_device(as_ctrl->dev, as_dev->dma_addr, > > + as_dev->buf_len, DMA_BIDIRECTIONAL); > > + mb(); > > Are you sure you need this memory barrier here? > > P.S.: Just in case... any other write or read will anyway add a barrier ;-) > ack, I will fix it in v4. > > + > > + /* set dma addr */ > > + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR, > > + as_dev->dma_addr); > > + if (err) > > + return err; > > + > > ..snip.. > > > + > > +static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc, > > + u64 offs, size_t len, const void *buf) > > +{ > > + struct spi_device *spi = desc->mem->spi; > > + struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); > > + struct spi_mem_op *op = &desc->info.op_tmpl; > > + struct airoha_snand_ctrl *as_ctrl; > > + u32 wr_mode, val; > > + int err; > > + > > + as_ctrl = spi_controller_get_devdata(spi->controller); > > + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); > > + if (err < 0) > > + return err; > > + > > + dma_sync_single_for_cpu(as_ctrl->dev, as_dev->dma_addr, > > + as_dev->buf_len, DMA_BIDIRECTIONAL); > > + memcpy(as_dev->txrx_buf + offs, buf, len); > > + dma_sync_single_for_device(as_ctrl->dev, as_dev->dma_addr, > > + as_dev->buf_len, DMA_BIDIRECTIONAL); > > + mb(); > > + > > + err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA); > > + if (err < 0) > > + return err; > > + > > + err = airoha_snand_nfi_config(as_ctrl); > > + if (err) > > + return err; > > + > > + if (op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_QUAD || > > + op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD) > > + wr_mode = 2; > > I'm mostly sure that this '2' is supposed to be BIT(1) instead ack, I will fix it in v4. > > > + else > > + wr_mode = 0; > > + > > + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR, > > + as_dev->dma_addr); > > + if (err) > > + return err; > > + > > + val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num; > > + val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, val); > > val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, > as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num); > > Being this 100 cols eventually, you wouldn't even need two lines - but > if you don't want to go 100, it's still more readable (imo) like this, > even if in two lines. I prefer to be below 79 columns, so I will go for the 2 lines :) I will fix it in v4. > > > + err = regmap_update_bits(as_ctrl->regmap_nfi, > > + REG_SPI_NFI_SNF_MISC_CTL2, > > + SPI_NFI_PROG_LOAD_BYTE_NUM, val); > > + if (err) > > + return err; > > + > > + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL1, > > + FIELD_PREP(SPI_NFI_PG_LOAD_CMD, > > + op->cmd.opcode)); > > + if (err) > > + return err; > > + > > + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL, > > + FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, wr_mode)); > > + if (err) > > + return err; > > + > > ..snip.. > > > + > > +#define SPI_NAND_CACHE_SIZE (4096 + 256) > > #include <linux/sizes.h> <--- at the beginning > > #define SPI_NAND_CACHE_SIZE (SZ_4K + SZ_256) > > ...there are macros for that, might as well use them, right? :-) ack, I will fix it in v4. > > > + > > +static int airoha_snand_setup(struct spi_device *spi) > > +{ > > + struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); > > + struct airoha_snand_ctrl *as_ctrl; > > + > > + as_dev = kzalloc(sizeof(*as_dev), GFP_KERNEL); > > + if (!as_dev) > > + return -ENOMEM; > > + > > + spi_set_ctldata(spi, as_dev); > > + as_dev->data_need_update = true; > > + > > + /* prepare device buffer */ > > + as_dev->buf_len = SPI_NAND_CACHE_SIZE; > > + as_dev->txrx_buf = kzalloc(as_dev->buf_len, GFP_KERNEL); > > + if (!as_dev->txrx_buf) > > + goto error_dev_free; > > + > > + as_ctrl = spi_controller_get_devdata(spi->controller); > > + as_dev->dma_addr = dma_map_single(as_ctrl->dev, as_dev->txrx_buf, > > + as_dev->buf_len, DMA_BIDIRECTIONAL); > > + if (dma_mapping_error(as_ctrl->dev, as_dev->dma_addr)) > > + goto error_buf_free; > > + > > + return 0; > > + > > +error_buf_free: > > + kfree(as_dev->txrx_buf); > > +error_dev_free: > > + kfree(as_dev); > > + > > + return -EINVAL; > > +} > > + > > +static void airoha_snand_cleanup(struct spi_device *spi) > > +{ > > + struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); > > + struct airoha_snand_ctrl *as_ctrl; > > + > > + as_ctrl = spi_controller_get_devdata(spi->controller); > > + dma_unmap_single(as_ctrl->dev, as_dev->dma_addr, > > + as_dev->buf_len, DMA_BIDIRECTIONAL); > > + kfree(as_dev->txrx_buf); > > + > > + spi_set_ctldata(spi, NULL); > > +} > > + > > +static int airoha_snand_nfi_setup(struct airoha_snand_ctrl *as_ctrl) > > +{ > > + u32 val, sec_size, sec_num; > > + int err; > > + > > + err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, &val); > > + if (err) > > + return err; > > + > > + sec_num = FIELD_GET(SPI_NFI_SEC_NUM, val); > > + > > + err = regmap_read(as_ctrl->regmap_nfi, > > + REG_SPI_NFI_SECCUS_SIZE, &val); > > + if (err) > > + return err; > > + > > + sec_size = FIELD_GET(SPI_NFI_CUS_SEC_SIZE, val); > > + > > + /* init default value */ > > + as_ctrl->nfi_cfg.sec_size = sec_size; > > + as_ctrl->nfi_cfg.sec_num = sec_num; > > + as_ctrl->nfi_cfg.page_size = rounddown(sec_size * sec_num, 1024); > > + as_ctrl->nfi_cfg.spare_size = 16; > > + > > + err = airoha_snand_nfi_init(as_ctrl); > > + if (err) > > + return err; > > + > > + return airoha_snand_nfi_config(as_ctrl); > > +} > > + > > +static const struct regmap_config spi_ctrl_regmap_config = { > > + .name = "ctrl", > > + .reg_bits = 32, > > + .val_bits = 32, > > + .reg_stride = 4, > > + .max_register = REG_SPI_CTRL_NFI2SPI_EN, > > +}; > > + > > +static const struct regmap_config spi_nfi_regmap_config = { > > + .name = "nfi", > > + .reg_bits = 32, > > + .val_bits = 32, > > + .reg_stride = 4, > > + .max_register = REG_SPI_NFI_SNF_NFI_CNFG, > > +}; > > + > > +static const struct of_device_id airoha_snand_ids[] = { > > + { .compatible = "airoha,en7581-snand" }, > > + { } > > { /* sentinel */ } > > ...please! ack, I will fix it in v4. Regards, Lorenzo > > > +}; > > +MODULE_DEVICE_TABLE(of, airoha_snand_ids); > > + > > Cheers, > Angelo > > [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --] ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 3/3] spi: airoha: add SPI-NAND Flash controller driver 2024-04-23 10:16 ` [PATCH v3 3/3] spi: airoha: add SPI-NAND Flash controller driver Lorenzo Bianconi 2024-04-23 10:44 ` AngeloGioacchino Del Regno @ 2024-04-23 23:51 ` Andy Shevchenko 2024-04-24 10:06 ` Lorenzo Bianconi 1 sibling, 1 reply; 12+ messages in thread From: Andy Shevchenko @ 2024-04-23 23:51 UTC (permalink / raw) To: Lorenzo Bianconi Cc: linux-spi, conor, broonie, lorenzo.bianconi83, linux-arm-kernel, robh+dt, krzysztof.kozlowski+dt, conor+dt, devicetree, nbd, john, dd, catalin.marinas, will, upstream, angelogioacchino.delregno Tue, Apr 23, 2024 at 12:16:37PM +0200, Lorenzo Bianconi kirjoitti: > Introduce support for SPI-NAND driver of the Airoha NAND Flash Interface > found on Airoha ARM SoCs. ... > +#include <linux/bitfield.h> > +#include <linux/clk.h> > +#include <linux/device.h> > +#include <linux/dma-mapping.h> > +#include <linux/init.h> > +#include <linux/iopoll.h> > +#include <linux/kernel.h> Make sure you are using exact headers you need, this one seems "proxy" and not really in use here. (Quite likely you wanted minmax.h, types.h, and possible others.) > +#include <linux/module.h> > +#include <linux/mutex.h> > +#include <linux/platform_device.h> > +#include <linux/regmap.h> > +#include <linux/spi/spi.h> > +#include <linux/spi/spi-mem.h> ... > +#define SPI_NFI_ALL_IRQ_EN (SPI_NFI_RD_DONE_EN | \ > + SPI_NFI_WR_DONE_EN | \ > + SPI_NFI_RST_DONE_EN | \ > + SPI_NFI_ERASE_DONE_EN | \ > + SPI_NFI_BUSY_RETURN_EN | \ > + SPI_NFI_ACCESS_LOCK_EN | \ > + SPI_NFI_AHB_DONE_EN) What about writing this as #define SPI_NFI_ALL_IRQ_EN \ (SPI_NFI_RD_DONE_EN | SPI_NFI_WR_DONE_EN | \ SPI_NFI_RST_DONE_EN | SPI_NFI_ERASE_DONE_EN | \ SPI_NFI_BUSY_RETURN_EN | SPI_NFI_ACCESS_LOCK_EN | \ SPI_NFI_AHB_DONE_EN) ? ... > +enum airoha_snand_mode { > + SPI_MODE_AUTO, > + SPI_MODE_MANUAL, > + SPI_MODE_DMA, > + SPI_MODE_NO Is _NO a termination entry? Meaning there always be only 3 modes no matter what. If not, leave the trailing comma as it helps to reduce a burden in case this list will be expanded. > +}; ... > +struct airoha_snand_dev { > + size_t buf_len; > + > + u8 *txrx_buf; > + dma_addr_t dma_addr; > + > + bool data_need_update; > + u64 cur_page_num; > +}; Most likely `pahole` shows better layout to save a few bytes in some cases. ... > +struct airoha_snand_ctrl { > + struct device *dev; > + struct regmap *regmap_ctrl; > + struct regmap *regmap_nfi; > + struct clk *spi_clk; > + > + struct { > + size_t page_size; > + size_t sec_size; > + unsigned char sec_num; > + unsigned char spare_size; Hmm... Why not u8 for both of these? > + } nfi_cfg; > +}; ... > +static int airoha_snand_write_data(struct airoha_snand_ctrl *as_ctrl, u8 cmd, > + const u8 *data, int len) > +{ > + int i = 0; > + > + while (i < len) { Seems nothing prevents you from using for-loop here as well. > + int data_len = min(len, MAX_TRANSFER_SIZE); > + int err; > + > + err = airoha_snand_set_fifo_op(as_ctrl, cmd, data_len); > + if (err) > + return err; > + > + err = airoha_snand_write_data_to_fifo(as_ctrl, &data[i], > + data_len); > + if (err < 0) > + return err; > + > + i += data_len; > + } > + > + return 0; > +} ... > +static int airoha_snand_read_data(struct airoha_snand_ctrl *as_ctrl, u8 *data, > + int len) As per above. ... > + /* addr part */ > + for (i = 0; i < op->addr.nbytes; i++) { > + u8 cmd = opcode == SPI_NAND_OP_GET_FEATURE ? 0x11 : 0x8; > + > + data = op->addr.val >> ((op->addr.nbytes - i - 1) * 8); Seems like you wanted to have always the same endianess and hence can be done outside the loop via cpu_to_xxx()? > + err = airoha_snand_write_data(as_ctrl, cmd, &data, > + sizeof(data)); > + if (err) > + return err; > + } ... > +static int airoha_snand_setup(struct spi_device *spi) > +{ > + struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); > + struct airoha_snand_ctrl *as_ctrl; > + > + as_dev = kzalloc(sizeof(*as_dev), GFP_KERNEL); > + if (!as_dev) > + return -ENOMEM; > + > + spi_set_ctldata(spi, as_dev); > + as_dev->data_need_update = true; > + > + /* prepare device buffer */ > + as_dev->buf_len = SPI_NAND_CACHE_SIZE; > + as_dev->txrx_buf = kzalloc(as_dev->buf_len, GFP_KERNEL); > + if (!as_dev->txrx_buf) > + goto error_dev_free; > + > + as_ctrl = spi_controller_get_devdata(spi->controller); > + as_dev->dma_addr = dma_map_single(as_ctrl->dev, as_dev->txrx_buf, > + as_dev->buf_len, DMA_BIDIRECTIONAL); > + if (dma_mapping_error(as_ctrl->dev, as_dev->dma_addr)) > + goto error_buf_free; > + > + return 0; > + > +error_buf_free: > + kfree(as_dev->txrx_buf); > +error_dev_free: > + kfree(as_dev); Why not utilising cleanup.h? (__free(), no_free_ptr(), etc) > + return -EINVAL; > +} ... > + err = regmap_read(as_ctrl->regmap_nfi, > + REG_SPI_NFI_SECCUS_SIZE, &val); One line? > + if (err) > + return err; ... > + as_ctrl->nfi_cfg.page_size = rounddown(sec_size * sec_num, 1024); round_down() is optimised for power-of-2. You would need to include math.h IIRC. ... > + as_ctrl->regmap_ctrl = devm_regmap_init_mmio(&pdev->dev, base, > + &spi_ctrl_regmap_config); With help of struct device *dev = &pdev->dev; at the top of the function the entire code will become neater. > + if (IS_ERR(as_ctrl->regmap_ctrl)) { > + dev_err(&pdev->dev, "failed to init spi ctrl regmap: %ld\n", > + PTR_ERR(as_ctrl->regmap_ctrl)); > + return PTR_ERR(as_ctrl->regmap_ctrl); return dev_err_probe(...); > + } ... > + dev_err(&pdev->dev, "failed to init spi nfi regmap: %ld\n", > + PTR_ERR(as_ctrl->regmap_nfi)); > + return PTR_ERR(as_ctrl->regmap_nfi); return dev_err_probe(...); ... > + dev_err(&pdev->dev, "unable to get spi clk"); > + return PTR_ERR(as_ctrl->spi_clk); Ditto. ... > + Unneeded blank line. > +module_platform_driver(airoha_snand_driver); -- With Best Regards, Andy Shevchenko ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 3/3] spi: airoha: add SPI-NAND Flash controller driver 2024-04-23 23:51 ` Andy Shevchenko @ 2024-04-24 10:06 ` Lorenzo Bianconi 2024-04-24 12:51 ` Andy Shevchenko 0 siblings, 1 reply; 12+ messages in thread From: Lorenzo Bianconi @ 2024-04-24 10:06 UTC (permalink / raw) To: Andy Shevchenko Cc: linux-spi, conor, broonie, lorenzo.bianconi83, linux-arm-kernel, robh+dt, krzysztof.kozlowski+dt, conor+dt, devicetree, nbd, john, dd, catalin.marinas, will, upstream, angelogioacchino.delregno [-- Attachment #1: Type: text/plain, Size: 6683 bytes --] > Tue, Apr 23, 2024 at 12:16:37PM +0200, Lorenzo Bianconi kirjoitti: > > Introduce support for SPI-NAND driver of the Airoha NAND Flash Interface > > found on Airoha ARM SoCs. > > ... > > > +#include <linux/bitfield.h> > > +#include <linux/clk.h> > > +#include <linux/device.h> > > +#include <linux/dma-mapping.h> > > +#include <linux/init.h> > > +#include <linux/iopoll.h> > > > +#include <linux/kernel.h> > > Make sure you are using exact headers you need, this one seems "proxy" and not > really in use here. > > (Quite likely you wanted minmax.h, types.h, and possible others.) ack, I will fix it. > > > +#include <linux/module.h> > > +#include <linux/mutex.h> > > +#include <linux/platform_device.h> > > +#include <linux/regmap.h> > > +#include <linux/spi/spi.h> > > +#include <linux/spi/spi-mem.h> > > ... > > > +#define SPI_NFI_ALL_IRQ_EN (SPI_NFI_RD_DONE_EN | \ > > + SPI_NFI_WR_DONE_EN | \ > > + SPI_NFI_RST_DONE_EN | \ > > + SPI_NFI_ERASE_DONE_EN | \ > > + SPI_NFI_BUSY_RETURN_EN | \ > > + SPI_NFI_ACCESS_LOCK_EN | \ > > + SPI_NFI_AHB_DONE_EN) > > What about writing this as > > #define SPI_NFI_ALL_IRQ_EN \ > (SPI_NFI_RD_DONE_EN | SPI_NFI_WR_DONE_EN | \ > SPI_NFI_RST_DONE_EN | SPI_NFI_ERASE_DONE_EN | \ > SPI_NFI_BUSY_RETURN_EN | SPI_NFI_ACCESS_LOCK_EN | \ > SPI_NFI_AHB_DONE_EN) > > ? no strong opinion about it, I will fix it. > > ... > > > +enum airoha_snand_mode { > > + SPI_MODE_AUTO, > > + SPI_MODE_MANUAL, > > + SPI_MODE_DMA, > > + SPI_MODE_NO > > Is _NO a termination entry? Meaning there always be only 3 modes no matter > what. If not, leave the trailing comma as it helps to reduce a burden in case > this list will be expanded. I think we can get rid of it > > > +}; > > ... > > > +struct airoha_snand_dev { > > + size_t buf_len; > > + > > + u8 *txrx_buf; > > + dma_addr_t dma_addr; > > + > > + bool data_need_update; > > + u64 cur_page_num; > > +}; > > Most likely `pahole` shows better layout to save a few bytes in some cases. ack, I think we can swap data_need_update and cur_page_num. > > ... > > > +struct airoha_snand_ctrl { > > + struct device *dev; > > + struct regmap *regmap_ctrl; > > + struct regmap *regmap_nfi; > > + struct clk *spi_clk; > > + > > + struct { > > + size_t page_size; > > + size_t sec_size; > > > + unsigned char sec_num; > > + unsigned char spare_size; > > Hmm... Why not u8 for both of these? ack, I will fix it. > > > + } nfi_cfg; > > +}; > > ... > > > +static int airoha_snand_write_data(struct airoha_snand_ctrl *as_ctrl, u8 cmd, > > + const u8 *data, int len) > > +{ > > + int i = 0; > > + > > + while (i < len) { > > Seems nothing prevents you from using for-loop here as well. ack, I will fix it. > > > + int data_len = min(len, MAX_TRANSFER_SIZE); > > + int err; > > + > > + err = airoha_snand_set_fifo_op(as_ctrl, cmd, data_len); > > + if (err) > > + return err; > > + > > + err = airoha_snand_write_data_to_fifo(as_ctrl, &data[i], > > + data_len); > > + if (err < 0) > > + return err; > > + > > + i += data_len; > > + } > > + > > + return 0; > > +} > > ... > > > +static int airoha_snand_read_data(struct airoha_snand_ctrl *as_ctrl, u8 *data, > > + int len) > > As per above. ack, I will fix it. > > ... > > > + /* addr part */ > > + for (i = 0; i < op->addr.nbytes; i++) { > > + u8 cmd = opcode == SPI_NAND_OP_GET_FEATURE ? 0x11 : 0x8; > > + > > + data = op->addr.val >> ((op->addr.nbytes - i - 1) * 8); > > Seems like you wanted to have always the same endianess and hence can be done > outside the loop via cpu_to_xxx()? sorry, I did not get what you mean here, data value relies on the loop iteration. > > > + err = airoha_snand_write_data(as_ctrl, cmd, &data, > > + sizeof(data)); > > + if (err) > > + return err; > > + } > > ... > > > +static int airoha_snand_setup(struct spi_device *spi) > > +{ > > + struct airoha_snand_dev *as_dev = spi_get_ctldata(spi); > > + struct airoha_snand_ctrl *as_ctrl; > > + > > + as_dev = kzalloc(sizeof(*as_dev), GFP_KERNEL); > > + if (!as_dev) > > + return -ENOMEM; > > + > > + spi_set_ctldata(spi, as_dev); > > + as_dev->data_need_update = true; > > + > > + /* prepare device buffer */ > > + as_dev->buf_len = SPI_NAND_CACHE_SIZE; > > + as_dev->txrx_buf = kzalloc(as_dev->buf_len, GFP_KERNEL); > > + if (!as_dev->txrx_buf) > > + goto error_dev_free; > > + > > + as_ctrl = spi_controller_get_devdata(spi->controller); > > + as_dev->dma_addr = dma_map_single(as_ctrl->dev, as_dev->txrx_buf, > > + as_dev->buf_len, DMA_BIDIRECTIONAL); > > + if (dma_mapping_error(as_ctrl->dev, as_dev->dma_addr)) > > + goto error_buf_free; > > + > > + return 0; > > + > > +error_buf_free: > > + kfree(as_dev->txrx_buf); > > +error_dev_free: > > + kfree(as_dev); > > Why not utilising cleanup.h? (__free(), no_free_ptr(), etc) I guess we can just allocate as_dev and as_dev->txrx_buf with devm_kzalloc() here, agree? > > > + return -EINVAL; > > +} > > ... > > > + err = regmap_read(as_ctrl->regmap_nfi, > > + REG_SPI_NFI_SECCUS_SIZE, &val); > > One line? ack, I will fix it. > > > + if (err) > > + return err; > > ... > > > + as_ctrl->nfi_cfg.page_size = rounddown(sec_size * sec_num, 1024); > > round_down() is optimised for power-of-2. > You would need to include math.h IIRC. ack, I will fix it. > > ... > > > + as_ctrl->regmap_ctrl = devm_regmap_init_mmio(&pdev->dev, base, > > + &spi_ctrl_regmap_config); > > With help of > > struct device *dev = &pdev->dev; > > at the top of the function the entire code will become neater. ack, I will fix it. > > > + if (IS_ERR(as_ctrl->regmap_ctrl)) { > > + dev_err(&pdev->dev, "failed to init spi ctrl regmap: %ld\n", > > + PTR_ERR(as_ctrl->regmap_ctrl)); > > + return PTR_ERR(as_ctrl->regmap_ctrl); > > return dev_err_probe(...); > > > + } > > ... > > > + dev_err(&pdev->dev, "failed to init spi nfi regmap: %ld\n", > > + PTR_ERR(as_ctrl->regmap_nfi)); > > + return PTR_ERR(as_ctrl->regmap_nfi); > > return dev_err_probe(...); > > ... > > > + dev_err(&pdev->dev, "unable to get spi clk"); > > + return PTR_ERR(as_ctrl->spi_clk); > > Ditto. > > ... > > > + > > Unneeded blank line. ack, I will fix it. Regards, Lorenzo > > > +module_platform_driver(airoha_snand_driver); > > -- > With Best Regards, > Andy Shevchenko > > > [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --] ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 3/3] spi: airoha: add SPI-NAND Flash controller driver 2024-04-24 10:06 ` Lorenzo Bianconi @ 2024-04-24 12:51 ` Andy Shevchenko 2024-04-24 14:38 ` Lorenzo Bianconi 0 siblings, 1 reply; 12+ messages in thread From: Andy Shevchenko @ 2024-04-24 12:51 UTC (permalink / raw) To: Lorenzo Bianconi Cc: linux-spi, conor, broonie, lorenzo.bianconi83, linux-arm-kernel, robh+dt, krzysztof.kozlowski+dt, conor+dt, devicetree, nbd, john, dd, catalin.marinas, will, upstream, angelogioacchino.delregno On Wed, Apr 24, 2024 at 1:07 PM Lorenzo Bianconi <lorenzo@kernel.org> wrote: > > Tue, Apr 23, 2024 at 12:16:37PM +0200, Lorenzo Bianconi kirjoitti: ... > > > + /* addr part */ > > > + for (i = 0; i < op->addr.nbytes; i++) { > > > + u8 cmd = opcode == SPI_NAND_OP_GET_FEATURE ? 0x11 : 0x8; > > > + > > > + data = op->addr.val >> ((op->addr.nbytes - i - 1) * 8); > > > > Seems like you wanted to have always the same endianess and hence can be done > > outside the loop via cpu_to_xxx()? > > sorry, I did not get what you mean here, data value relies on the loop > iteration. u8 byte_stream[8]; u8 cmd; cmd = ...; // find what suits your case(s) put_unaligned_be64(op->addr.val, byte_stream); for-loop { err = _write_data(); ... } > > > + err = airoha_snand_write_data(as_ctrl, cmd, &data, > > > + sizeof(data)); > > > + if (err) > > > + return err; > > > + } ... > > Why not utilising cleanup.h? (__free(), no_free_ptr(), etc) > > I guess we can just allocate as_dev and as_dev->txrx_buf with devm_kzalloc() > here, agree? If this is a solely part of ->probe() stage, yes. -- With Best Regards, Andy Shevchenko ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 3/3] spi: airoha: add SPI-NAND Flash controller driver 2024-04-24 12:51 ` Andy Shevchenko @ 2024-04-24 14:38 ` Lorenzo Bianconi 0 siblings, 0 replies; 12+ messages in thread From: Lorenzo Bianconi @ 2024-04-24 14:38 UTC (permalink / raw) To: Andy Shevchenko Cc: linux-spi, conor, broonie, lorenzo.bianconi83, linux-arm-kernel, robh+dt, krzysztof.kozlowski+dt, conor+dt, devicetree, nbd, john, dd, catalin.marinas, will, upstream, angelogioacchino.delregno [-- Attachment #1: Type: text/plain, Size: 1449 bytes --] > On Wed, Apr 24, 2024 at 1:07 PM Lorenzo Bianconi <lorenzo@kernel.org> wrote: > > > Tue, Apr 23, 2024 at 12:16:37PM +0200, Lorenzo Bianconi kirjoitti: > > ... > > > > > + /* addr part */ > > > > + for (i = 0; i < op->addr.nbytes; i++) { > > > > + u8 cmd = opcode == SPI_NAND_OP_GET_FEATURE ? 0x11 : 0x8; > > > > + > > > > + data = op->addr.val >> ((op->addr.nbytes - i - 1) * 8); > > > > > > Seems like you wanted to have always the same endianess and hence can be done > > > outside the loop via cpu_to_xxx()? > > > > sorry, I did not get what you mean here, data value relies on the loop > > iteration. > > u8 byte_stream[8]; > u8 cmd; > > cmd = ...; > > // find what suits your case(s) > put_unaligned_be64(op->addr.val, byte_stream); > for-loop { > err = _write_data(); > ... > } ack, I will fix it in v4. Rregards, Lorenzo > > > > > > + err = airoha_snand_write_data(as_ctrl, cmd, &data, > > > > + sizeof(data)); > > > > + if (err) > > > > + return err; > > > > + } > > ... > > > > Why not utilising cleanup.h? (__free(), no_free_ptr(), etc) > > > > I guess we can just allocate as_dev and as_dev->txrx_buf with devm_kzalloc() > > here, agree? > > If this is a solely part of ->probe() stage, yes. > > -- > With Best Regards, > Andy Shevchenko [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --] ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2024-04-24 14:38 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-04-23 10:16 [PATCH v3 0/3] Add add SPI-NAND Flash controller driver for EN7581 Lorenzo Bianconi 2024-04-23 10:16 ` [PATCH v3 1/3] dt-bindings: spi: airoha: Add YAML schema for SNFI controller Lorenzo Bianconi 2024-04-23 10:44 ` AngeloGioacchino Del Regno 2024-04-23 10:16 ` [PATCH v3 2/3] arm64: dts: airoha: add EN7581 spi-nand node Lorenzo Bianconi 2024-04-23 10:44 ` AngeloGioacchino Del Regno 2024-04-23 10:16 ` [PATCH v3 3/3] spi: airoha: add SPI-NAND Flash controller driver Lorenzo Bianconi 2024-04-23 10:44 ` AngeloGioacchino Del Regno 2024-04-23 11:02 ` Lorenzo Bianconi 2024-04-23 23:51 ` Andy Shevchenko 2024-04-24 10:06 ` Lorenzo Bianconi 2024-04-24 12:51 ` Andy Shevchenko 2024-04-24 14:38 ` Lorenzo Bianconi
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).