From patchwork Thu Apr 11 22:36:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg Malysa X-Patchwork-Id: 1922732 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=TfIZVsrs; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VFvjv3Fsmz1yYL for ; Fri, 12 Apr 2024 08:38:27 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id DE4AA8821B; Fri, 12 Apr 2024 00:37:50 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.b="TfIZVsrs"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id C33F68758B; Fri, 12 Apr 2024 00:37:46 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.2 Received: from mail-yw1-x112a.google.com (mail-yw1-x112a.google.com [IPv6:2607:f8b0:4864:20::112a]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 2E4B8881ED for ; Fri, 12 Apr 2024 00:37:42 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=greg.malysa@timesys.com Received: by mail-yw1-x112a.google.com with SMTP id 00721157ae682-61428e80f0cso2391057b3.2 for ; Thu, 11 Apr 2024 15:37:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=timesys-com.20230601.gappssmtp.com; s=20230601; t=1712875061; x=1713479861; darn=lists.denx.de; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=VC5QgJvEMwSHV+2NgM4n/T0M53mzLRV8tgNTWTyL2+8=; b=TfIZVsrsL7dZbt7wPmmz55LUhoP3zU3nuhqJSJF2/nz+Dx70Q2yE8DXzRl45TAR4Oo Zg1sD4W5mWUKjqgBbLL3Pkm+Yz75J05s/dPpaj4RBHwCbtZMI4rbR0NKpEVJDh8idnP1 5Mcv3VBBELN7BUS+IlQPajm1y/RU+JBtAKUzzG4fAvCIJ/9T8qmVN1MosUXwlM/iNBU5 MhowHM1K/E6MrYJx938zPhM9CIP1Oc9cH0K5W7U7WtN6OlwY3DFauqM5dSBIz7c06rL2 bvJylgzjydHg2hvRtncBMjxtn1584nd417uGvpOkOyGRWT8eY58UV8SHhVIk7IM/zHtt cCnA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712875061; x=1713479861; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=VC5QgJvEMwSHV+2NgM4n/T0M53mzLRV8tgNTWTyL2+8=; b=kQQPuW0bXM/efuODx1KKu3nw1BRDwmjNGrwFPiLYFooxo7lTwuoduxfNz+vjldesqa CfDRXBOpKJBqlVWNsBrzpr9/mtpAmQEM4YHJZ97CmizInXSMWH/yfIuIXTWxg9zO2kvI lr+ZXURNATilvZuO0A4cftZR42I/LLfl0PxH+fmKUzc8nT3TN4sNY8x4CSQfzCGjiny7 SUNHSJl1YEsSyOGDQuaABHpv/RwIMZ0pMEceW5RHLF0Pm76KqZNShho2pnFgLz5JVTb0 QhEDcLZyP//1/LJva341gzHC5WmchOcyimZicgmrFaHHeLQbaH4RUQn8yq7/jfHo7nbo gG1g== X-Gm-Message-State: AOJu0Ywg9Cedy04Qlbe2NV4Lwzrwx0Ltf+b4kGJyFmpZIM7jH6LWcMfc pIWx748lwxmEV71FGvIE4wn4E6pnREm7oC31voF/XYX7zpnE901R7sW6YQdEiM9ibphXmOcyFst jxA== X-Google-Smtp-Source: AGHT+IEgdPMNYg5eJGI94hu/ft6hWgHOjnI3HBLdwCR1v6MR0LocLrgnkfEkhsL6JAjHGpLAuEU3JQ== X-Received: by 2002:a25:e081:0:b0:db9:9134:bb28 with SMTP id x123-20020a25e081000000b00db99134bb28mr1018117ybg.4.1712875060031; Thu, 11 Apr 2024 15:37:40 -0700 (PDT) Received: from executor.attlocal.net ([2600:1700:5eb5:1ba0:dc1f:cff:fef9:435b]) by smtp.gmail.com with ESMTPSA id q11-20020a25f90b000000b00dcbbea79ffcsm482744ybe.42.2024.04.11.15.37.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Apr 2024 15:37:39 -0700 (PDT) From: Greg Malysa To: u-boot@lists.denx.de Cc: Arturs.Artamonovs@analog.com, Vasileios.Bimpikas@analog.com, Utsav.Agarwal@analog.com, Ian Roberts , Nathan Barrett-Morrison , Greg Malysa , Andre Przywara , Angelo Dureghello , Dhruva Gole , Francesco Dolcini , Heinrich Schuchardt , Jagan Teki , Jonas Karlman , Marek Vasut , Max Krummenacher , Michal Simek , Simon Glass , Svyatoslav Ryhel , Takahiro Kuwano , Tom Rini , Vaishnav Achath , Venkatesh Yadav Abbarapu , Vignesh R Subject: [PATCH 01/11] mtd: spi-nor: Add calibration hook for high speed SPI Date: Thu, 11 Apr 2024 18:36:45 -0400 Message-ID: <20240411223709.573-2-greg.malysa@timesys.com> X-Mailer: git-send-email 2.43.2 In-Reply-To: <20240411223709.573-1-greg.malysa@timesys.com> References: <20240411223709.573-1-greg.malysa@timesys.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean From: Ian Roberts High speed SPI flash chip operation, such as speeds greater than 50MHz, require a calibration of the data lines to determine the correct signal propagation delay. This calibration delay will vary based on flash chip, operating frequency, board trace length, and board temperature to a smaller extent. To my knowledge, JEDEC doesn't have a well defined standard for how calibration should be implemented by flash chip manufacturers. The few flash chip datasheets I have viewed from chips that do support such high speed operation implement very different methods for assisting in calibration, such as: * No provision for calibration. * Independent "data learning" commands. * Scratch data registers. * Predefined bit patterns inserted before read data contents (preamble). Thus, the simplest and most portable solution to calibrate appears to be to simply read and compare a known data pattern from the flash array. During SPI flash probe, calibration is initialized after the SFDP read, but before read command selection. At this stage, higher speeds, more advanced read commands, and additional IO lanes, and dual data rate options have not yet been enabled. Communication is most reliable at this stage to read out a pattern to then check against later. The most basic and widely supported read commands are then used to read out the data pattern. The default is to read 2 flash pages worth of data at the first address in flash. Commonly, this is where a bootloader might be located on the chip. While a bootloader is not an ideal pattern, two pages worth of data hopefully contains enough entropy to calibrate successfully. This can be further customized with two new flash chip device tree parameters: * 'calibration-offset' The flash chip address offset where the pattern data is located. * 'calibration-length' The length of pattern data. The calibration step is then called at the end of the chip probe, right after all advanced IO modes have been enabled, such as multiple IO lanes and SDR/DDR modes. Another solution to the high speed calibration issue is to perform the calibration offline and then to simply apply the calibration at boot. This skips a potentially lengthy calibration process. However, this change set also serves as an ideal hook for controller drivers to also simply apply any pre-calibrated values. Early commands in the probe process, such as RDID, RDSFDP, and RDAY may have slower operating frequencies. As, for example, the JEDEC spec only requires that RDSFDP and RDAY to support at least up to 50MHz frequency. Thus it is ideal to probe the chip at lower frequencies with more reliable IO modes, such as a low frequency with one lane at a single data rate. This patch implements: * SPI-mem API function for drivers to implement to perform calibration. * Read pattern data through reliable methods. * Call SPI-mem API calibration function with a read check function that uses probed read commands and all enabled fast IO modes. Co-developed-by: Nathan Barrett-Morrison Signed-off-by: Nathan Barrett-Morrison Signed-off-by: Greg Malysa Signed-off-by: Ian Roberts --- --- doc/device-tree-bindings/spi/spi-bus.txt | 4 + drivers/mtd/spi/Kconfig | 12 ++ drivers/mtd/spi/spi-nor-core.c | 168 +++++++++++++++++++++++ drivers/spi/spi-mem.c | 24 ++++ include/linux/mtd/spi-nor.h | 7 + include/spi-mem.h | 19 +++ 6 files changed, 234 insertions(+) diff --git a/doc/device-tree-bindings/spi/spi-bus.txt b/doc/device-tree-bindings/spi/spi-bus.txt index e57897ac0c..654d388cac 100644 --- a/doc/device-tree-bindings/spi/spi-bus.txt +++ b/doc/device-tree-bindings/spi/spi-bus.txt @@ -61,6 +61,10 @@ contain the following properties. used for MISO. Defaults to 1 if not present. - spi-half-duplex - (optional) Indicates that the SPI bus should wait for a header byte before reading data from the slave. +- calibration-offset - (optional) Check pattern offset location for high- + speed calibration. +- calibration-length - (optional) Check pattern length for high-speed + calibration. Some SPI controllers and devices support Dual and Quad SPI transfer mode. It allows data in SPI system transferred in 2 wires(DUAL) or 4 wires(QUAD). diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig index d068b7860e..ed0335d9ba 100644 --- a/drivers/mtd/spi/Kconfig +++ b/drivers/mtd/spi/Kconfig @@ -127,6 +127,18 @@ config SPI_FLASH_SOFT_RESET_ON_BOOT that are not supposed to be handed to U-Boot in Octal DTR mode, even if they _do_ support the Soft Reset sequence. +config SPI_FLASH_HS_CALIB + bool "Support for high-speed SPI flash calibration" + default n + help + Modern flash chips and controllers that operate at speeds higher than + 50MHz require delays in the signal lines to be calibrated. This + feature adds a spi-mem API hook to allow controller drivers to + perform the calibration during chip probe. + Select N here if SPI flash chips or controllers on your platform + operate roughly around or less than 50MHz, or if the controller driver + doesn't implement the calibration hook. + config SPI_FLASH_BAR bool "SPI flash Bank/Extended address register support" help diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index f86003ca8c..f164c3cf73 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -3230,6 +3230,152 @@ static int spi_nor_select_erase(struct spi_nor *nor, return 0; } +#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB) +/** + * Very basic check to make sure every bit changes in the reference pattern + * and that there aren't too many duplicate bytes in a row, such as all FF's + * or all zeros. + * Returns boolean. + */ +static int spi_nor_spimem_validate_calib_data(u8 *buff, size_t len) +{ + size_t i; + u8 prev; + u8 changed_bits = 0; + size_t changing = 0; + + if (!buff || !len) + return 0; + + prev = *buff; + for (i = 1; i < len; ++i) { + u8 changed = prev ^ buff[i]; + + changed_bits |= changed; + if (changed) + ++changing; + prev = buff[i]; + } + return (changing > (len / 2)) && (changed_bits == 0xff); +} + +/** Returns 0 on success, negative error code on error. + */ +static int spi_nor_spimem_calib_read_chk(struct spi_slave *slave) +{ + struct spi_nor *nor = dev_get_uclass_priv(slave->dev); + + ssize_t readval = nor->read(nor, nor->calib_off, + nor->calib_size, + nor->calib_buff); + if (readval != nor->calib_size) { + if (readval >= 0) + return -EIO; + return readval; + } + return (memcmp(nor->calib_ref_buff, + nor->calib_buff, nor->calib_size) != 0); +} + +static int spi_nor_spimem_calib(struct spi_nor *nor) +{ + int retval = 0; + + if (!nor->calib_buff || !nor->calib_ref_buff) + return retval; + + retval = spi_mem_calibrate(nor->spi, spi_nor_spimem_calib_read_chk); + + devm_kfree(nor->dev, nor->calib_buff); + nor->calib_buff = NULL; + devm_kfree(nor->dev, nor->calib_ref_buff); + nor->calib_ref_buff = NULL; + + return retval; +} + +static +int spi_nor_spimem_calib_init(struct spi_nor *nor, + const struct spi_nor_flash_parameter *params, + u32 shared_caps_mask) +{ + int err; + ssize_t readval; + ofnode np = dev_ofnode(nor->spi->dev); + u8 old_addr_width = nor->addr_width; + + if (!spi_mem_has_calibrate(nor->spi)) + return 0; + + /* Select a supported 1x read command to obtain calibration + * reference data. + */ + shared_caps_mask &= (SNOR_HWCAPS_READ | SNOR_HWCAPS_READ_FAST); + err = spi_nor_select_read(nor, params, shared_caps_mask); + if (err) { + dev_warn(nor->dev, + "Skipping SPI-nor calibration: Can't select 1x read settings supported by both the controller and memory.\n"); + return 0; + } + + /* Default to read the first 2 pages of the flash. Usually, this + * would contain a bootloader image. While this isn't an ideal + * calibration pattern, it's hopefully good enough to get something + * working most of the time. + */ + ofnode_read_u32(np, "calibration-offset", &nor->calib_off); + ofnode_read_u32(np, "calibration-length", &nor->calib_size); + if (!nor->calib_size) + nor->calib_size = 2 * nor->page_size; + + if (nor->addr_width == 4 || + ((nor->calib_off + nor->calib_size) > SZ_16M)) { + /* Only support a >16MB calibration address if the flash chip + * supports 4 byte commands. Otherwise, it would require + * enabling 4 byte mode. This isn't handled until later, in + * spi_nor_init() + */ + if (!(nor->flags & SPI_NOR_4B_OPCODES)) { + dev_warn(nor->dev, + "Skipping SPI-nor calibration: calibration currently only supports 3 byte addressing.\n"); + return 0; + } + nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode); + nor->addr_width = 4; + } else { + nor->addr_width = 3; + } + + nor->calib_ref_buff = devm_kmalloc(nor->dev, nor->calib_size, + GFP_KERNEL); + nor->calib_buff = devm_kmalloc(nor->dev, nor->calib_size, GFP_KERNEL); + if (!nor->calib_ref_buff || !nor->calib_buff) + return -ENOMEM; + + readval = nor->read(nor, nor->calib_off, + nor->calib_size, nor->calib_ref_buff); + if (readval < 0) + return readval; + else if (nor->calib_size != readval) + return -EIO; + + if (!spi_nor_spimem_validate_calib_data(nor->calib_ref_buff, + nor->calib_size)) { + devm_kfree(nor->dev, nor->calib_ref_buff); + devm_kfree(nor->dev, nor->calib_buff); + nor->calib_ref_buff = NULL; + nor->calib_buff = NULL; + dev_warn(nor->dev, + "Skipping SPI-nor calibration: Calibration data has poor entropy.\n"); + } + + /* Restore settings. */ + nor->addr_width = old_addr_width; + + return 0; +} +#endif + static int spi_nor_default_setup(struct spi_nor *nor, const struct flash_info *info, const struct spi_nor_flash_parameter *params) @@ -3240,6 +3386,12 @@ static int spi_nor_default_setup(struct spi_nor *nor, spi_nor_adjust_hwcaps(nor, params, &shared_mask); +#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB) + err = spi_nor_spimem_calib_init(nor, params, shared_mask); + if (err) + return err; +#endif + /* Select the (Fast) Read command. */ err = spi_nor_select_read(nor, params, shared_mask); if (err) { @@ -3892,6 +4044,14 @@ static int spi_nor_init(struct spi_nor *nor) set_4byte(nor, nor->info, 1); } +#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB) + err = spi_nor_spimem_calib(nor); + if (err) { + dev_err(nor->dev, "SPI calibration failed\n"); + return err; + } +#endif + return 0; } @@ -4057,6 +4217,14 @@ int spi_nor_scan(struct spi_nor *nor) spi_nor_soft_reset(nor); #endif /* CONFIG_SPI_FLASH_SOFT_RESET_ON_BOOT */ +#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB) + if (spi_mem_has_calibrate(nor->spi)) { + ret = spi_mem_calibrate(nor->spi, NULL); + if (ret) + return ret; + } +#endif + info = spi_nor_read_id(nor); if (IS_ERR_OR_NULL(info)) return -ENOENT; diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index b7eca58359..03ede22216 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -763,6 +763,30 @@ ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc, } EXPORT_SYMBOL_GPL(spi_mem_dirmap_write); +#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB) +int spi_mem_has_calibrate(struct spi_slave *slave) +{ + struct udevice *bus = slave->dev->parent; + struct dm_spi_ops *ops = spi_get_ops(bus); + + return (ops->mem_ops && ops->mem_ops->calibrate); +} +EXPORT_SYMBOL_GPL(spi_mem_has_calibrate); + +int spi_mem_calibrate(struct spi_slave *slave, + int (*calib_chk_fn)(struct spi_slave *)) +{ + struct udevice *bus = slave->dev->parent; + struct dm_spi_ops *ops = spi_get_ops(bus); + + if (spi_mem_has_calibrate(slave)) + return ops->mem_ops->calibrate(slave, calib_chk_fn); + + return -ENOSYS; +} +EXPORT_SYMBOL_GPL(spi_mem_calibrate); +#endif + #ifndef __UBOOT__ static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv) { diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index d1dbf3eadb..2eddb52392 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -593,6 +593,13 @@ struct spi_nor { u32 size; u32 sector_size; u32 erase_size; + +#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB) + u8 *calib_ref_buff; + u8 *calib_buff; + u32 calib_size; + u32 calib_off; +#endif }; #ifndef __UBOOT__ diff --git a/include/spi-mem.h b/include/spi-mem.h index 3c8e95b6f5..3db0f7e11c 100644 --- a/include/spi-mem.h +++ b/include/spi-mem.h @@ -243,6 +243,15 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem) * the currently mapped area), and the caller of * spi_mem_dirmap_write() is responsible for calling it again in * this case. + * @calibrate: instruct driver to perform or clear high speed calibration for + * the memory device. The provided function should be used by the + * driver to check if a calibration configuration is valid. May be + * called multiple times as needed. Do note that calling the check + * function may call back into the other ops in this interface. + * A null calib_chk_fn should clear any existing calibration. + * calib_chk_fn is expected to return 0 on success, negative error + * codes, and a positive value on failure. + * The same return value expectation for the calibrate function. * * This interface should be implemented by SPI controllers providing an * high-level interface to execute SPI memory operation, which is usually the @@ -266,6 +275,10 @@ struct spi_controller_mem_ops { u64 offs, size_t len, void *buf); ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc, u64 offs, size_t len, const void *buf); +#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB) + int (*calibrate)(struct spi_slave *slave, + int (*calib_chk_fn)(struct spi_slave *)); +#endif }; #ifndef __UBOOT__ @@ -341,6 +354,12 @@ ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc, ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc, u64 offs, size_t len, const void *buf); +#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB) +int spi_mem_has_calibrate(struct spi_slave *slave); +int spi_mem_calibrate(struct spi_slave *slave, + int (*calib_chk_fn)(struct spi_slave *)); +#endif + #ifndef __UBOOT__ int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv, struct module *owner); From patchwork Thu Apr 11 22:36:46 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg Malysa X-Patchwork-Id: 1922728 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=TimMT7ed; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VFvj726Jlz1yYL for ; Fri, 12 Apr 2024 08:37:47 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id B599F881EF; Fri, 12 Apr 2024 00:37:44 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.b="TimMT7ed"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 54C1E88207; Fri, 12 Apr 2024 00:37:44 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-yb1-xb36.google.com (mail-yb1-xb36.google.com [IPv6:2607:f8b0:4864:20::b36]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 105AC87B82 for ; Fri, 12 Apr 2024 00:37:42 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=greg.malysa@timesys.com Received: by mail-yb1-xb36.google.com with SMTP id 3f1490d57ef6-d9b9adaf291so321035276.1 for ; Thu, 11 Apr 2024 15:37:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=timesys-com.20230601.gappssmtp.com; s=20230601; t=1712875061; x=1713479861; darn=lists.denx.de; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=GwVzG40rMp+XeKrnbrR6xYabuH0Dhlxm6zgMOLWXDu4=; b=TimMT7edjuOy4UgWGmkhyr6sEiADHM2Lkid1JrgvC20XWS7W3YSV5V4Xnp2eT1MTi5 Cqk99jeE6TrRMnTxLatb0tgm5u9h9vXZRfDiU1p7moYMGQBx5ZvaRI8BCad/+p7Kj7kI H4Rv0mX6GicbNfm5MXZu75VX9hooGVOGkP4eefBSNNiJpd4o/ibmGE1S/4EZTBqVQusF 2limNF8RAyu+IonzHskdX43+uKvGr1YbPS/TEJpHP58Gk4n4mCtOIAdOFtWbZEHsyCFN jGjKKQJXamOW9UpWSvLx2KQCqV2ot+tvovRFpyPxx6hfFqW/n8Dukc3rvVasHAUdJoBr Rp0Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712875061; x=1713479861; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=GwVzG40rMp+XeKrnbrR6xYabuH0Dhlxm6zgMOLWXDu4=; b=grCFUciSMQZz4mWCeo5KvaF9tEc53uIbj2JPx1lpSaKf8gCOxKJAaFNyZVIXbuVzlU cLXVph0j3dQgIvBpxBubVHvoE90otno2Zm/PgI5Aj25SOcuWoqjxLIlHBVfvb4HyH2WL qmBEmAAV+AFOuHaS1xG6a/3qrfNecsTSafFMiUP3QIHjwtdskaNVpODaHcL8dAT1BaNN 3gUIbS6xmgLJA+u//JH2LB1YLCBW7yxR90BMo3nPALerYmBa+pMdgHYQvnRejkwRnb3A 449ozJmnTzXaYTr8rHIsE5HTjEjSLTCF4Qsnbx7uVKj5vNWOVNENSo4TliJrcABvbNQH BiJg== X-Gm-Message-State: AOJu0YymMUswQdJBvijBD4b5Y714BxnYlLv66NKtWCtwRFPnGgN1bpqR yCgGTtBFFl/UzAE0NCUD5gbYddcOU3W302TXicIijNm+WmmOC8J+G6FTOsSA3/L4h7OvziXSZQ0 /dw== X-Google-Smtp-Source: AGHT+IF7duu9K9kKg+riy3/yNczOYf9jbufUkataBjSyrA7nldxv7WzLG4kf/AdRbEWs69sECG+g2A== X-Received: by 2002:a25:fc12:0:b0:dc2:279f:f7e with SMTP id v18-20020a25fc12000000b00dc2279f0f7emr966422ybd.10.1712875060758; Thu, 11 Apr 2024 15:37:40 -0700 (PDT) Received: from executor.attlocal.net ([2600:1700:5eb5:1ba0:dc1f:cff:fef9:435b]) by smtp.gmail.com with ESMTPSA id q11-20020a25f90b000000b00dcbbea79ffcsm482744ybe.42.2024.04.11.15.37.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Apr 2024 15:37:40 -0700 (PDT) From: Greg Malysa To: u-boot@lists.denx.de Cc: Arturs.Artamonovs@analog.com, Vasileios.Bimpikas@analog.com, Utsav.Agarwal@analog.com, Ian Roberts , Nathan Barrett-Morrison , Greg Malysa , Andre Przywara , Bruce Suen , Frieder Schrempf , Heinrich Schuchardt , Jagan Teki , Jim Liu , Jit Loon Lim , Jonas Karlman , Marek Vasut , Neal Frager , Nishanth Menon , Takahiro Kuwano , Tom Rini , Vaishnav Achath , Venkatesh Yadav Abbarapu , Vignesh R Subject: [PATCH 02/11] mtd: spi-nor: Octal DTR support for IS25*x Date: Thu, 11 Apr 2024 18:36:46 -0400 Message-ID: <20240411223709.573-3-greg.malysa@timesys.com> X-Mailer: git-send-email 2.43.2 In-Reply-To: <20240411223709.573-1-greg.malysa@timesys.com> References: <20240411223709.573-1-greg.malysa@timesys.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean From: Ian Roberts ISSI IS25*x series SPIflash chips are capable of Octal IO and DDR. Add spi-nor support to enable and operate in these modes. Co-developed-by: Nathan Barrett-Morrison Signed-off-by: Nathan Barrett-Morrison Signed-off-by: Greg Malysa Signed-off-by: Ian Roberts --- drivers/mtd/spi/spi-nor-core.c | 78 ++++++++++++++++++++++++++++++++++ drivers/mtd/spi/spi-nor-ids.c | 6 ++- include/linux/mtd/spi-nor.h | 6 +++ 3 files changed, 89 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index f164c3cf73..ce86e53860 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -3968,6 +3968,76 @@ static struct spi_nor_fixups macronix_octal_fixups = { }; #endif /* CONFIG_SPI_FLASH_MACRONIX */ +#ifdef CONFIG_SPI_FLASH_ISSI +/** + * spi_nor_issi_octal_dtr_enable() - Enable octal DTR on ISSI flashes. + * @nor: pointer to a 'struct spi_nor' + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_issi_octal_dtr_enable(struct spi_nor *nor) +{ + struct spi_mem_op op; + int ret; + u8 regval; + + nor->read_dummy = ISSI_MAX_DC; + + ret = write_enable(nor); + if (ret) + return ret; + + regval = SPINOR_REG_ISSI_VCR_ODDR_EN; + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_ISSI_WR_VCR, 1), + SPI_MEM_OP_ADDR(3, SPINOR_REG_ISSI_VCR_IOMODE, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, ®val, 1)); + + ret = spi_mem_exec_op(nor->spi, &op); + if (ret) { + dev_err(nor->dev, "Failed to enable octal DTR mode\n"); + return ret; + } + + nor->reg_proto = SNOR_PROTO_8_8_8_DTR; + + return 0; +} + +static void issi_octal_default_init(struct spi_nor *nor) +{ + nor->octal_dtr_enable = spi_nor_issi_octal_dtr_enable; +} + +static void issi_octal_post_sfdp_fixup(struct spi_nor *nor, + struct spi_nor_flash_parameter *params) +{ + /* + * Adding SNOR_HWCAPS_PP_8_8_8_DTR in hwcaps.mask when + * SPI_NOR_OCTAL_DTR_READ flag exists. + */ + if (params->hwcaps.mask & SNOR_HWCAPS_READ_8_8_8_DTR) + params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR; + nor->cmd_ext_type = SPI_NOR_EXT_INVERT; + + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_8_8_8_DTR], + 0, 16, 0x0c, SNOR_PROTO_8_8_8_DTR); + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_8_8_8_DTR], + 0x12, SNOR_PROTO_8_8_8_DTR); + + params->rdsr_dummy = 8; + + nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE; + nor->flags |= SNOR_F_SOFT_RESET; +} + +static struct spi_nor_fixups issi_octal_dtr_fixups = { + .default_init = issi_octal_default_init, + .post_sfdp = issi_octal_post_sfdp_fixup, +}; +#endif /* CONFIG_SPI_FLASH_ISSI */ + /** spi_nor_octal_dtr_enable() - enable Octal DTR I/O if needed * @nor: pointer to a 'struct spi_nor' * @@ -4161,6 +4231,14 @@ void spi_nor_set_fixups(struct spi_nor *nor) nor->fixups = &mt35xu512aba_fixups; #endif +#if CONFIG_IS_ENABLED(SPI_FLASH_ISSI) + if (JEDEC_MFR(nor->info) == SNOR_MFR_ISSI) { + if ((nor->info->id[1] == 0x5a || nor->info->id[1] == 0x5b) && + (nor->info->id[2] == 0x19 || nor->info->id[2] == 0x18)) + nor->fixups = &issi_octal_dtr_fixups; + } +#endif + #if CONFIG_IS_ENABLED(SPI_FLASH_MACRONIX) nor->fixups = ¯onix_octal_fixups; #endif /* SPI_FLASH_MACRONIX */ diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c index 4e83b8c94c..e3e37cd79b 100644 --- a/drivers/mtd/spi/spi-nor-ids.c +++ b/drivers/mtd/spi/spi-nor-ids.c @@ -238,8 +238,12 @@ const struct flash_info spi_nor_ids[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { INFO("is25wp01g", 0x9d701b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("is25lx256", 0x9d5a19, 0, 128 * 1024, 256, + SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ + | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, { INFO("is25wx256", 0x9d5b19, 0, 128 * 1024, 256, - SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) }, + SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ + | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, { INFO("is25lx512", 0x9d5a1a, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_4B_OPCODES | SPI_NOR_HAS_TB) }, #endif diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 2eddb52392..bd364e74a7 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -131,6 +131,12 @@ #define SPINOR_REG_MXIC_DC_20 0x0 /* Setting dummy cycles to 20 */ #define MXIC_MAX_DC 20 /* Maximum value of dummy cycles */ +/* Used for ISSI flashes. */ +#define SPINOR_OP_ISSI_WR_VCR 0x81 +#define SPINOR_REG_ISSI_VCR_IOMODE 0x0 +#define SPINOR_REG_ISSI_VCR_ODDR_EN 0xe7 +#define ISSI_MAX_DC 16 + /* Used for Spansion flashes only. */ #define SPINOR_OP_BRWR 0x17 /* Bank register write */ #define SPINOR_OP_BRRD 0x16 /* Bank register read */ From patchwork Thu Apr 11 22:36:47 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg Malysa X-Patchwork-Id: 1922729 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=mgyAcJFS; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VFvjK5CmKz1yYL for ; Fri, 12 Apr 2024 08:37:57 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 539F1881FA; Fri, 12 Apr 2024 00:37:47 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.b="mgyAcJFS"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 63C248758B; Fri, 12 Apr 2024 00:37:46 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-yb1-xb29.google.com (mail-yb1-xb29.google.com [IPv6:2607:f8b0:4864:20::b29]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id DDEC3881FD for ; Fri, 12 Apr 2024 00:37:42 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=greg.malysa@timesys.com Received: by mail-yb1-xb29.google.com with SMTP id 3f1490d57ef6-dd161eb03afso327478276.0 for ; Thu, 11 Apr 2024 15:37:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=timesys-com.20230601.gappssmtp.com; s=20230601; t=1712875061; x=1713479861; darn=lists.denx.de; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=WRNyd6a7VWNtm86TXN1lqxb3cvB2GYtldmsHq2lj/VI=; b=mgyAcJFScLsBHGeNHxKXOU67tPVCgQfR6pIaxcBe7/vDGaScinDdc9R3LqPxt3nB06 J8THx6ush4YDA8NGOV5N2qL6rreCCryAGnUuY8ApLkCio++hAtA+68JSBWfeAHB5OXQz +q6ZUgYVJO5OXxivuCprJ1MtSuTImMhv9VFHeQcPAsYY9Oc8nqJtcqmWQhkg5L/Ua6jw DHJul3YbBibtOV85EFe1LqTJQK5QeHnW7wufrMfkx3aPkZgFPb0u150pIE0bEkqYZrRg wcrFOyhj+nfW52zyIBXB1Jn/Z4QeTEowNq9YkwPmspEmmLMaDpD1OW6/xmp7pbrQFQAv b0Qw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712875061; x=1713479861; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=WRNyd6a7VWNtm86TXN1lqxb3cvB2GYtldmsHq2lj/VI=; b=J3gGfdrPv1T06oyiGMyRPB3GjamrUw1zaEn2pFTrCjmAVjrrrItUCzT2j6WdKGC+P8 dMkmDbxUtpSH0aYEgvmdQy2bBgugemRbzwA9P6cMFM1lpZIZk4tS57iCAWurdKMvlJ10 ZwewU+PJyxojyXSknTxUWqh1p0zzZGG6nUoD8oqLjxBIsUzAELt9fzxYl/3/ETcWtU46 TWRBZS3Y8DNJswXoXz0QWVTjILXYvqOELXtG+3KmILF/C44Q2lQkAFHqfeB6Q+nn+l4S +2Tj2rtLp7t7ogp4iCCbu3lxaiHSRAr+elXbgvAq7yGTj6lrtX5ANGjezxVosJuuM1fX QAcw== X-Gm-Message-State: AOJu0YzfJ81NwiSYGy6qCVUHwphF1e6wk26cKWLhYnGLRtb9dgb3jozK bloOxhkBT4Smdg95fm/qyPWV7TeKsmNJ1Bg8PKvWDNvBH+U9BD8wBLiz1U092VK+KToN0De1hvo 5Qw== X-Google-Smtp-Source: AGHT+IGrPNyOFzYCDSBEleYqeFglTgZcw5ykZCOeRTXa36MgBErOXpbMj8CS2cpy8t8xVV3545GyWw== X-Received: by 2002:a05:6902:1b82:b0:dc7:465d:c06d with SMTP id ei2-20020a0569021b8200b00dc7465dc06dmr1019280ybb.28.1712875061486; Thu, 11 Apr 2024 15:37:41 -0700 (PDT) Received: from executor.attlocal.net ([2600:1700:5eb5:1ba0:dc1f:cff:fef9:435b]) by smtp.gmail.com with ESMTPSA id q11-20020a25f90b000000b00dcbbea79ffcsm482744ybe.42.2024.04.11.15.37.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Apr 2024 15:37:41 -0700 (PDT) From: Greg Malysa To: u-boot@lists.denx.de Cc: Arturs.Artamonovs@analog.com, Vasileios.Bimpikas@analog.com, Utsav.Agarwal@analog.com, Ian Roberts , Nathan Barrett-Morrison , Greg Malysa , Apurva Nandan , Dhruva Gole , Jagan Teki , Jan Kiszka , Neha Malcom Francis , Tejas Bhumkar , Tom Rini , Udit Kumar Subject: [PATCH 03/11] spi: cadence-quadspi: Enable DDR bit for DTR commands Date: Thu, 11 Apr 2024 18:36:47 -0400 Message-ID: <20240411223709.573-4-greg.malysa@timesys.com> X-Mailer: git-send-email 2.43.2 In-Reply-To: <20240411223709.573-1-greg.malysa@timesys.com> References: <20240411223709.573-1-greg.malysa@timesys.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean From: Ian Roberts The Cadence octal SPI IP read instruction register requires a bit to be set to indicate if the read opcode is a compliant DDR read command. Co-developed-by: Nathan Barrett-Morrison Signed-off-by: Nathan Barrett-Morrison Signed-off-by: Greg Malysa Signed-off-by: Ian Roberts --- drivers/spi/cadence_qspi.h | 1 + drivers/spi/cadence_qspi_apb.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h index 693474a287..72e92cc997 100644 --- a/drivers/spi/cadence_qspi.h +++ b/drivers/spi/cadence_qspi.h @@ -61,6 +61,7 @@ #define CQSPI_REG_RD_INSTR 0x04 #define CQSPI_REG_RD_INSTR_OPCODE_LSB 0 #define CQSPI_REG_RD_INSTR_TYPE_INSTR_LSB 8 +#define CQSPI_REG_RD_INSTR_DDR_EN_MASK BIT(10) #define CQSPI_REG_RD_INSTR_TYPE_ADDR_LSB 12 #define CQSPI_REG_RD_INSTR_TYPE_DATA_LSB 16 #define CQSPI_REG_RD_INSTR_MODE_EN_LSB 20 diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index fb90532217..34cacf1880 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -446,6 +446,7 @@ int cadence_qspi_apb_command_read_setup(struct cadence_spi_priv *priv, return ret; reg = cadence_qspi_calc_rdreg(priv); + reg |= op->cmd.dtr ? CQSPI_REG_RD_INSTR_DDR_EN_MASK : 0; writel(reg, priv->regbase + CQSPI_REG_RD_INSTR); return 0; @@ -537,6 +538,7 @@ int cadence_qspi_apb_command_write_setup(struct cadence_spi_priv *priv, return ret; reg = cadence_qspi_calc_rdreg(priv); + reg |= op->cmd.dtr ? CQSPI_REG_RD_INSTR_DDR_EN_MASK : 0; writel(reg, priv->regbase + CQSPI_REG_RD_INSTR); return 0; @@ -638,6 +640,7 @@ int cadence_qspi_apb_read_setup(struct cadence_spi_priv *priv, opcode = op->cmd.opcode; rd_reg = opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB; + rd_reg |= op->cmd.dtr ? CQSPI_REG_RD_INSTR_DDR_EN_MASK : 0; rd_reg |= cadence_qspi_calc_rdreg(priv); writel(op->addr.val, priv->regbase + CQSPI_REG_INDIRECTRDSTARTADDR); @@ -812,6 +815,7 @@ int cadence_qspi_apb_write_setup(struct cadence_spi_priv *priv, writel(reg, priv->regbase + CQSPI_REG_WR_INSTR); reg = cadence_qspi_calc_rdreg(priv); + reg |= op->cmd.dtr ? CQSPI_REG_RD_INSTR_DDR_EN_MASK : 0; writel(reg, priv->regbase + CQSPI_REG_RD_INSTR); writel(op->addr.val, priv->regbase + CQSPI_REG_INDIRECTWRSTARTADDR); From patchwork Thu Apr 11 22:36:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg Malysa X-Patchwork-Id: 1922731 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=IXMUZQ2P; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VFvjp0RT0z1yYL for ; Fri, 12 Apr 2024 08:38:22 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 3713088235; Fri, 12 Apr 2024 00:37:49 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.b="IXMUZQ2P"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 8357D881FD; Fri, 12 Apr 2024 00:37:46 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-yw1-x1136.google.com (mail-yw1-x1136.google.com [IPv6:2607:f8b0:4864:20::1136]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id E5C1688202 for ; Fri, 12 Apr 2024 00:37:43 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=greg.malysa@timesys.com Received: by mail-yw1-x1136.google.com with SMTP id 00721157ae682-6181d032bf9so2868087b3.3 for ; Thu, 11 Apr 2024 15:37:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=timesys-com.20230601.gappssmtp.com; s=20230601; t=1712875062; x=1713479862; darn=lists.denx.de; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=TZGOt78qiZteGmXNos080gB0AWFgnQINCSKqCGF7BkA=; b=IXMUZQ2Pv8+BDNpqQUGjnA7qNZfUulZWyXuSINfpDxauMGKDCzUEy5PAy5psEx5/GL VFa5GRSCxVEy/DOtSEN8wxFG5YAIsszybGWRFxGqdaOfKLl5JPq999Ld0ZDQ7KyxXnmG gxpidNydb/snn38xTiYr0HrGnEwZz5iFGrxt67vo+qCIzwlGWcJk8VV17lj7z7MSNwW+ m1CfCJnmnIgIjFmsYgObwqJDvRrf+KDJV82n5EDIOJAc/EiCGV5JHzkIWEWOGH8XmDO+ IWpEEuicGdLPPQoWrtAFVzW60P1Gr6FplnBmIKCw1ADEr1UqePeQlD7Zr0jioBSLGInL 1g9Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712875062; x=1713479862; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=TZGOt78qiZteGmXNos080gB0AWFgnQINCSKqCGF7BkA=; b=o3dRzpWc/B7cpx1f+mqB+Bu1N97WWK/DCRRhDMxHiCkWdJ6TjxNW4Eh0eF/VcewEpl ge5zgFTxD6V1d2xkxnsW4yGNhVHbR4Ml+rW60mTDg5bQYDasyX8JXSRnNZrcl9xP8MMX kRdFoYeMDZko992672StfKSJMmsPU8KTYn/RbH/5QLJDuSj/ZAAUga70qm5kNyQ5VQzQ zKgktaTYYiIHAQsfHrK1XWFg/qxMBbLX20OzpMeYREh+/JCwvdyhamWxyONxiBveiX3V cOg81ryUlWQjogEQSBEBpwcrXWw/w7iYVmo3XWi735T2CCtCbC6fkSJrcaC6qr6DHGmq vzdQ== X-Gm-Message-State: AOJu0Yyj3GhYRaP/EbT51l042l1Ym9PlYhMCmbq6YvWp5cJtN4pSjta4 /a0g4rgVMO7YndzYaHbadB8reWpEAdxNnnbaVXpijePFN8VnxHfBL5CG/k/rFs0lJoKPfun4VV3 zzA== X-Google-Smtp-Source: AGHT+IHRMu8bDYH5mSUp+uY/hIlXrMDxH7r+k+0NP4sdjdURiYxCBufwUNdpZGSzelSr0qhKAds0eg== X-Received: by 2002:a25:8106:0:b0:dc6:de64:f74 with SMTP id o6-20020a258106000000b00dc6de640f74mr875406ybk.9.1712875062308; Thu, 11 Apr 2024 15:37:42 -0700 (PDT) Received: from executor.attlocal.net ([2600:1700:5eb5:1ba0:dc1f:cff:fef9:435b]) by smtp.gmail.com with ESMTPSA id q11-20020a25f90b000000b00dcbbea79ffcsm482744ybe.42.2024.04.11.15.37.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Apr 2024 15:37:41 -0700 (PDT) From: Greg Malysa To: u-boot@lists.denx.de Cc: Arturs.Artamonovs@analog.com, Vasileios.Bimpikas@analog.com, Utsav.Agarwal@analog.com, Ian Roberts , Nathan Barrett-Morrison , Greg Malysa , Apurva Nandan , Dhruva Gole , Jagan Teki , Jan Kiszka , Tejas Bhumkar , Tom Rini Subject: [PATCH 04/11] spi: cadence-quadspi: enable opcode extension based on command length Date: Thu, 11 Apr 2024 18:36:48 -0400 Message-ID: <20240411223709.573-5-greg.malysa@timesys.com> X-Mailer: git-send-email 2.43.2 In-Reply-To: <20240411223709.573-1-greg.malysa@timesys.com> References: <20240411223709.573-1-greg.malysa@timesys.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean From: Ian Roberts Some flash chips use dual opcodes in other modes. For example, the Macronix MX66 requires dual opcodes for STR octal operation. Thus, enable opcode extension based on the length of the command instead of the DTR mode of the controller. Co-developed-by: Nathan Barrett-Morrison Signed-off-by: Nathan Barrett-Morrison Signed-off-by: Greg Malysa Signed-off-by: Ian Roberts --- drivers/spi/cadence_qspi_apb.c | 66 +++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index 34cacf1880..eb9f4ed63d 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -412,19 +412,27 @@ static int cadence_qspi_enable_dtr(struct cadence_spi_priv *priv, reg = readl(priv->regbase + CQSPI_REG_CONFIG); - if (enable) { - reg |= CQSPI_REG_CONFIG_DTR_PROTO; + switch (op->cmd.nbytes) { + case 1: + reg &= ~CQSPI_REG_CONFIG_DUAL_OPCODE; + break; + case 2: reg |= CQSPI_REG_CONFIG_DUAL_OPCODE; /* Set up command opcode extension. */ ret = cadence_qspi_setup_opcode_ext(priv, op, shift); if (ret) return ret; - } else { - reg &= ~CQSPI_REG_CONFIG_DTR_PROTO; - reg &= ~CQSPI_REG_CONFIG_DUAL_OPCODE; + break; + default: + return log_msg_ret("QSPI: Invalid command length", -EINVAL); } + if (enable) + reg |= CQSPI_REG_CONFIG_DTR_PROTO; + else + reg &= ~CQSPI_REG_CONFIG_DTR_PROTO; + writel(reg, priv->regbase + CQSPI_REG_CONFIG); return 0; @@ -465,10 +473,16 @@ int cadence_qspi_apb_command_read(struct cadence_spi_priv *priv, unsigned int dummy_clk; u8 opcode; - if (priv->dtr) - opcode = op->cmd.opcode >> 8; - else + switch (op->cmd.nbytes) { + case 1: opcode = op->cmd.opcode; + break; + case 2: + opcode = op->cmd.opcode >> 8; + break; + default: + return log_msg_ret("QSPI: Invalid command length", -EINVAL); + } if (opcode == CMD_4BYTE_OCTAL_READ && !priv->dtr) opcode = CMD_4BYTE_FAST_READ; @@ -557,10 +571,16 @@ int cadence_qspi_apb_command_write(struct cadence_spi_priv *priv, void *reg_base = priv->regbase; u8 opcode; - if (priv->dtr) - opcode = op->cmd.opcode >> 8; - else + switch (op->cmd.nbytes) { + case 1: opcode = op->cmd.opcode; + break; + case 2: + opcode = op->cmd.opcode >> 8; + break; + default: + return log_msg_ret("QSPI: Invalid command length", -EINVAL); + } reg |= opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB; @@ -634,10 +654,16 @@ int cadence_qspi_apb_read_setup(struct cadence_spi_priv *priv, priv->regbase + CQSPI_REG_INDIRECTTRIGGER); /* Configure the opcode */ - if (priv->dtr) - opcode = op->cmd.opcode >> 8; - else + switch (op->cmd.nbytes) { + case 1: opcode = op->cmd.opcode; + break; + case 2: + opcode = op->cmd.opcode >> 8; + break; + default: + return log_msg_ret("QSPI: Invalid command length", -EINVAL); + } rd_reg = opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB; rd_reg |= op->cmd.dtr ? CQSPI_REG_RD_INSTR_DDR_EN_MASK : 0; @@ -804,10 +830,16 @@ int cadence_qspi_apb_write_setup(struct cadence_spi_priv *priv, priv->regbase + CQSPI_REG_INDIRECTTRIGGER); /* Configure the opcode */ - if (priv->dtr) - opcode = op->cmd.opcode >> 8; - else + switch (op->cmd.nbytes) { + case 1: opcode = op->cmd.opcode; + break; + case 2: + opcode = op->cmd.opcode >> 8; + break; + default: + return log_msg_ret("QSPI: Invalid command length", -EINVAL); + } reg = opcode << CQSPI_REG_WR_INSTR_OPCODE_LSB; reg |= priv->data_width << CQSPI_REG_WR_INSTR_TYPE_DATA_LSB; From patchwork Thu Apr 11 22:36:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg Malysa X-Patchwork-Id: 1922730 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=MMpFPVeL; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VFvjV6cBFz1yYL for ; Fri, 12 Apr 2024 08:38:06 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 16E258821D; Fri, 12 Apr 2024 00:37:48 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.b="MMpFPVeL"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 5A82B8821D; Fri, 12 Apr 2024 00:37:46 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-yb1-xb30.google.com (mail-yb1-xb30.google.com [IPv6:2607:f8b0:4864:20::b30]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 302AE88203 for ; Fri, 12 Apr 2024 00:37:44 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=greg.malysa@timesys.com Received: by mail-yb1-xb30.google.com with SMTP id 3f1490d57ef6-dc23bf7e5aaso351789276.0 for ; Thu, 11 Apr 2024 15:37:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=timesys-com.20230601.gappssmtp.com; s=20230601; t=1712875063; x=1713479863; darn=lists.denx.de; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=DNxj6UVPinaepYnUbjkM2PEvKLXZlIWzfOwKwE/yf38=; b=MMpFPVeLU7I7/hMB1eUQzLpIJCtBDqugDxFE2OU/tiwdCdR0dYJuBvm+peVyXQKZg8 F0r6pTHGkr9CITLjI1t+b14KEbOf6NyfboF8Jsk7Blvxjc427iLQjAhvzQYQVIZ1BbjG LCH1G5AibPv+GyfvrNt5OWt/c5ocQiXDeWvFZ6jZmGqtmzt6OGeu2FVxBQ80JIQcB5dl I0o7t3e0MkL0gv1EMiVLQar3DMhIpaeysxAqYPKh7S/GknL2NcORu9AhDpVbZxyQQFF8 sUZk6t9Mkl+yq6Xz/toUeXV3ixJRZf2Lt/KkPlo2RW8GvHJ4ISBAzWJg2xZD6D7Uk7sT wEgA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712875063; x=1713479863; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=DNxj6UVPinaepYnUbjkM2PEvKLXZlIWzfOwKwE/yf38=; b=nELv6Bbp0G+npmewLpJiqQSVjN0koRQKi6buL9RxPD7L2dslFHkfQMPJ5kvMcx+X80 E3AnAu51p10hA3pVt6jhn2bYE3jCSnl9+C447qRYzQ/+sWbZ+kB/kL0w9SncQEMLWal/ aTRSQTCF5Yt7EA/WAJZ98iCOjRATq0bgVjbRPMFwiuFPnpprGETDjqJ+7iwSx/ADtrbT NZ7NM7AwSTRwiiyKnncNGDH4mpwcgvx7/75ePDi2NI3ihEWNf9ddIgpMScO4BssAlhzW gpk6A7xhecCdAXuQXmcdNj+pWFOCWtGi4PGK/QLUbynD/BZb37HTXVB8pk8VsADIbZ2U ENww== X-Gm-Message-State: AOJu0Yxs58pQYHCgf1oUoW2KKPrNKW9yFSiveqXH+SFkGWmY6ufpSxAP ChSUmOWlPtJikYru43wvZCoyV8YkEpdhN0vltI3ZT8aCszwAfAOvTkEOiDkid2P18C8ZMFwE99Z O2g== X-Google-Smtp-Source: AGHT+IFTVzsw+3G+J1IhpVmx+xJH0M3eKGZUP3W2XVdEMcHYBa3KBCcUbS9Ve4xLtNlkWpbbncNrfA== X-Received: by 2002:a25:b204:0:b0:dcc:5a73:1cad with SMTP id i4-20020a25b204000000b00dcc5a731cadmr873283ybj.42.1712875062959; Thu, 11 Apr 2024 15:37:42 -0700 (PDT) Received: from executor.attlocal.net ([2600:1700:5eb5:1ba0:dc1f:cff:fef9:435b]) by smtp.gmail.com with ESMTPSA id q11-20020a25f90b000000b00dcbbea79ffcsm482744ybe.42.2024.04.11.15.37.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Apr 2024 15:37:42 -0700 (PDT) From: Greg Malysa To: u-boot@lists.denx.de Cc: Arturs.Artamonovs@analog.com, Vasileios.Bimpikas@analog.com, Utsav.Agarwal@analog.com, Ian Roberts , Nathan Barrett-Morrison , Greg Malysa , Apurva Nandan , Dhruva Gole , Jagan Teki , Jan Kiszka , Tejas Bhumkar , Tom Rini , Udit Kumar Subject: [PATCH 05/11] spi: cadence-quadspi: disable automatic write enable Date: Thu, 11 Apr 2024 18:36:49 -0400 Message-ID: <20240411223709.573-6-greg.malysa@timesys.com> X-Mailer: git-send-email 2.43.2 In-Reply-To: <20240411223709.573-1-greg.malysa@timesys.com> References: <20240411223709.573-1-greg.malysa@timesys.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean From: Ian Roberts The spi-nor subsystem issues the write enable command manually. So this automatic feature sends duplicate commands and also introduces the possibility of erroneous writes. Disable the automatic write enable feature by default. Co-developed-by: Nathan Barrett-Morrison Signed-off-by: Nathan Barrett-Morrison Signed-off-by: Greg Malysa Signed-off-by: Ian Roberts --- drivers/spi/cadence_qspi.h | 1 + drivers/spi/cadence_qspi_apb.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h index 72e92cc997..355919cb23 100644 --- a/drivers/spi/cadence_qspi.h +++ b/drivers/spi/cadence_qspi.h @@ -73,6 +73,7 @@ #define CQSPI_REG_WR_INSTR 0x08 #define CQSPI_REG_WR_INSTR_OPCODE_LSB 0 +#define CQSPI_REG_WR_INSTR_WELDIS_MASK BIT(8) #define CQSPI_REG_WR_INSTR_TYPE_ADDR_LSB 12 #define CQSPI_REG_WR_INSTR_TYPE_DATA_LSB 16 diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index eb9f4ed63d..176cff5338 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -842,6 +842,7 @@ int cadence_qspi_apb_write_setup(struct cadence_spi_priv *priv, } reg = opcode << CQSPI_REG_WR_INSTR_OPCODE_LSB; + reg |= CQSPI_REG_WR_INSTR_WELDIS_MASK; reg |= priv->data_width << CQSPI_REG_WR_INSTR_TYPE_DATA_LSB; reg |= priv->addr_width << CQSPI_REG_WR_INSTR_TYPE_ADDR_LSB; writel(reg, priv->regbase + CQSPI_REG_WR_INSTR); From patchwork Thu Apr 11 22:36:50 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg Malysa X-Patchwork-Id: 1922733 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=KDjHERth; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VFvk548Cmz1yYL for ; Fri, 12 Apr 2024 08:38:37 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id BEA20881FD; Fri, 12 Apr 2024 00:37:51 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.b="KDjHERth"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 8EB4B881F1; Fri, 12 Apr 2024 00:37:48 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-yb1-xb2b.google.com (mail-yb1-xb2b.google.com [IPv6:2607:f8b0:4864:20::b2b]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id BE2E1881F1 for ; Fri, 12 Apr 2024 00:37:44 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=greg.malysa@timesys.com Received: by mail-yb1-xb2b.google.com with SMTP id 3f1490d57ef6-dd161eb03afso327506276.0 for ; Thu, 11 Apr 2024 15:37:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=timesys-com.20230601.gappssmtp.com; s=20230601; t=1712875063; x=1713479863; darn=lists.denx.de; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=cwmGtDM/sA+95pY5uIfDWYc2ODxI8hTv7lUwHq58KoM=; b=KDjHERthIo/pBrNOuEq12jje4LfKhe3iCjVNq7DaLuTe4CSkU9wnRome4PD8Q71Zxu KrEiPpalF4Hi8bJfPRQsiu1vlhbfzVtbEjqf+wmx4le+0ywTLFjBiFH/wJZocedsuCjk jLMPt5QPTYdxWRbdeAvIz0YJfnHzNtAAUvon0qtSCH86C6vbD5lSqEr+/0ykrJJ4Supw izmU040pEX/zwF/r8GK5CZy5y/RCI//9G/Pc8TKe71UouB8TOs1x+I9DADfac+iJLoSp cmU+wyTD7Ff/aT+lRr0L6EmAV3QVoa6nzufCSZuurZBNE4Zkju7Ken/TIBxM90TmaL2/ uYQw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712875063; x=1713479863; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=cwmGtDM/sA+95pY5uIfDWYc2ODxI8hTv7lUwHq58KoM=; b=xIk/7A0czgPH5F59k86i04416Az/U7Mgyab1ZayjyUSeTGULW2nX/J2HSgpQujKda6 kF2K0tiKOC5p0a1/g3mhjTD5g9ZfdqK6kozEDsteI3M1RZCnQafV3re+yvgZ9r2/xDWG 9O47Xo6DYhdIE2QomO0hwtgWCHD7F10O5NiBs4kq2DxK0WU7rkn6kpRe5GEB47TeaSGg bek3v8mgp/Fr6zHs1VSBeczRf6AJDt3ccdOvbU7/gx5tlaleVyEnW0LE9fqgioKDldgP 9pjRWK7nrB0kuvjyRtBZwONwg+10xV2or7/W72u8BVVJTvadUoANJ/cpAfexpuxS9KBo vfCg== X-Gm-Message-State: AOJu0YwtIB4AYeQNZUeZHusomeel4ABVqTm6D3KA7PYz84Qh0rzb4HQJ yC7l/vlS5PDn6HKwfFhyZgPeIW3ep3Rz2VF5o049i7bW2mna9iajO/gPiKaLqKysaceNuMoEYBP yqg== X-Google-Smtp-Source: AGHT+IEzmz9/m3IwN68W4zDwoljswLSBXzaAD256VSuCysxg2axm3TXVJgQSr6TqTbKl89dqnBX12A== X-Received: by 2002:a25:b193:0:b0:dcf:bc57:cd61 with SMTP id h19-20020a25b193000000b00dcfbc57cd61mr932181ybj.50.1712875063526; Thu, 11 Apr 2024 15:37:43 -0700 (PDT) Received: from executor.attlocal.net ([2600:1700:5eb5:1ba0:dc1f:cff:fef9:435b]) by smtp.gmail.com with ESMTPSA id q11-20020a25f90b000000b00dcbbea79ffcsm482744ybe.42.2024.04.11.15.37.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Apr 2024 15:37:43 -0700 (PDT) From: Greg Malysa To: u-boot@lists.denx.de Cc: Arturs.Artamonovs@analog.com, Vasileios.Bimpikas@analog.com, Utsav.Agarwal@analog.com, Ian Roberts , Nathan Barrett-Morrison , Greg Malysa , Apurva Nandan , Dhruva Gole , Jagan Teki , Jan Kiszka , Tejas Bhumkar , Tom Rini Subject: [PATCH 06/11] spi: cadence-quadspi: unconditionally disable auto status register reads Date: Thu, 11 Apr 2024 18:36:50 -0400 Message-ID: <20240411223709.573-7-greg.malysa@timesys.com> X-Mailer: git-send-email 2.43.2 In-Reply-To: <20240411223709.573-1-greg.malysa@timesys.com> References: <20240411223709.573-1-greg.malysa@timesys.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean From: Ian Roberts In addition to the given reason for the conditional disable of this feature for DTR: Theoretically, some flashes have their WIP bit in different bit positions or have a different bit polarity. spi-nor currently does not have an interface in place to dictate this information to this driver for proper configuration. The default of the controller hardware has this status register auto polling without expiration. This means that if there is any controller misconfiguration or communication failure, it will completely lock up the controller. Thus, unconditionally disable this feature for now. Co-developed-by: Nathan Barrett-Morrison Signed-off-by: Nathan Barrett-Morrison Signed-off-by: Greg Malysa Signed-off-by: Ian Roberts --- drivers/spi/cadence_qspi_apb.c | 46 ++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index 176cff5338..d347cb8d47 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -853,19 +853,29 @@ int cadence_qspi_apb_write_setup(struct cadence_spi_priv *priv, writel(op->addr.val, priv->regbase + CQSPI_REG_INDIRECTWRSTARTADDR); - if (priv->dtr) { - /* - * Some flashes like the cypress Semper flash expect a 4-byte - * dummy address with the Read SR command in DTR mode, but this - * controller does not support sending address with the Read SR - * command. So, disable write completion polling on the - * controller's side. spi-nor will take care of polling the - * status register. - */ - reg = readl(priv->regbase + CQSPI_REG_WR_COMPLETION_CTRL); - reg |= CQSPI_REG_WR_DISABLE_AUTO_POLL; - writel(reg, priv->regbase + CQSPI_REG_WR_COMPLETION_CTRL); - } + /* + * Some flashes like the cypress Semper flash expect a 4-byte + * dummy address with the Read SR command in DTR mode, but this + * controller does not support sending address with the Read SR + * command. So, disable write completion polling on the + * controller's side. spi-nor will take care of polling the + * status register. + * + * Theoretically, some flashes have their WIP bit in different + * bit positions or have a different bit polarity. spi-nor + * currently does not have an interface in place to dictate + * this information to this driver for proper configuration. + * + * The default of the controller hardware has this status register + * auto polling without expiration. This means that if there is any + * controller misconfiguration or communication failure, it will + * completely lock up the controller. + * + * Thus, unconditionally disable this feature for now. + */ + reg = readl(priv->regbase + CQSPI_REG_WR_COMPLETION_CTRL); + reg |= CQSPI_REG_WR_DISABLE_AUTO_POLL; + writel(reg, priv->regbase + CQSPI_REG_WR_COMPLETION_CTRL); reg = readl(priv->regbase + CQSPI_REG_SIZE); reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; @@ -970,16 +980,8 @@ int cadence_qspi_apb_write_execute(struct cadence_spi_priv *priv, const void *buf = op->data.buf.out; size_t len = op->data.nbytes; - /* - * Some flashes like the Cypress Semper flash expect a dummy 4-byte - * address (all 0s) with the read status register command in DTR mode. - * But this controller does not support sending dummy address bytes to - * the flash when it is polling the write completion register in DTR - * mode. So, we can not use direct mode when in DTR mode for writing - * data. - */ cadence_qspi_apb_enable_linear_mode(true); - if (!priv->dtr && priv->use_dac_mode && (to + len < priv->ahbsize)) { + if (priv->use_dac_mode && (to + len < priv->ahbsize)) { memcpy_toio(priv->ahbbase + to, buf, len); if (!cadence_qspi_wait_idle(priv->regbase)) return -EIO; From patchwork Thu Apr 11 22:36:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg Malysa X-Patchwork-Id: 1922737 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=DMtWHaiD; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VFvkr3K5xz1yYL for ; Fri, 12 Apr 2024 08:39:16 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id A63CC88227; Fri, 12 Apr 2024 00:37:54 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.b="DMtWHaiD"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 2B88B88227; Fri, 12 Apr 2024 00:37:50 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-yb1-xb34.google.com (mail-yb1-xb34.google.com [IPv6:2607:f8b0:4864:20::b34]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 7073A88211 for ; Fri, 12 Apr 2024 00:37:45 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=greg.malysa@timesys.com Received: by mail-yb1-xb34.google.com with SMTP id 3f1490d57ef6-dcd9e34430cso343224276.1 for ; Thu, 11 Apr 2024 15:37:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=timesys-com.20230601.gappssmtp.com; s=20230601; t=1712875064; x=1713479864; darn=lists.denx.de; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=DM9XHh/qPuTHB5P1dE9Mw9suPfUvDU6T3YKOEUDAS/4=; b=DMtWHaiDorgD81UJbBHa4qAA2FdNXvMR1FelVgMHs9T5HBFF6nkOlTbFPYfOvYcQXI sNudH7NF0wyZjatsaXdY8KklC9PYfg0orGUvlzNu98HkcTxaP2S8182KIOgoP5Ea5Gku ucjjmpsaNVAc3QYAVdPnuQVoICQJS50mu3/IKF42beWe59EfSSeKFtHYpmIJmo3Kk7Iv CEqCeql7t+mDsCMsxbWeJcTdpej/n1DFZLhSVgprNToJwH/+fY0HYeaA8wnF46+wzs1C tRMh6ihlq54FEfXRrtm83yd9bPU+MmZfGc7t6sdJ1zyYwI+6Xguix3M2z7w/AzK7yNps bf2Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712875064; x=1713479864; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=DM9XHh/qPuTHB5P1dE9Mw9suPfUvDU6T3YKOEUDAS/4=; b=PlfHAwxiB5gUH+ctMkAfLVWu9p0124T9BnNM4EAhtWkw6k+O/UhhHzx8J5t8Ww51G9 aKvKWlzh02su2Xutt64B1bBNDBpJex1dfSVkc3aTaCZKZgRaK8pHhIqDJXofZ3p6LrXz LdEJLdQ4MMHspHAPJK1Ej3rXrwJ5iegod7XVOldwrNuF0wwFx+Q5aq9+dfWVOrO3ZrQP FurWlNTbt36iIMatOI7uwMEYa6kBJQp+Zou4zfwax6gnL5gOXtjN8GG+g9AazErzCZwV 0Ux1DgsZvJ5k8qycrRYHllc5ZLeTyN1Hr5zCKEmEiw0a3pOs8lrsdWCSBL3LJ7cUk81r K9Ew== X-Gm-Message-State: AOJu0YyvFZk+byU+XunqcVht0oVYLVqIDgvrObqL0tOkllmYW/NgtmV3 6WmmoVaCsQQkXtrmQyk6mtyZwu5N6JJvgIG2A5f0EBd+sJ/qV3O9Fc5/VsGd92PDtu1tm5RE9ob MrA== X-Google-Smtp-Source: AGHT+IHBvwGDkF1DLI9OmlqqBG1A+qPysmf+pMkLcYX2QjkmZaSOD1UMSM8Y/kXccHS8d1wwt1iX8Q== X-Received: by 2002:a25:df91:0:b0:dcc:dbb8:ba73 with SMTP id w139-20020a25df91000000b00dccdbb8ba73mr836934ybg.9.1712875064133; Thu, 11 Apr 2024 15:37:44 -0700 (PDT) Received: from executor.attlocal.net ([2600:1700:5eb5:1ba0:dc1f:cff:fef9:435b]) by smtp.gmail.com with ESMTPSA id q11-20020a25f90b000000b00dcbbea79ffcsm482744ybe.42.2024.04.11.15.37.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Apr 2024 15:37:43 -0700 (PDT) From: Greg Malysa To: u-boot@lists.denx.de Cc: Arturs.Artamonovs@analog.com, Vasileios.Bimpikas@analog.com, Utsav.Agarwal@analog.com, Ian Roberts , Nathan Barrett-Morrison , Greg Malysa , Apurva Nandan , Ashok Reddy Soma , Dhruva Gole , Igor Prusov , Jagan Teki , Jan Kiszka , Johan Jonker , Tejas Bhumkar , Tom Rini , Udit Kumar Subject: [PATCH 07/11] spi: cadence-quadspi: Remove redundant DTR state Date: Thu, 11 Apr 2024 18:36:51 -0400 Message-ID: <20240411223709.573-8-greg.malysa@timesys.com> X-Mailer: git-send-email 2.43.2 In-Reply-To: <20240411223709.573-1-greg.malysa@timesys.com> References: <20240411223709.573-1-greg.malysa@timesys.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean From: Ian Roberts cadence_spi_mem_supports_op() already checks that every memory operation either has all DTR booleans set or cleared. Thus, there is no need to store a cached dtr value. The command DTR state can be used since it is not optional like the other fields. Co-developed-by: Nathan Barrett-Morrison Signed-off-by: Nathan Barrett-Morrison Signed-off-by: Greg Malysa Signed-off-by: Ian Roberts --- drivers/spi/cadence_qspi.c | 6 ++++++ drivers/spi/cadence_qspi.h | 1 - drivers/spi/cadence_qspi_apb.c | 27 ++++++++------------------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index f4593c47b8..a2644d9e11 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -362,6 +362,12 @@ static bool cadence_spi_mem_supports_op(struct spi_slave *slave, bool all_true, all_false; /* + * For an op to be DTR, cmd phase along with every other non-empty + * phase should have dtr field set to 1. If an op phase has zero + * nbytes, ignore its dtr field; otherwise, check its dtr field. + * Also, dummy checks not performed here Since supports_op() + * already checks that all or none of the fields are DTR. + * * op->dummy.dtr is required for converting nbytes into ncycles. * Also, don't check the dtr field of the op phase having zero nbytes. */ diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h index 355919cb23..5704f5a3f6 100644 --- a/drivers/spi/cadence_qspi.h +++ b/drivers/spi/cadence_qspi.h @@ -265,7 +265,6 @@ struct cadence_spi_priv { u8 inst_width; u8 addr_width; u8 data_width; - bool dtr; }; /* Functions call declaration */ diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index d347cb8d47..2600370f85 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -120,17 +120,6 @@ static int cadence_qspi_set_protocol(struct cadence_spi_priv *priv, { int ret; - /* - * For an op to be DTR, cmd phase along with every other non-empty - * phase should have dtr field set to 1. If an op phase has zero - * nbytes, ignore its dtr field; otherwise, check its dtr field. - * Also, dummy checks not performed here Since supports_op() - * already checks that all or none of the fields are DTR. - */ - priv->dtr = op->cmd.dtr && - (!op->addr.nbytes || op->addr.dtr) && - (!op->data.nbytes || op->data.dtr); - ret = cadence_qspi_buswidth_to_inst_type(op->cmd.buswidth); if (ret < 0) return ret; @@ -449,7 +438,7 @@ int cadence_qspi_apb_command_read_setup(struct cadence_spi_priv *priv, return ret; ret = cadence_qspi_enable_dtr(priv, op, CQSPI_REG_OP_EXT_STIG_LSB, - priv->dtr); + op->cmd.dtr); if (ret) return ret; @@ -484,13 +473,13 @@ int cadence_qspi_apb_command_read(struct cadence_spi_priv *priv, return log_msg_ret("QSPI: Invalid command length", -EINVAL); } - if (opcode == CMD_4BYTE_OCTAL_READ && !priv->dtr) + if (opcode == CMD_4BYTE_OCTAL_READ && !op->cmd.dtr) opcode = CMD_4BYTE_FAST_READ; reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB; /* Set up dummy cycles. */ - dummy_clk = cadence_qspi_calc_dummy(op, priv->dtr); + dummy_clk = cadence_qspi_calc_dummy(op, op->cmd.dtr); if (dummy_clk > CQSPI_DUMMY_CLKS_MAX) return -ENOTSUPP; @@ -547,7 +536,7 @@ int cadence_qspi_apb_command_write_setup(struct cadence_spi_priv *priv, return ret; ret = cadence_qspi_enable_dtr(priv, op, CQSPI_REG_OP_EXT_STIG_LSB, - priv->dtr); + op->cmd.dtr); if (ret) return ret; @@ -597,7 +586,7 @@ int cadence_qspi_apb_command_write(struct cadence_spi_priv *priv, } /* Set up dummy cycles. */ - dummy_clk = cadence_qspi_calc_dummy(op, priv->dtr); + dummy_clk = cadence_qspi_calc_dummy(op, op->cmd.dtr); if (dummy_clk > CQSPI_DUMMY_CLKS_MAX) return -EOPNOTSUPP; @@ -645,7 +634,7 @@ int cadence_qspi_apb_read_setup(struct cadence_spi_priv *priv, return ret; ret = cadence_qspi_enable_dtr(priv, op, CQSPI_REG_OP_EXT_READ_LSB, - priv->dtr); + op->cmd.dtr); if (ret) return ret; @@ -673,7 +662,7 @@ int cadence_qspi_apb_read_setup(struct cadence_spi_priv *priv, if (dummy_bytes) { /* Convert to clock cycles. */ - dummy_clk = cadence_qspi_calc_dummy(op, priv->dtr); + dummy_clk = cadence_qspi_calc_dummy(op, op->cmd.dtr); if (dummy_clk > CQSPI_DUMMY_CLKS_MAX) return -ENOTSUPP; @@ -821,7 +810,7 @@ int cadence_qspi_apb_write_setup(struct cadence_spi_priv *priv, return ret; ret = cadence_qspi_enable_dtr(priv, op, CQSPI_REG_OP_EXT_WRITE_LSB, - priv->dtr); + op->cmd.dtr); if (ret) return ret; From patchwork Thu Apr 11 22:36:52 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg Malysa X-Patchwork-Id: 1922734 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=FJ/O9thL; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VFvkH6ZQHz1yYL for ; Fri, 12 Apr 2024 08:38:47 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 6A3378822B; Fri, 12 Apr 2024 00:37:52 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.b="FJ/O9thL"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id CFAB888229; Fri, 12 Apr 2024 00:37:48 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-yb1-xb2a.google.com (mail-yb1-xb2a.google.com [IPv6:2607:f8b0:4864:20::b2a]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 552478821A for ; Fri, 12 Apr 2024 00:37:46 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=greg.malysa@timesys.com Received: by mail-yb1-xb2a.google.com with SMTP id 3f1490d57ef6-dc25e12cc63so1360428276.0 for ; Thu, 11 Apr 2024 15:37:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=timesys-com.20230601.gappssmtp.com; s=20230601; t=1712875065; x=1713479865; darn=lists.denx.de; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=8ez5KAUpmv7EhJN0xXjFazlw/untzxRkYccdlhMzF6g=; b=FJ/O9thLkpDwsDYYfcijdozjQamv7+GAYLWAoiiVcBx2Hamb7woXqsD4uu6ntqucQF MfgV/ADyzjIFGt1DAymVsHn4/UkeJM9NlonhETzWyAD8iyz2MwDWIGti4E2azsy1jYRx THrWizKiKymB4oOqzXuONXsZcB00SvHpnlnf2tL0CeQaiPD0SNF35zgyZgxbR8cQCIQv k2lcepxbkPntShjxvXpvmzzd6/O+0Chs/Z7By/lVD+5pPjxjpml1RRiRTwKtGbBDyAVe TIUYnljkQRvlSElzez/ICZZI2qyFdLTLR6R+/1UTLlSiLPeUumKmLito/rLokfUdSD+A RXqw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712875065; x=1713479865; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=8ez5KAUpmv7EhJN0xXjFazlw/untzxRkYccdlhMzF6g=; b=dUZzVNuvIpu7UpxEXwsWRkPHoz39opUW6qz9nEJem4g6T6GVO1vW/tPeDRbMeC6tYe 5CH/tBfNiBytVpxOwUdhzmcjATJ+j30YtfXAeGy2E8vuJUcXT+8qEZQEeeU9r7dzXPVS vHo+dWUTqVpRFyAlPzJVWDk+gJogO/9mufs9pBAwcPPMk0l8Jdw9AcW9Oo9x4Bq4RqQn +QK3EpCSwVBjhNaunrqWcsOj2jrtNlvLDEM8AHL27aNQk4hyKmvgIpPE82uIR4xyXRi7 i4fuFMOZk138qrXNPM5EDZNU0W4ZwYTXDVRgb3jqBwndN3V4xEqUndWCJUfza4ja/nL2 oUDw== X-Gm-Message-State: AOJu0YwiBCYQiB9aRCbzXA2GbSDqPPggQYfqWh1nPSE2Ls/SingFruMn Co9b6gMimU5AtRLuGXcswcCg+3klln+HGpBL/XuJ4fm5LucI2Rbz/c5thjgi4Vj4PhZefSUO2ye VSw== X-Google-Smtp-Source: AGHT+IFbXSXaKJ0FaJ39SbncBgLof/wK3ldyAJjq/0E1TsodUI7dLQevWNSZPoiL5tEyxhqcqS49xQ== X-Received: by 2002:a25:5144:0:b0:dcf:eb30:c580 with SMTP id f65-20020a255144000000b00dcfeb30c580mr1036582ybb.3.1712875064792; Thu, 11 Apr 2024 15:37:44 -0700 (PDT) Received: from executor.attlocal.net ([2600:1700:5eb5:1ba0:dc1f:cff:fef9:435b]) by smtp.gmail.com with ESMTPSA id q11-20020a25f90b000000b00dcbbea79ffcsm482744ybe.42.2024.04.11.15.37.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Apr 2024 15:37:44 -0700 (PDT) From: Greg Malysa To: u-boot@lists.denx.de Cc: Arturs.Artamonovs@analog.com, Vasileios.Bimpikas@analog.com, Utsav.Agarwal@analog.com, Ian Roberts , Nathan Barrett-Morrison , Greg Malysa , Apurva Nandan , Dhruva Gole , Jagan Teki , Jan Kiszka , Tejas Bhumkar , Tom Rini Subject: [PATCH 08/11] spi: cadence-quadspi: Direct mode does not support zero length addresses Date: Thu, 11 Apr 2024 18:36:52 -0400 Message-ID: <20240411223709.573-9-greg.malysa@timesys.com> X-Mailer: git-send-email 2.43.2 In-Reply-To: <20240411223709.573-1-greg.malysa@timesys.com> References: <20240411223709.573-1-greg.malysa@timesys.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean From: Ian Roberts It is not possible to configure the Cadence SPI IP block to use a zero length address in DMA read or write commands. Co-developed-by: Nathan Barrett-Morrison Signed-off-by: Nathan Barrett-Morrison Signed-off-by: Greg Malysa Signed-off-by: Ian Roberts --- drivers/spi/cadence_qspi_apb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index 2600370f85..340889c271 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -784,7 +784,7 @@ int cadence_qspi_apb_read_execute(struct cadence_spi_priv *priv, cadence_qspi_apb_enable_linear_mode(true); - if (priv->use_dac_mode && (from + len < priv->ahbsize)) { + if (op->addr.nbytes && priv->use_dac_mode && (from + len < priv->ahbsize)) { if (len < 256 || dma_memcpy(buf, priv->ahbbase + from, len) < 0) { memcpy_fromio(buf, priv->ahbbase + from, len); @@ -970,7 +970,7 @@ int cadence_qspi_apb_write_execute(struct cadence_spi_priv *priv, size_t len = op->data.nbytes; cadence_qspi_apb_enable_linear_mode(true); - if (priv->use_dac_mode && (to + len < priv->ahbsize)) { + if (op->addr.nbytes && priv->use_dac_mode && (to + len < priv->ahbsize)) { memcpy_toio(priv->ahbbase + to, buf, len); if (!cadence_qspi_wait_idle(priv->regbase)) return -EIO; From patchwork Thu Apr 11 22:36:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg Malysa X-Patchwork-Id: 1922735 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=TNjEqUqy; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VFvkV2j5Xz1yYL for ; Fri, 12 Apr 2024 08:38:58 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 10C7188213; Fri, 12 Apr 2024 00:37:53 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.b="TNjEqUqy"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 0FEEF88229; Fri, 12 Apr 2024 00:37:49 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-yb1-xb34.google.com (mail-yb1-xb34.google.com [IPv6:2607:f8b0:4864:20::b34]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 9FA9F881FE for ; Fri, 12 Apr 2024 00:37:46 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=greg.malysa@timesys.com Received: by mail-yb1-xb34.google.com with SMTP id 3f1490d57ef6-dcbc6a6808fso308473276.2 for ; Thu, 11 Apr 2024 15:37:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=timesys-com.20230601.gappssmtp.com; s=20230601; t=1712875065; x=1713479865; darn=lists.denx.de; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=UaUFGnTC45loDw+ajgYymT2Xbb7DoytNPTC5wR165Zs=; b=TNjEqUqyoZUcXiIrmH0h/R9TwBsByMRMm7GYC+67PXJDz5H4wxaRliFQEf+0UV9Jsc lKHZWb+EEaIbv3hRO1UkdTmgbB9TON1whr2yu1TAV5AWYEFMBAwWw9hpaA0zsOOHDHle IMR4xWpFzA5XpVpRjKQMlR715YGaDcbvvX28WPUNlvGxxZ9LVccaIaWT+fnzd7LUCOQY K6K7ROaqozgzPqKdMescgxj2cQY+5pzY/1Qb3gTizLBr31RwPvbcVHVlW1ouT14bgK/T 2OuFMEDAnPU+0BfQyoIwPrZ+SFuG/PMRyDJigJFaFK1kD51TV0hm/uvaw8k727Wjtl9M 6QAw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712875065; x=1713479865; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=UaUFGnTC45loDw+ajgYymT2Xbb7DoytNPTC5wR165Zs=; b=I31EPPPggCOpo20mCoOCAaCFB1b5ZCE8k8pf96uZkwMhXK1oI6XRygzSXr2CG32DlN 0PLMrPMmYneHcy6OgZuv3zzT2PvkLnBdSZOYhIElYgN9M4Lz++JrcHp/3XxqhnLOjEzy 6voSJERfEFo0fQlRQPa+yvOYVlIhq375RR+gMkjevNxqDwxZ+pT1HIT0syQ3p4k4Ok7g RxQFCZoO0UvqxDM2Tjdf/L9xGgn1YvkvE9jvTAE0UmJeET4gjSShyPfoY5+OKamHpbdJ Kk5LIYwdtElW4nkt+q01ZH5SEvic1LUuKL2HUJRK+mv/DP+OkSPjqV+yUiAedE1Q/+vp qFpg== X-Gm-Message-State: AOJu0YwuWdx0oasxJKgLeJMWkNM5IHN66/W9VeuxLQqZVB30/0DnGq35 5yneOW4cP+IIu6sYJAAJOo6a2Iyb/ZlwE896oencTPnTDFEfOlwG4Gf5ZyEWoT3+JK1fbmS9BUp xBg== X-Google-Smtp-Source: AGHT+IG+1M8DLkz2MMBlk0+RYG0bPrk5TQLObIDo+PVZ3rjEBizty/4Qe2Ny2S38qMppWpvx8FoExw== X-Received: by 2002:a05:6902:2409:b0:dcf:56c5:4298 with SMTP id dr9-20020a056902240900b00dcf56c54298mr1091188ybb.12.1712875065378; Thu, 11 Apr 2024 15:37:45 -0700 (PDT) Received: from executor.attlocal.net ([2600:1700:5eb5:1ba0:dc1f:cff:fef9:435b]) by smtp.gmail.com with ESMTPSA id q11-20020a25f90b000000b00dcbbea79ffcsm482744ybe.42.2024.04.11.15.37.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Apr 2024 15:37:45 -0700 (PDT) From: Greg Malysa To: u-boot@lists.denx.de Cc: Arturs.Artamonovs@analog.com, Vasileios.Bimpikas@analog.com, Utsav.Agarwal@analog.com, Ian Roberts , Nathan Barrett-Morrison , Greg Malysa , Apurva Nandan , Ashok Reddy Soma , Dhruva Gole , Igor Prusov , Jagan Teki , Jan Kiszka , Johan Jonker , Tejas Bhumkar , Tom Rini , Udit Kumar Subject: [PATCH 09/11] spi: cadence-quadspi: Add support for memory DMA channel transfers Date: Thu, 11 Apr 2024 18:36:53 -0400 Message-ID: <20240411223709.573-10-greg.malysa@timesys.com> X-Mailer: git-send-email 2.43.2 In-Reply-To: <20240411223709.573-1-greg.malysa@timesys.com> References: <20240411223709.573-1-greg.malysa@timesys.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean From: Ian Roberts On the SC59x platform, the Cadence SPI IP block can use memory DMA channels to execute transactions. Existing Cadence DMA support attempts appears to be SOC specific and not generic. Thus, framework to use the DMA subsystem was added. On the SC59x, DMA to the Cadence SPI block is connected via memory DMA instead of peripheral DMA. In addition, some of the memory DMA channels are recommended over others for better transaction performance. This initial implementation simply uses the recommended memory channel indicated from the device tree. Peripheral DMA support can be added later for platforms that need it. Co-developed-by: Nathan Barrett-Morrison Signed-off-by: Nathan Barrett-Morrison Signed-off-by: Greg Malysa Signed-off-by: Ian Roberts --- drivers/spi/cadence_qspi.c | 47 ++++++++++++++++ drivers/spi/cadence_qspi.h | 31 +++++++++-- drivers/spi/cadence_qspi_apb.c | 99 ++++++++++++++++++++++++++++++---- 3 files changed, 164 insertions(+), 13 deletions(-) diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index a2644d9e11..a5e921cae7 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -194,6 +196,42 @@ static int cadence_spi_set_speed(struct udevice *bus, uint hz) return 0; } +#if CONFIG_IS_ENABLED(DMA_CHANNELS) +static int cadence_spi_probe_dma(struct udevice *bus) +{ + struct cadence_spi_priv *priv = dev_get_priv(bus); + struct dma_dev_priv *dma_uc; + int hasdma; + int ret; + + hasdma = (ofnode_read_u32(dev_ofnode(bus), "dmas", NULL) == 0) && + (ofnode_read_u32(dev_ofnode(bus), "dma-names", NULL) == 0); + if (!hasdma) + return 0; + + ret = dma_get_by_name(bus, "dst", &priv->dstdma); + if (ret != 0) + return 0; + + dma_uc = dev_get_uclass_priv(priv->dstdma.dev); + + if (dma_uc->supported == DMA_SUPPORTS_MEM_TO_MEM) { + /* We were given a specific DMA channel that only + * supports mem-to-mem transactions. + */ + priv->hasdma = hasdma; + priv->ops.direct_read_copy = cadence_qspi_apb_read_copy_mdma; + priv->ops.direct_write_copy = cadence_qspi_apb_write_copy_mdma; + return 0; + } + + /* Todo: Implement device DMA channel modes when needed + * (DMA_SUPPORTS_MEM_TO_DEV, DMA_SUPPORTS_DEV_TO_MEM). + */ + return -ENOSYS; +} +#endif + static int cadence_spi_probe(struct udevice *bus) { struct cadence_spi_plat *plat = dev_get_plat(bus); @@ -219,6 +257,9 @@ static int cadence_spi_probe(struct udevice *bus) priv->tchsh_ns = plat->tchsh_ns; priv->tslch_ns = plat->tslch_ns; + priv->ops.direct_read_copy = cadence_qspi_apb_direct_read_copy; + priv->ops.direct_write_copy = cadence_qspi_apb_direct_write_copy; + if (IS_ENABLED(CONFIG_ZYNQMP_FIRMWARE)) xilinx_pm_request(PM_REQUEST_NODE, PM_DEV_OSPI, ZYNQMP_PM_CAPABILITY_ACCESS, ZYNQMP_PM_MAX_QOS, @@ -252,6 +293,12 @@ static int cadence_spi_probe(struct udevice *bus) priv->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC, priv->ref_clk_hz); + if (CONFIG_IS_ENABLED(DMA_CHANNELS)) { + ret = cadence_spi_probe_dma(bus); + if (ret) + return ret; + } + /* Versal and Versal-NET use spi calibration to set read delay */ if (CONFIG_IS_ENABLED(ARCH_VERSAL) || CONFIG_IS_ENABLED(ARCH_VERSAL_NET)) diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h index 5704f5a3f6..9c15d3c6df 100644 --- a/drivers/spi/cadence_qspi.h +++ b/drivers/spi/cadence_qspi.h @@ -223,7 +223,16 @@ struct cadence_spi_plat { u32 tchsh_ns; u32 tslch_ns; - bool is_dma; + bool is_dma; +}; + +struct cadence_spi_priv; + +struct cadence_drv_ops { + int (*direct_read_copy)(struct cadence_spi_priv *priv, + void *dst, u64 src, size_t len); + int (*direct_write_copy)(struct cadence_spi_priv *priv, + const void *src, u64 dst, size_t len); }; struct cadence_spi_priv { @@ -234,11 +243,17 @@ struct cadence_spi_priv { unsigned int fifo_depth; unsigned int fifo_width; unsigned int trigger_address; - fdt_addr_t ahbsize; + fdt_addr_t ahbsize; size_t cmd_len; u8 cmd_buf[32]; size_t data_len; + bool hasdma; +#if CONFIG_IS_ENABLED(DMA_CHANNELS) + struct dma dstdma; +#endif + struct cadence_drv_ops ops; + int qspi_is_init; unsigned int qspi_calibrated_hz; unsigned int qspi_calibrated_cs; @@ -253,8 +268,8 @@ struct cadence_spi_priv { u32 tsd2d_ns; u32 tchsh_ns; u32 tslch_ns; - u8 edge_mode; - u8 dll_mode; + u8 edge_mode; + u8 dll_mode; bool extra_dummy; bool ddr_init; bool is_decoded_cs; @@ -312,4 +327,12 @@ int cadence_qspi_versal_flash_reset(struct udevice *dev); ofnode cadence_qspi_get_subnode(struct udevice *dev); void cadence_qspi_apb_enable_linear_mode(bool enable); +int cadence_qspi_apb_read_copy_mdma(struct cadence_spi_priv *priv, + void *dst, u64 src, size_t len); +int cadence_qspi_apb_write_copy_mdma(struct cadence_spi_priv *priv, + const void *src, u64 dst, size_t len); +int cadence_qspi_apb_direct_read_copy(struct cadence_spi_priv *priv, + void *dst, u64 src, size_t len); +int cadence_qspi_apb_direct_write_copy(struct cadence_spi_priv *priv, + const void *src, u64 dst, size_t len); #endif /* __CADENCE_QSPI_H__ */ diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index 340889c271..90b9c558b2 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -29,9 +29,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -781,17 +783,15 @@ int cadence_qspi_apb_read_execute(struct cadence_spi_priv *priv, u64 from = op->addr.val; void *buf = op->data.buf.in; size_t len = op->data.nbytes; + int retval = 0; cadence_qspi_apb_enable_linear_mode(true); if (op->addr.nbytes && priv->use_dac_mode && (from + len < priv->ahbsize)) { - if (len < 256 || - dma_memcpy(buf, priv->ahbbase + from, len) < 0) { - memcpy_fromio(buf, priv->ahbbase + from, len); - } + retval = priv->ops.direct_read_copy(priv, buf, from, len); if (!cadence_qspi_wait_idle(priv->regbase)) - return -EIO; - return 0; + retval = -EIO; + return retval; } return cadence_qspi_apb_indirect_read_execute(priv, len, buf); @@ -968,13 +968,14 @@ int cadence_qspi_apb_write_execute(struct cadence_spi_priv *priv, u32 to = op->addr.val; const void *buf = op->data.buf.out; size_t len = op->data.nbytes; + int retval = 0; cadence_qspi_apb_enable_linear_mode(true); if (op->addr.nbytes && priv->use_dac_mode && (to + len < priv->ahbsize)) { - memcpy_toio(priv->ahbbase + to, buf, len); + retval = priv->ops.direct_write_copy(priv, buf, to, len); if (!cadence_qspi_wait_idle(priv->regbase)) - return -EIO; - return 0; + retval = -EIO; + return retval; } return cadence_qspi_apb_indirect_write_execute(priv, len, buf); @@ -999,3 +1000,83 @@ void cadence_qspi_apb_enter_xip(void *reg_base, char xip_dummy) reg |= (1 << CQSPI_REG_RD_INSTR_MODE_EN_LSB); writel(reg, reg_base + CQSPI_REG_RD_INSTR); } + +#if CONFIG_IS_ENABLED(DMA_CHANNELS) +static int cadence_qspi_apb_copy_mdma(struct udevice *dmadev, + void *dst, void *src, size_t len) +{ + struct dma_ops *ops = (struct dma_ops *)dmadev->driver->ops; + + /* Some transfers might not be aligned to cache boundaries. Align them + * for the cache operation while preserving the original transfer + * address. + */ + uintptr_t algn_dst_l = ((uintptr_t)dst / ARCH_DMA_MINALIGN) * + ARCH_DMA_MINALIGN; + uintptr_t algn_dst_h = ALIGN((uintptr_t)dst + len, ARCH_DMA_MINALIGN); + uintptr_t algn_src_l = ((uintptr_t)src / ARCH_DMA_MINALIGN) * + ARCH_DMA_MINALIGN; + uintptr_t algn_src_h = ALIGN((uintptr_t)src + len, ARCH_DMA_MINALIGN); + uintptr_t algn_len = max(algn_dst_h - algn_dst_l, + algn_src_h - algn_src_l); + + dma_addr_t dst_map = dma_map_single((void *)algn_dst_l, algn_len, + DMA_FROM_DEVICE); + dma_addr_t src_map = dma_map_single((void *)algn_src_l, algn_len, + DMA_TO_DEVICE); + + uintptr_t dma_dst = dst_map + ((uintptr_t)dst - algn_dst_l); + uintptr_t dma_src = src_map + ((uintptr_t)src - algn_src_l); + + int ret = ops->transfer(dmadev, DMA_MEM_TO_MEM, dma_dst, dma_src, len); + + dma_unmap_single(dst_map, algn_len, DMA_FROM_DEVICE); + dma_unmap_single(src_map, algn_len, DMA_TO_DEVICE); + + return ret; +} + +int cadence_qspi_apb_read_copy_mdma(struct cadence_spi_priv *priv, + void *dst, u64 src, size_t len) +{ + return cadence_qspi_apb_copy_mdma(priv->dstdma.dev, dst, + priv->ahbbase + src, len); +} + +int cadence_qspi_apb_write_copy_mdma(struct cadence_spi_priv *priv, + const void *src, u64 dst, size_t len) +{ + return cadence_qspi_apb_copy_mdma(priv->dstdma.dev, + priv->ahbbase + dst, + (void *)src, len); +} +#else +int cadence_qspi_apb_read_copy_mdma(struct cadence_spi_priv *priv, + void *dst, u64 src, size_t len) +{ + return -ENOSYS; +} + +int cadence_qspi_apb_write_copy_mdma(struct cadence_spi_priv *priv, + const void *src, u64 dst, size_t len) +{ + return -ENOSYS; +} +#endif + +int cadence_qspi_apb_direct_read_copy(struct cadence_spi_priv *priv, + void *dst, u64 src, size_t len) +{ + if (len < 256 || + dma_memcpy(dst, priv->ahbbase + src, len) < 0) { + memcpy_fromio(dst, priv->ahbbase + src, len); + } + return 0; +} + +int cadence_qspi_apb_direct_write_copy(struct cadence_spi_priv *priv, + const void *src, u64 dst, size_t len) +{ + memcpy_toio(priv->ahbbase + dst, src, len); + return 0; +} From patchwork Thu Apr 11 22:36:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg Malysa X-Patchwork-Id: 1922736 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=0DKiZUWS; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VFvkg5lbrz1yYL for ; Fri, 12 Apr 2024 08:39:07 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id E86778823C; Fri, 12 Apr 2024 00:37:53 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.b="0DKiZUWS"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 87D18881ED; Fri, 12 Apr 2024 00:37:49 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-yb1-xb2a.google.com (mail-yb1-xb2a.google.com [IPv6:2607:f8b0:4864:20::b2a]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 424B38758B for ; Fri, 12 Apr 2024 00:37:47 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=greg.malysa@timesys.com Received: by mail-yb1-xb2a.google.com with SMTP id 3f1490d57ef6-ddda842c399so323542276.3 for ; Thu, 11 Apr 2024 15:37:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=timesys-com.20230601.gappssmtp.com; s=20230601; t=1712875066; x=1713479866; darn=lists.denx.de; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Daz87Jsd3MES1tYDBM2LSyxiFUcw/7q9uHvplAxcodY=; b=0DKiZUWSeRnoZJOGjfBCgOjan5X+nOUPQOXjEXHm52jp3Bmh4t4+70DVuGUZ/CzFSN hD2mIcPo2sovkYB7q01hf+ysVuK/CjkMYk/cPfVhh+NVvpwzignxKooxR3QZyfxfJaGN 67gRCtOBWgn471k7tX6CKStZ7WYaM2PSnQJ4NajzzixzKP6GItTnJKumvWDJ5VNpvX23 83uGgfTU47DuLjZ+oCf1X9p22uI75hzsIaK4/z11RN+Mwk4XONGCnxuOhihHTl6qzTx/ VJxIXFr+oLdmOjT6Ef+Ofw/OcI47lpv0Ls7zYC1QueCLvPMzogVTJKlTtHApL/ANy1BB yE9Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712875066; x=1713479866; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Daz87Jsd3MES1tYDBM2LSyxiFUcw/7q9uHvplAxcodY=; b=WkeM2ep77mtR45yv7BunHHa7LZODHEQYlxx39X7+KtpJD1AkAHLeNPJa0FaEgtiygp GN80TNiT4noKjspWkANwdUydWQXFaYsSJrDD2dTY8UDr8L7JHqhmcE/s/Qfv3OC5UAFq UUsVbDVzuwI/3pJ/GIq6yICUWa/C5JRCVZabCzmVlehUyj7nBKb+CrLz5AWl738PJ6os yHzrxdDoVngyd/Pwkecq+M3F+zBWcHRHm4Jl2uQooI5D1HG78nwwU4N0JeHbi71eXDq4 gyEpM/IDHYYD78t2TQ2Id0PjOfKKmCCRtxFe8kZfvIu4FiXS5UaxL2j+AM4j6fnP2URo FGuQ== X-Gm-Message-State: AOJu0YxScVcyfu497krxB7SrZR3Lau4w/r1c8+Vnz7KeLt+3YpjzKV6W 5Blqu937ILfE+DM1Oiz+PuHlymD1GVnygRVxXzp3EWdOOovYzQhyMBlE+5maV99MbTDQgkREDbL vnQ== X-Google-Smtp-Source: AGHT+IHodI7c3/oCP5vXFrwVBiSDx945Tj7yByl1aRYQHV+CO/ZdpDTBH9NJ6C/dyGhgXPapdCJGMw== X-Received: by 2002:a05:6902:e13:b0:dc7:4367:2527 with SMTP id df19-20020a0569020e1300b00dc743672527mr1114712ybb.49.1712875066010; Thu, 11 Apr 2024 15:37:46 -0700 (PDT) Received: from executor.attlocal.net ([2600:1700:5eb5:1ba0:dc1f:cff:fef9:435b]) by smtp.gmail.com with ESMTPSA id q11-20020a25f90b000000b00dcbbea79ffcsm482744ybe.42.2024.04.11.15.37.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Apr 2024 15:37:45 -0700 (PDT) From: Greg Malysa To: u-boot@lists.denx.de Cc: Arturs.Artamonovs@analog.com, Vasileios.Bimpikas@analog.com, Utsav.Agarwal@analog.com, Ian Roberts , Nathan Barrett-Morrison , Greg Malysa , Apurva Nandan , Ashok Reddy Soma , Igor Prusov , Jagan Teki , Johan Jonker , Neha Malcom Francis , Tejas Bhumkar , Tom Rini , Udit Kumar Subject: [PATCH 10/11] spi: cadence-quadspi: Add DT control of max Read Delay Capture value Date: Thu, 11 Apr 2024 18:36:54 -0400 Message-ID: <20240411223709.573-11-greg.malysa@timesys.com> X-Mailer: git-send-email 2.43.2 In-Reply-To: <20240411223709.573-1-greg.malysa@timesys.com> References: <20240411223709.573-1-greg.malysa@timesys.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean From: Ian Roberts On some SOCs (eg sc59x), attempting to use too high of a Read Delay Capture value can cause the controller DMA to lock up. Thus, add a device tree configuration property to allow controlling the max Read Delay Capture value. Co-developed-by: Nathan Barrett-Morrison Signed-off-by: Nathan Barrett-Morrison Signed-off-by: Greg Malysa Signed-off-by: Ian Roberts --- doc/device-tree-bindings/spi/spi-cadence.txt | 2 ++ drivers/spi/cadence_qspi.c | 9 ++++++++- drivers/spi/cadence_qspi.h | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/device-tree-bindings/spi/spi-cadence.txt b/doc/device-tree-bindings/spi/spi-cadence.txt index 69e02c1c4b..9bd7ef8bed 100644 --- a/doc/device-tree-bindings/spi/spi-cadence.txt +++ b/doc/device-tree-bindings/spi/spi-cadence.txt @@ -29,3 +29,5 @@ connected flash properties select (n_ss_out). - cdns,tslch-ns : Delay in master reference clocks between setting n_ss_out low and first bit transfer +- cdns,max-read-delay : Max safe value to use for the read capture delay + during auto calibration. diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index a5e921cae7..3778a469d4 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -104,7 +104,7 @@ static int spi_calibration(struct udevice *bus, uint hz) /* use back the intended clock and find low range */ cadence_spi_write_speed(bus, hz); - for (i = 0; i < CQSPI_READ_CAPTURE_MAX_DELAY; i++) { + for (i = 0; i < priv->max_read_delay; i++) { /* Disable QSPI */ cadence_qspi_apb_controller_disable(base); @@ -246,6 +246,7 @@ static int cadence_spi_probe(struct udevice *bus) priv->fifo_depth = plat->fifo_depth; priv->fifo_width = plat->fifo_width; priv->trigger_address = plat->trigger_address; + priv->max_read_delay = plat->max_read_delay; priv->read_delay = plat->read_delay; priv->ahbsize = plat->ahbsize; priv->max_hz = plat->max_hz; @@ -456,6 +457,10 @@ static int cadence_spi_of_to_plat(struct udevice *bus) plat->is_dma = dev_read_bool(bus, "cdns,is-dma"); + plat->max_read_delay = dev_read_u32_default(bus, + "cdns,max-read-delay", + CQSPI_READ_CAPTURE_MAX_DELAY); + /* All other parameters are embedded in the child node */ subnode = cadence_qspi_get_subnode(bus); if (!ofnode_valid(subnode)) { @@ -484,6 +489,8 @@ static int cadence_spi_of_to_plat(struct udevice *bus) */ plat->read_delay = ofnode_read_s32_default(subnode, "cdns,read-delay", -1); + if (plat->read_delay > plat->max_read_delay) + plat->read_delay = plat->max_read_delay; debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n", __func__, plat->regbase, plat->ahbbase, plat->max_hz, diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h index 9c15d3c6df..d7a02f0870 100644 --- a/drivers/spi/cadence_qspi.h +++ b/drivers/spi/cadence_qspi.h @@ -214,6 +214,7 @@ struct cadence_spi_plat { fdt_addr_t ahbsize; bool use_dac_mode; int read_delay; + int max_read_delay; /* Flash parameters */ u32 page_size; @@ -260,6 +261,7 @@ struct cadence_spi_priv { unsigned int previous_hz; u32 wr_delay; int read_delay; + int max_read_delay; struct reset_ctl_bulk *resets; u32 page_size; From patchwork Thu Apr 11 22:36:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Greg Malysa X-Patchwork-Id: 1922738 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.a=rsa-sha256 header.s=20230601 header.b=21e8evKF; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VFvl21ng4z1yYL for ; Fri, 12 Apr 2024 08:39:26 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 4457C8824A; Fri, 12 Apr 2024 00:37:55 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=timesys-com.20230601.gappssmtp.com header.i=@timesys-com.20230601.gappssmtp.com header.b="21e8evKF"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 6076C8823C; Fri, 12 Apr 2024 00:37:53 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-yb1-xb34.google.com (mail-yb1-xb34.google.com [IPv6:2607:f8b0:4864:20::b34]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 1B436881B2 for ; Fri, 12 Apr 2024 00:37:49 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=timesys.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=greg.malysa@timesys.com Received: by mail-yb1-xb34.google.com with SMTP id 3f1490d57ef6-dcc80d6004bso332766276.0 for ; Thu, 11 Apr 2024 15:37:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=timesys-com.20230601.gappssmtp.com; s=20230601; t=1712875068; x=1713479868; darn=lists.denx.de; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=+jOhTNeiPAIhyg6vTb/No1Ra6lSgdjo92WJ/ddVuxLM=; b=21e8evKFxHewSN5ZF7d/o2mdIP8q4HdBR0pn84Q8Wnpy2L1DllOD3V18QPi1xrzvF1 kmvvxSbldtyGFC4/rRheK/5BlnC88KlpZRCrrBGf0aU0OlZ6KYs4RG678awYJ2rQnnIO LmA42LXFeVxdudyunLqRXiMZC/iLpeoUnOta0jKoFodD65fkSpZ8Qu5r6f8vErpJbM61 GRL9vsOW/65xEhrks2krG27lfSuFPrR6gmJiqQIjM3o7Wzdp/LkX2JWE+hs76UTKtGMU nhpXRnWAumsWWWDbDsEQJryZzkC6ZBIWK0fbCZv/EC4QHcFblp3hA89ZVjMRZo+4D3A8 9yxA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1712875068; x=1713479868; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=+jOhTNeiPAIhyg6vTb/No1Ra6lSgdjo92WJ/ddVuxLM=; b=Qo+hE7Hf5Eo1YMPRd3Ttg0dCUa0vh7q7LiZYvXK/v7fnhVI9HFAD77IWHb71Yodzdv 2C5r1N0aaVE1Q0WvmHZ9zVPaOH7SvhD2olRxqgDS8zuZTmPwfl0yemN57fb9nyr7wYK4 K41sXwfPxzP2UjpVFHVV8ziIQdx/Yo6tH9w5zLnRwipFzy2NUQ5dIS+PWwf2i4cW6iUJ Y4C8D1DBCIoCdiXqhprNYzGYD9N+Qw7+xEfKRVABrHUc345BfRsZv4eD9sm+J60GGlgg xWLKv2AfD+FUDG4jbRuhmTzZ27pESe7YiQ0UnCAA0nKv29HvqlSJ/Q9deOrPVopVmhPC UK9w== X-Gm-Message-State: AOJu0YyEWN4iPU2+bsMHoQkp8F5rFrVC0CjCiVJ9NLjoOUZNrw0YoSKc NH42wJee0eQqyQQpoYV5XnJ9lCXqKucHswnJbHeCmTJ2uWdELgS5fqQ4MPJJFkH1D/7S25Gz/Wo lsg== X-Google-Smtp-Source: AGHT+IGk12XWaXlKqZPu0cW8uZPSVDp7tBMeqM1BKt4KY3fvYQyqm4dGuBKaOSQh0vgtrdpvKJcoWw== X-Received: by 2002:a25:664c:0:b0:dcf:6122:ccec with SMTP id z12-20020a25664c000000b00dcf6122ccecmr910943ybm.36.1712875067463; Thu, 11 Apr 2024 15:37:47 -0700 (PDT) Received: from executor.attlocal.net ([2600:1700:5eb5:1ba0:dc1f:cff:fef9:435b]) by smtp.gmail.com with ESMTPSA id q11-20020a25f90b000000b00dcbbea79ffcsm482744ybe.42.2024.04.11.15.37.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Apr 2024 15:37:46 -0700 (PDT) From: Greg Malysa To: u-boot@lists.denx.de Cc: Arturs.Artamonovs@analog.com, Vasileios.Bimpikas@analog.com, Utsav.Agarwal@analog.com, Ian Roberts , Nathan Barrett-Morrison , Greg Malysa , Apurva Nandan , Ashok Reddy Soma , Dhruva Gole , Igor Prusov , Jagan Teki , Jan Kiszka , Johan Jonker , Tejas Bhumkar , Tom Rini , Udit Kumar Subject: [PATCH 11/11] spi: cadence-quadspi: Implement high speed calibration Date: Thu, 11 Apr 2024 18:36:55 -0400 Message-ID: <20240411223709.573-12-greg.malysa@timesys.com> X-Mailer: git-send-email 2.43.2 In-Reply-To: <20240411223709.573-1-greg.malysa@timesys.com> References: <20240411223709.573-1-greg.malysa@timesys.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean From: Ian Roberts Implement the spi-mem calibration hook for high speed flash operation for use on the SC59x SOCs. The Cadence controller IP has support for the DQS signal and a PHY mode that facilitates speeds greater than 50MHz. At high speeds, the IO lines must be calibrated for signal propagation delay. This calibration is intended to be executed in the final IO configuration mode. That is, if 8-lane DDR IO operation is the use case, calibration must occur while that mode is enabled. For example, there might be excess noise on a single IO lane while operating in 8-lane mode that then limits the speed of the entire bus. SPI bus drivers are not involved in the control of the SPI flash chip operating mode, and performing the switch is done by the SPI-nor subsystem. To add to this complexity, different IO modes use different command sets, and different flash chips may also modify this command set further. Thus, we must lean on the spi-nor subsystem through the spi-mem calibration function for the most portable implementation of calibration. The original calibration code in this driver only calibrates the Read Delay Capture value, over a single lane, over only 3 bytes in the readid command. This produces unreliable calibrations in single IO mode and is unusable in DDR or multi-IO modes. The prior calibration implementation is replaced in favor of the new approach when CONFIG_SPI_FLASH_HS_CALIB is defined. The old implementation is still available when not defined. However, the previous implementation has been tweaked to take advantage of code reuse and to fix an invalid SPI chip select read from the dm_spi_ops set_speed callback. It would always return the same invalid CS number, never triggering a recalibration if the chip changes. However, this driver was not implemented with support for multiple chips on the bus in mind anyway. For example: * of_to_plat only scans the first subnode for flash-specific configuration, and thus only a single copy of this info is declared in the plat and priv structs. * Defining cdns,read-delay overrides any automatic recalibration that would normally occur from a chip select change to this single value. A few additional comments, checks, and renames have been made to make this more clear. The new calibration implementation explicitly disallows changing the chip after calibration until it is properly implemented in the driver. The legacy implementation will still allow the chip to change but now correctly trigger a recalibration after the chip select changes. The issue with cdns,read-delay and multi-IO modes is only fixed in the new implementation. Co-developed-by: Nathan Barrett-Morrison Signed-off-by: Nathan Barrett-Morrison Signed-off-by: Greg Malysa Signed-off-by: Ian Roberts --- doc/device-tree-bindings/spi/spi-cadence.txt | 9 + drivers/spi/cadence_qspi.c | 392 +++++++++++++------ drivers/spi/cadence_qspi.h | 77 ++-- drivers/spi/cadence_qspi_apb.c | 327 ++++++++++++++-- 4 files changed, 641 insertions(+), 164 deletions(-) diff --git a/doc/device-tree-bindings/spi/spi-cadence.txt b/doc/device-tree-bindings/spi/spi-cadence.txt index 9bd7ef8bed..4ee0b628e3 100644 --- a/doc/device-tree-bindings/spi/spi-cadence.txt +++ b/doc/device-tree-bindings/spi/spi-cadence.txt @@ -31,3 +31,12 @@ connected flash properties n_ss_out low and first bit transfer - cdns,max-read-delay : Max safe value to use for the read capture delay during auto calibration. +- cdns,spi-calib-frequency : Max safe SPI clock frequency to use before bus + calibration is performed. +- cdns,dqs : Enable use of the DQS signal with the flash chip. +- cdns,phy : Enable use of the high-speed PHY feature with the + flash chip. Generally required for speeds higher + than 50MHz. +- cdns,read-delay : Optional pre-calibrated Read Delay Capture value. +- cdns,phyrxdly : Optional pre-calibrated PHY RX Delay value. +- cdns,phytxdly : Optional pre-calibrated PHY TX Delay value. diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index 3778a469d4..1db3167a5b 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,20 @@ #define CQSPI_READ 2 #define CQSPI_WRITE 3 +static bool is_calibrated(struct cadence_spi_priv *priv, + struct spi_slave *slave) +{ + return (priv->qspi_calibrated_hz == priv->req_hz) && + (priv->qspi_calibrated_cs == spi_chip_select(slave->dev)); +} + +static void set_calibrated(struct cadence_spi_priv *priv, + struct spi_slave *slave) +{ + priv->qspi_calibrated_hz = priv->req_hz; + priv->qspi_calibrated_cs = spi_chip_select(slave->dev); +} + __weak int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv, const struct spi_mem_op *op) { @@ -49,86 +64,92 @@ __weak ofnode cadence_qspi_get_subnode(struct udevice *dev) static int cadence_spi_write_speed(struct udevice *bus, uint hz) { struct cadence_spi_priv *priv = dev_get_priv(bus); + struct cadence_spi_plat *plat = dev_get_plat(bus); cadence_qspi_apb_config_baudrate_div(priv->regbase, priv->ref_clk_hz, hz); /* Reconfigure delay timing if speed is changed. */ cadence_qspi_apb_delay(priv->regbase, priv->ref_clk_hz, hz, - priv->tshsl_ns, priv->tsd2d_ns, - priv->tchsh_ns, priv->tslch_ns); - - return 0; + plat->tshsl_ns, plat->tsd2d_ns, + plat->tchsh_ns, plat->tslch_ns); } -static int cadence_spi_read_id(struct cadence_spi_priv *priv, u8 len, - u8 *idcode) +void cadence_spi_update_speed(struct udevice *bus, bool calibrated) { - int err; + struct cadence_spi_priv *priv = dev_get_priv(bus); + struct cadence_spi_plat *plat = dev_get_plat(bus); + void *regb = priv->regbase; + uint hz = priv->req_hz; + u32 rdc = 0; - struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x9F, 1), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(len, idcode, 1)); + if (!calibrated) + hz = plat->calib_hz; - err = cadence_qspi_apb_command_read_setup(priv, &op); - if (!err) - err = cadence_qspi_apb_command_read(priv, &op); + /* Disable QSPI */ + cadence_qspi_apb_controller_disable(regb); + + if (calibrated) { + rdc = priv->read_delay; + if (plat->phy_support && plat->use_phy) { + cadence_qspi_apb_enable_phy(regb, true); + if (plat->slow_phy_tx) + hz /= 4; + } + } else if (plat->phy_support) { + cadence_qspi_apb_enable_phy(regb, false); + } - return err; + cadence_spi_write_speed(bus, hz); + cadence_qspi_apb_readdata_capture(priv, 1, rdc); + + if (plat->phy_support) + cadence_qspi_apb_set_phy_cfg(regb, + priv->phyrxdly, priv->phytxdly); + + /* Enable QSPI */ + cadence_qspi_apb_controller_enable(regb); } -/* Calibration sequence to determine the read data capture delay register */ -static int spi_calibration(struct udevice *bus, uint hz) +/* Calibration sequence to determine the read data capture delay register + * Returns 0 on success, negative error code. + */ +static int spi_non_phy_calibrate(struct spi_slave *slave, + int (*test_read_fn)(struct spi_slave *)) { + struct udevice *bus = slave->dev->parent; struct cadence_spi_priv *priv = dev_get_priv(bus); + struct cadence_spi_plat *plat = dev_get_plat(bus); void *base = priv->regbase; - unsigned int idcode = 0, temp = 0; int err = 0, i, range_lo = -1, range_hi = -1; - /* start with slowest clock (1 MHz) */ - cadence_spi_write_speed(bus, 1000000); - - /* configure the read data capture delay register to 0 */ - cadence_qspi_apb_readdata_capture(base, 1, 0); - - /* Enable QSPI */ - cadence_qspi_apb_controller_enable(base); - - /* read the ID which will be our golden value */ - err = cadence_spi_read_id(priv, 3, (u8 *)&idcode); - if (err) { - puts("SF: Calibration failed (read)\n"); - return err; - } - /* use back the intended clock and find low range */ - cadence_spi_write_speed(bus, hz); - for (i = 0; i < priv->max_read_delay; i++) { + cadence_spi_update_speed(bus, true); + + for (i = 0; i < plat->max_read_delay; i++) { /* Disable QSPI */ cadence_qspi_apb_controller_disable(base); /* reconfigure the read data capture delay register */ - cadence_qspi_apb_readdata_capture(base, 1, i); + cadence_qspi_apb_readdata_capture(priv, 1, i); /* Enable back QSPI */ cadence_qspi_apb_controller_enable(base); - /* issue a RDID to get the ID value */ - err = cadence_spi_read_id(priv, 3, (u8 *)&temp); - if (err) { + err = test_read_fn(slave); + if (err < 0) { puts("SF: Calibration failed (read)\n"); - return err; + goto err; } /* search for range lo */ - if (range_lo == -1 && temp == idcode) { + if (range_lo == -1 && err == 0) { range_lo = i; continue; } /* search for range hi */ - if (range_lo != -1 && temp != idcode) { + if (range_lo != -1 && err) { range_hi = i - 1; break; } @@ -137,59 +158,169 @@ static int spi_calibration(struct udevice *bus, uint hz) if (range_lo == -1) { puts("SF: Calibration failed (low range)\n"); - return err; + err = -EIO; + goto err; } /* Disable QSPI for subsequent initialization */ cadence_qspi_apb_controller_disable(base); - /* configure the final value for read data capture delay register */ - cadence_qspi_apb_readdata_capture(base, 1, (range_hi + range_lo) / 2); - debug("SF: Read data capture delay calibrated to %i (%i - %i)\n", - (range_hi + range_lo) / 2, range_lo, range_hi); + priv->read_delay = (range_hi + range_lo) / 2; - /* just to ensure we do once only when speed or chip select change */ - priv->qspi_calibrated_hz = hz; - priv->qspi_calibrated_cs = spi_chip_select(bus); + /* configure the final value for read data capture delay register */ + cadence_qspi_apb_readdata_capture(priv, 1, priv->read_delay); + debug("SF: Calibration: read-delay=%u (%i - %i)\n", + priv->read_delay, range_lo, range_hi); return 0; + +err: + cadence_spi_update_speed(bus, false); + return err; } -static int cadence_spi_set_speed(struct udevice *bus, uint hz) +#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB) +static void clr_calibrated(struct cadence_spi_priv *priv, + struct spi_slave *slave) { + priv->qspi_calibrated_hz = 0; +} + +/* Returns 0 on success, negative error code. + */ +int cadence_spi_calibrate(struct spi_slave *slave, + int (*test_read_fn)(struct spi_slave *)) +{ + struct udevice *bus = slave->dev->parent; struct cadence_spi_priv *priv = dev_get_priv(bus); - int err; + struct cadence_spi_plat *plat = dev_get_plat(bus); + int err = 0; - if (!hz || hz > priv->max_hz) - hz = priv->max_hz; - /* Disable QSPI */ - cadence_qspi_apb_controller_disable(priv->regbase); + if (!test_read_fn) { + if (priv->qspi_calibrated_hz && + priv->qspi_calibrated_cs != spi_chip_select(slave->dev)) { + debug("%s: multiple chips on the bus not yet implemented\n", + __func__); + return -ENOSYS; + } + clr_calibrated(priv, slave); + cadence_spi_update_speed(bus, false); + return 0; + } - /* - * If the device tree already provides a read delay value, use that - * instead of calibrating. + /* todo: Allow recalibrations. This could be useful in the event + * of a communication CRC failure. A recalibration could improve + * signal quality. This however, requires implementing communication + * CRC features in spi-nor. */ - if (priv->read_delay >= 0) { - cadence_spi_write_speed(bus, hz); - cadence_qspi_apb_readdata_capture(priv->regbase, 1, - priv->read_delay); - } else if (priv->previous_hz != hz || - priv->qspi_calibrated_hz != hz || - priv->qspi_calibrated_cs != spi_chip_select(bus)) { - /* - * Calibration required for different current SCLK speed, - * requested SCLK speed or chip select - */ - err = spi_calibration(bus, hz); - if (err) - return err; + if (is_calibrated(priv, slave)) + return 0; - /* prevent calibration run when same as previous request */ - priv->previous_hz = hz; + if (plat->calib_cfg) { + set_calibrated(priv, slave); + cadence_spi_update_speed(bus, true); + return 0; } - /* Enable QSPI */ - cadence_qspi_apb_controller_enable(priv->regbase); + if (plat->use_phy) + err = cadence_qspi_apb_phy_calibrate(slave, test_read_fn); + else + err = spi_non_phy_calibrate(slave, test_read_fn); + if (err) + return err; + + set_calibrated(priv, slave); + + return 0; +} +#else +/* NOTE: This will not work as expected if spi-nor has put the chip into a + * multi-io or DDR mode. Use CONFIG_SPI_FLASH_HS_CALIB. + * Calibrating from only 3 bytes is also not enough to get a reliable + * calibration range. + */ +static int cadence_spi_read_id(struct cadence_spi_priv *priv, u8 len, + u8 *idcode) +{ + int err; + + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x9F, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(len, idcode, 1)); + + err = cadence_qspi_apb_command_read_setup(priv, &op); + if (!err) + err = cadence_qspi_apb_command_read(priv, &op); + return err; +} + +/* Returns 0 on success, negative error code. + */ +static int cadence_spi_legacy_non_phy_calib_chk(struct spi_slave *slave) +{ + struct udevice *bus = slave->dev->parent; + struct cadence_spi_priv *priv = dev_get_priv(bus); + u32 temp = 0; + + int err = cadence_spi_read_id(priv, 3, (u8 *)&temp); + + if (err) + return err; + + return temp != priv->chipid; +} + +/* Calibration sequence to determine the read data capture delay register + * Returns 0 on success, negative error code. + */ +static int legacy_spi_calibration(struct spi_slave *slave) +{ + struct udevice *bus = slave->dev->parent; + struct cadence_spi_priv *priv = dev_get_priv(bus); + struct cadence_spi_plat *plat = dev_get_plat(bus); + int err = 0; + + if (is_calibrated(priv, slave)) { + return 0; + } else if (plat->calib_cfg) { + set_calibrated(priv, slave); + cadence_spi_update_speed(bus, true); + return 0; + } + + cadence_spi_update_speed(bus, false); + + /* read the ID which will be our golden value */ + err = cadence_spi_read_id(priv, 3, (u8 *)&priv->chipid); + if (err) { + puts("SF: Calibration failed (read)\n"); + return err; + } + + err = spi_non_phy_calibrate(slave, cadence_spi_legacy_non_phy_calib_chk); + if (err) + return err; + + set_calibrated(priv, slave); + + return 0; +} +#endif + +static int cadence_spi_set_speed(struct udevice *bus, uint hz) +{ + struct cadence_spi_priv *priv = dev_get_priv(bus); + /* + * In the high speed calib case, clibration clearing will then apply + * the max non-calibrated speed. + * When calibration occurs later, it will then apply the full requested + * speed. + * + * In the legacy calibration case, exec_op calls check if the new + * speed needs to be applied. + */ + priv->req_hz = hz; debug("%s: speed=%d\n", __func__, hz); @@ -239,6 +370,7 @@ static int cadence_spi_probe(struct udevice *bus) struct clk clk; int ret; + priv->plat = plat; priv->regbase = plat->regbase; priv->ahbbase = plat->ahbbase; priv->is_dma = plat->is_dma; @@ -246,17 +378,10 @@ static int cadence_spi_probe(struct udevice *bus) priv->fifo_depth = plat->fifo_depth; priv->fifo_width = plat->fifo_width; priv->trigger_address = plat->trigger_address; - priv->max_read_delay = plat->max_read_delay; - priv->read_delay = plat->read_delay; priv->ahbsize = plat->ahbsize; - priv->max_hz = plat->max_hz; - - priv->page_size = plat->page_size; - priv->block_size = plat->block_size; - priv->tshsl_ns = plat->tshsl_ns; - priv->tsd2d_ns = plat->tsd2d_ns; - priv->tchsh_ns = plat->tchsh_ns; - priv->tslch_ns = plat->tslch_ns; + priv->read_delay = plat->read_delay; + priv->phyrxdly = plat->phyrxdly; + priv->phytxdly = plat->phytxdly; priv->ops.direct_read_copy = cadence_qspi_apb_direct_read_copy; priv->ops.direct_write_copy = cadence_qspi_apb_direct_write_copy; @@ -354,6 +479,33 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi, cadence_qspi_apb_chipselect(base, spi_chip_select(spi->dev), priv->is_decoded_cs); + /* todo: Due to there only being 1 declaration of per-flash parameters, + * this driver only ever correctly supported 1 chip on the bus. + * Per-flash data must be moved into a data structure that can lookup + * by chip select. + */ +#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB) + if (priv->qspi_calibrated_hz && + priv->qspi_calibrated_cs != spi_chip_select(spi->dev)) { + debug("%s: multiple chips on the bus not yet implemented\n", + __func__); + return -ENOSYS; + } else if (is_calibrated(priv, spi) && + priv->qspi_calibrated_hz != priv->req_hz) { + debug("%s: speed change after calibration not yet supported\n", + __func__); + return -ENOSYS; + } +#else + /* Regardless of the above, attempt a recalib anyway. */ + if (!is_calibrated(priv, spi) || + priv->qspi_calibrated_hz != priv->req_hz) { + err = legacy_spi_calibration(spi); + if (err) + return err; + } +#endif + if (op->data.dir == SPI_MEM_DATA_IN && op->data.buf.in) { /* * Performing reads in DAC mode forces to read minimum 4 bytes @@ -441,6 +593,7 @@ static int cadence_spi_of_to_plat(struct udevice *bus) { struct cadence_spi_plat *plat = dev_get_plat(bus); struct cadence_spi_priv *priv = dev_get_priv(bus); + struct cadence_spi_plat *chip = plat; ofnode subnode; plat->regbase = devfdt_get_addr_index_ptr(bus, 0); @@ -457,6 +610,12 @@ static int cadence_spi_of_to_plat(struct udevice *bus) plat->is_dma = dev_read_bool(bus, "cdns,is-dma"); + if (CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB)) + plat->phy_support = bus->driver_data & CQSPI_HW_SUPPORTS_PHY; + + plat->slow_phy_tx = plat->phy_support && (bus->driver_data + & CQSPI_QUIRK_SLOW_PHY_TX_DMA); + plat->max_read_delay = dev_read_u32_default(bus, "cdns,max-read-delay", CQSPI_READ_CAPTURE_MAX_DELAY); @@ -464,37 +623,41 @@ static int cadence_spi_of_to_plat(struct udevice *bus) /* All other parameters are embedded in the child node */ subnode = cadence_qspi_get_subnode(bus); if (!ofnode_valid(subnode)) { - printf("Error: subnode with SPI flash config missing!\n"); + debug("Error: subnode with SPI flash config missing!\n"); return -ENODEV; } - /* Use 500 KHz as a suitable default */ - plat->max_hz = ofnode_read_u32_default(subnode, "spi-max-frequency", - 500000); - /* Read other parameters from DT */ - plat->page_size = ofnode_read_u32_default(subnode, "page-size", 256); - plat->block_size = ofnode_read_u32_default(subnode, "block-size", 16); - plat->tshsl_ns = ofnode_read_u32_default(subnode, "cdns,tshsl-ns", + chip->page_size = ofnode_read_u32_default(subnode, "page-size", 256); + chip->block_size = ofnode_read_u32_default(subnode, "block-size", 16); + chip->tshsl_ns = ofnode_read_u32_default(subnode, "cdns,tshsl-ns", 200); - plat->tsd2d_ns = ofnode_read_u32_default(subnode, "cdns,tsd2d-ns", + chip->tsd2d_ns = ofnode_read_u32_default(subnode, "cdns,tsd2d-ns", 255); - plat->tchsh_ns = ofnode_read_u32_default(subnode, "cdns,tchsh-ns", 20); - plat->tslch_ns = ofnode_read_u32_default(subnode, "cdns,tslch-ns", 20); - /* - * Read delay should be an unsigned value but we use a signed integer - * so that negative values can indicate that the device tree did not - * specify any signed values and we need to perform the calibration - * sequence to find it out. - */ - plat->read_delay = ofnode_read_s32_default(subnode, "cdns,read-delay", - -1); - if (plat->read_delay > plat->max_read_delay) - plat->read_delay = plat->max_read_delay; + chip->tchsh_ns = ofnode_read_u32_default(subnode, "cdns,tchsh-ns", 20); + chip->tslch_ns = ofnode_read_u32_default(subnode, "cdns,tslch-ns", 20); + + chip->calib_hz = ofnode_read_u32_default(subnode, + "cdns,spi-calib-frequency", + 1000000); + + chip->use_dqs = ofnode_read_bool(subnode, "cdns,dqs"); + chip->use_phy = ofnode_read_bool(subnode, "cdns,phy") && plat->phy_support; + + if (!ofnode_read_u32(subnode, "cdns,read-delay", &chip->read_delay)) + chip->calib_cfg |= true; + if (chip->read_delay > plat->max_read_delay) + chip->read_delay = plat->max_read_delay; + + if (!ofnode_read_u32(subnode, "cdns,phyrxdly", &chip->phyrxdly) || + !ofnode_read_u32(subnode, "cdns,phytxdly", &chip->phytxdly)) { + chip->calib_cfg |= true; + if (!chip->use_phy) + debug("PHY delays configured but PHY mode is not enabled!\n"); + } - debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n", - __func__, plat->regbase, plat->ahbbase, plat->max_hz, - plat->page_size); + debug("%s: regbase=%p ahbbase=%p page-size=%d\n", + __func__, plat->regbase, plat->ahbbase, plat->page_size); return 0; } @@ -502,6 +665,9 @@ static int cadence_spi_of_to_plat(struct udevice *bus) static const struct spi_controller_mem_ops cadence_spi_mem_ops = { .exec_op = cadence_spi_mem_exec_op, .supports_op = cadence_spi_mem_supports_op, +#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB) + .calibrate = cadence_spi_calibrate, +#endif }; static const struct dm_spi_ops cadence_spi_ops = { @@ -517,6 +683,8 @@ static const struct dm_spi_ops cadence_spi_ops = { static const struct udevice_id cadence_spi_ids[] = { { .compatible = "cdns,qspi-nor" }, { .compatible = "ti,am654-ospi" }, + { .compatible = "adi,sc59x-ospi", .data = + CQSPI_HW_SUPPORTS_PHY | CQSPI_QUIRK_SLOW_PHY_TX_DMA }, { } }; diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h index d7a02f0870..d38dee63d4 100644 --- a/drivers/spi/cadence_qspi.h +++ b/drivers/spi/cadence_qspi.h @@ -11,11 +11,17 @@ #include #include +/* HWcaps */ +#define CQSPI_HW_SUPPORTS_PHY BIT(0) +/* Quirks */ +#define CQSPI_QUIRK_SLOW_PHY_TX_DMA BIT(16) + #define CQSPI_IS_ADDR(cmd_len) (cmd_len > 1 ? 1 : 0) #define CQSPI_NO_DECODER_MAX_CS 4 #define CQSPI_DECODER_MAX_CS 16 #define CQSPI_READ_CAPTURE_MAX_DELAY 16 +#define CQSPI_PHY_DLL_MAX_DELAY 0x7f #define CQSPI_REG_POLL_US 1 /* 1us */ #define CQSPI_REG_RETRY 10000 @@ -53,6 +59,7 @@ #define CQSPI_REG_CONFIG_CHIPSELECT_LSB 10 #define CQSPI_REG_CONFIG_BAUD_LSB 19 #define CQSPI_REG_CONFIG_DTR_PROTO BIT(24) +#define CQSPI_REG_CONFIG_PIPELINE_PHY_EN_MASK BIT(25) #define CQSPI_REG_CONFIG_DUAL_OPCODE BIT(30) #define CQSPI_REG_CONFIG_IDLE_LSB 31 #define CQSPI_REG_CONFIG_CHIPSELECT_MASK 0xF @@ -89,9 +96,12 @@ #define CQSPI_REG_RD_DATA_CAPTURE 0x10 #define CQSPI_REG_RD_DATA_CAPTURE_BYPASS BIT(0) +#define CQSPI_REG_RD_DATA_CAPTURE_SAMPLE_EDGE BIT(5) #define CQSPI_REG_READCAPTURE_DQS_ENABLE BIT(8) #define CQSPI_REG_RD_DATA_CAPTURE_DELAY_LSB 1 #define CQSPI_REG_RD_DATA_CAPTURE_DELAY_MASK 0xF +#define CQSPI_REG_RD_DATA_CAPTURE_DDR_DELAY_LSB 16 +#define CQSPI_REG_RD_DATA_CAPTURE_DDR_DELAY_MASK 0xF #define CQSPI_REG_SIZE 0x14 #define CQSPI_REG_SIZE_ADDRESS_LSB 0 @@ -169,6 +179,12 @@ #define CQSPI_REG_PHY_CONFIG 0xB4 #define CQSPI_REG_PHY_CONFIG_RESET_FLD_MASK 0x40000000 +#define CQSPI_REG_PHY_CONFIG_RXDLY_MSK 0x7Fu +#define CQSPI_REG_PHY_CONFIG_TXDLY_MSK 0x7Fu +#define CQSPI_REG_PHY_CONFIG_RXDLY_LSB 0 +#define CQSPI_REG_PHY_CONFIG_TXDLY_LSB 16 +#define CQSPI_REG_PHY_CONFIG_RXBYP BIT(29) +#define CQSPI_REG_PHY_CONFIG_RESYNC BIT(31) #define CQSPI_DMA_DST_ADDR_REG 0x1800 #define CQSPI_DMA_DST_SIZE_REG 0x1804 @@ -204,7 +220,6 @@ CQSPI_REG_SDRAMLEVEL_WR_LSB) & CQSPI_REG_SDRAMLEVEL_WR_MASK) struct cadence_spi_plat { - unsigned int max_hz; void *regbase; void *ahbbase; bool is_decoded_cs; @@ -213,10 +228,15 @@ struct cadence_spi_plat { u32 trigger_address; fdt_addr_t ahbsize; bool use_dac_mode; - int read_delay; - int max_read_delay; + bool phy_support; + bool slow_phy_tx; + u32 max_read_delay; + + bool is_dma; + + /* Per-flash parameters */ + unsigned int calib_hz; - /* Flash parameters */ u32 page_size; u32 block_size; u32 tshsl_ns; @@ -224,7 +244,13 @@ struct cadence_spi_plat { u32 tchsh_ns; u32 tslch_ns; - bool is_dma; + bool calib_cfg; + u32 read_delay; + bool use_dqs; + bool use_phy; + u32 phyrxdly; + u32 phytxdly; + /* End per-flash parameters */ }; struct cadence_spi_priv; @@ -237,8 +263,10 @@ struct cadence_drv_ops { }; struct cadence_spi_priv { + uint req_hz; + struct cadence_spi_plat *plat; + unsigned int ref_clk_hz; - unsigned int max_hz; void *regbase; void *ahbbase; unsigned int fifo_depth; @@ -256,24 +284,22 @@ struct cadence_spi_priv { struct cadence_drv_ops ops; int qspi_is_init; - unsigned int qspi_calibrated_hz; unsigned int qspi_calibrated_cs; - unsigned int previous_hz; u32 wr_delay; - int read_delay; - int max_read_delay; struct reset_ctl_bulk *resets; - u32 page_size; - u32 block_size; - u32 tshsl_ns; - u32 tsd2d_ns; - u32 tchsh_ns; - u32 tslch_ns; - u8 edge_mode; - u8 dll_mode; - bool extra_dummy; - bool ddr_init; + + /* Flash parameters */ + unsigned int qspi_calibrated_hz; + u32 read_delay; + +#if !CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB) + unsigned int chipid; +#endif + u32 phyrxdly; + u32 phytxdly; + /* End Flash parameters */ + bool is_decoded_cs; bool use_dac_mode; bool is_dma; @@ -289,7 +315,10 @@ void cadence_qspi_apb_controller_init(struct cadence_spi_priv *priv); void cadence_qspi_apb_controller_enable(void *reg_base_addr); void cadence_qspi_apb_controller_disable(void *reg_base_addr); void cadence_qspi_apb_dac_mode_enable(void *reg_base); - +void cadence_qspi_apb_enable_phy(void *reg_base, bool enbl); +void cadence_qspi_apb_set_phy_cfg(void *reg_base, + u32 rxdly, u32 txdly); +void cadence_spi_update_speed(struct udevice *bus, bool calibrated); int cadence_qspi_apb_command_read_setup(struct cadence_spi_priv *priv, const struct spi_mem_op *op); int cadence_qspi_apb_command_read(struct cadence_spi_priv *priv, @@ -318,8 +347,8 @@ void cadence_qspi_apb_delay(void *reg_base, unsigned int tshsl_ns, unsigned int tsd2d_ns, unsigned int tchsh_ns, unsigned int tslch_ns); void cadence_qspi_apb_enter_xip(void *reg_base, char xip_dummy); -void cadence_qspi_apb_readdata_capture(void *reg_base, - unsigned int bypass, unsigned int delay); +void cadence_qspi_apb_readdata_capture(const struct cadence_spi_priv *priv, + unsigned int bypass, unsigned int delay); unsigned int cm_get_qspi_controller_clk_hz(void); int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv, const struct spi_mem_op *op); @@ -328,6 +357,8 @@ int cadence_qspi_apb_exec_flash_cmd(void *reg_base, unsigned int reg); int cadence_qspi_versal_flash_reset(struct udevice *dev); ofnode cadence_qspi_get_subnode(struct udevice *dev); void cadence_qspi_apb_enable_linear_mode(bool enable); +int cadence_qspi_apb_phy_calibrate(struct spi_slave *slave, + int (*test_read_fn)(struct spi_slave *)); int cadence_qspi_apb_read_copy_mdma(struct cadence_spi_priv *priv, void *dst, u64 src, size_t len); diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index 90b9c558b2..fde66cc13f 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -70,6 +70,284 @@ void cadence_qspi_apb_dac_mode_enable(void *reg_base) writel(reg, reg_base + CQSPI_REG_CONFIG); } +void cadence_qspi_apb_enable_phy(void *reg_base, bool enbl) +{ + u32 reg; + + reg = readl(reg_base + CQSPI_REG_CONFIG); + if (enbl) + reg |= CQSPI_REG_CONFIG_PHY_ENABLE_MASK + | CQSPI_REG_CONFIG_PIPELINE_PHY_EN_MASK; + else + reg &= ~(CQSPI_REG_CONFIG_PHY_ENABLE_MASK + | CQSPI_REG_CONFIG_PIPELINE_PHY_EN_MASK); + writel(reg, reg_base + CQSPI_REG_CONFIG); +} + +void cadence_qspi_apb_set_phy_cfg(void *reg_base, + u32 rxdly, u32 txdly) +{ + u32 reg; + + reg = readl(reg_base + CQSPI_REG_PHY_CONFIG); + reg &= ~(CQSPI_REG_PHY_CONFIG_RESYNC + | (CQSPI_REG_PHY_CONFIG_RXDLY_MSK + << CQSPI_REG_PHY_CONFIG_RXDLY_LSB) + | (CQSPI_REG_PHY_CONFIG_TXDLY_MSK + << CQSPI_REG_PHY_CONFIG_TXDLY_LSB)); + reg |= ((rxdly & CQSPI_REG_PHY_CONFIG_RXDLY_MSK) + << CQSPI_REG_PHY_CONFIG_RXDLY_LSB) + | ((txdly & CQSPI_REG_PHY_CONFIG_TXDLY_MSK) + << CQSPI_REG_PHY_CONFIG_TXDLY_LSB) + | CQSPI_REG_PHY_CONFIG_RXBYP; + writel(reg, reg_base + CQSPI_REG_PHY_CONFIG); + + reg = readl(reg_base + CQSPI_REG_PHY_CONFIG); + reg |= CQSPI_REG_PHY_CONFIG_RESYNC; + writel(reg, reg_base + CQSPI_REG_PHY_CONFIG); +} + +void cadence_qspi_apb_readdata_capture(const struct cadence_spi_priv *priv, + unsigned int bypass, unsigned int delay) +{ + void *reg_base = priv->regbase; + unsigned int reg; + + cadence_qspi_apb_controller_disable(reg_base); + reg = readl(reg_base + CQSPI_REG_RD_DATA_CAPTURE); + + if (bypass) + reg |= CQSPI_REG_RD_DATA_CAPTURE_BYPASS; + else + reg &= ~CQSPI_REG_RD_DATA_CAPTURE_BYPASS; + + reg &= ~(CQSPI_REG_RD_DATA_CAPTURE_DELAY_MASK + << CQSPI_REG_RD_DATA_CAPTURE_DELAY_LSB); + + reg |= (delay & CQSPI_REG_RD_DATA_CAPTURE_DELAY_MASK) + << CQSPI_REG_RD_DATA_CAPTURE_DELAY_LSB; + + if (priv->plat->phy_support) { + if (priv->plat->use_dqs) + reg |= CQSPI_REG_READCAPTURE_DQS_ENABLE; + else + reg &= ~CQSPI_REG_READCAPTURE_DQS_ENABLE; + } + + writel(reg, reg_base + CQSPI_REG_RD_DATA_CAPTURE); + + cadence_qspi_apb_controller_enable(reg_base); +} + +#if CONFIG_IS_ENABLED(SPI_FLASH_HS_CALIB) +/** + * This algorithm was implemented based on the Analog Devices application note + * EE-437: "OSPI PHY Configuration and Training". + * + * Algorithm breif: + * * Set Read Delay Capture, TX DLL delay, and RX DLL delay to 0. + * * Iterate over RDC, TXdly and RXdly until the first valid configuration is + * found. + * * Keep this RDC value. Scan all TXdly values for the range of valid values + * and pick the middle value. + * * If data strobe signal (DQS) is used, use the first valid RXdly value, +1. + * * If DQS is not used, scan all RXdly for the range of valid values and pick + * the middle value. + * * Configurations are considered valid if they pass for at least 2 + * consecutive iterations. + * + * The caller is responsible for providing the function to test + * if a configuration is valid. + * + * Returns 0 on success, negative error code. + */ +int cadence_qspi_apb_phy_calibrate(struct spi_slave *slave, + int (*test_read_fn)(struct spi_slave *)) +{ + struct udevice *bus = slave->dev->parent; + struct cadence_spi_priv *priv = dev_get_priv(bus); + struct cadence_spi_plat *plat = dev_get_plat(bus); + void * const reg_base = priv->regbase; + int err = 0; + const bool dqs = plat->use_dqs; + int fast = 1; + + int rdcd; + int txvalid_count; + int rxvalid_count; + + int first_txdly_valid; + int last_txdly_valid; + int first_rxdly_valid; + int last_rxdly_valid; + + int txdly; + int rxdly; + + int txdly_step; + int rxdly_step; + int txpass_limit; + int rxpass_limit; + + if (priv->req_hz != priv->ref_clk_hz) { + debug("%s: phy mode must operate at ref_clk speed.", __func__); + err = -EINVAL; + goto out; + } + + cadence_spi_update_speed(bus, true); + +try_again_slow: + if (fast) { + txdly_step = 4; + rxdly_step = 4; + txpass_limit = 16; + rxpass_limit = 16; + } else { + txdly_step = 1; + rxdly_step = 1; + txpass_limit = 128; + rxpass_limit = 128; + } + + first_txdly_valid = -1; + last_txdly_valid = -1; + + for (rdcd = 0; rdcd < plat->max_read_delay; ++rdcd) { + cadence_qspi_apb_readdata_capture(priv, 1, rdcd); + + txvalid_count = 0; + for (txdly = 0; txdly < CQSPI_PHY_DLL_MAX_DELAY; + txdly += txdly_step) { + rxvalid_count = 0; + for (rxdly = 0; rxdly < CQSPI_PHY_DLL_MAX_DELAY; + rxdly += rxdly_step) { + cadence_qspi_apb_set_phy_cfg(reg_base, + rxdly, txdly); + err = test_read_fn(slave); + if (err < 0) { + goto out; + } else if (!err) { + ++rxvalid_count; + + if (rxvalid_count == 2) + ++txvalid_count; + + /* Check for enough passing cfgs + * With DQS, only 2 need to pass. + */ + if (dqs && rxvalid_count >= 2) + break; + else if (!dqs && (rxvalid_count >= + rxpass_limit)) + break; + } else if (rxvalid_count == 1) { + //must be consecutive to be valid + rxvalid_count = 0; + } else if (rxvalid_count >= 2) { + break; //end of valid range + } + } + + if (rxvalid_count >= 2) { + if (first_txdly_valid < 0) + first_txdly_valid = txdly; + last_txdly_valid = txdly; + } + + if (txvalid_count >= txpass_limit) + break; + else if (txvalid_count && !rxvalid_count) + break; + } + if (first_txdly_valid >= 0) + break; + } + + if (first_txdly_valid < 0 || last_txdly_valid < 0) { + if (fast) { + fast = 0; + goto try_again_slow; + } else { + goto out; + } + } + + txdly = (first_txdly_valid + last_txdly_valid) / 2; + + rxvalid_count = 0; + first_rxdly_valid = -1; + last_rxdly_valid = -1; + for (rxdly = 0; rxdly < CQSPI_PHY_DLL_MAX_DELAY; + rxdly += rxdly_step) { + cadence_qspi_apb_set_phy_cfg(reg_base, rxdly, txdly); + err = test_read_fn(slave); + if (err < 0) { + goto out; + } else if (!err) { + ++rxvalid_count; + + if (first_rxdly_valid < 0) + first_rxdly_valid = rxdly; + last_rxdly_valid = rxdly; + + //check if we have enough passing configurations + if (dqs && rxvalid_count >= 2) + break; + else if (!dqs && (rxvalid_count >= rxpass_limit)) + break; + } else if (rxvalid_count == 1) { + //must be consecutive to be valid + rxvalid_count = 0; + first_rxdly_valid = -1; + last_rxdly_valid = -1; + } else if (rxvalid_count >= 2) { + break; //end of valid range + } + } + + if (first_rxdly_valid < 0 || last_rxdly_valid < 0) { + if (fast) { + fast = 0; + goto try_again_slow; + } else { + goto out; + } + } + + if (dqs) + rxdly = first_rxdly_valid + 1; + else + rxdly = (first_rxdly_valid + last_rxdly_valid) / 2; + + cadence_qspi_apb_set_phy_cfg(reg_base, rxdly, txdly); + err = test_read_fn(slave); + if (err < 0) { + goto out; + } else if (err) { + if (fast) { + fast = 0; + goto try_again_slow; + } else { + goto out; + } + } + + priv->phyrxdly = rxdly; + priv->phytxdly = txdly; + priv->read_delay = rdcd; + + debug("%s: read-delay=%u phyrxdly=%u phytxdly=%u\n", + __func__, rdcd, rxdly, txdly); + + return 0; + +out: + cadence_spi_update_speed(bus, false); + return err; +} +#endif + static unsigned int cadence_qspi_calc_dummy(const struct spi_mem_op *op, bool dtr) { @@ -166,30 +444,6 @@ static unsigned int cadence_qspi_wait_idle(void *reg_base) return 0; } -void cadence_qspi_apb_readdata_capture(void *reg_base, - unsigned int bypass, unsigned int delay) -{ - unsigned int reg; - cadence_qspi_apb_controller_disable(reg_base); - - reg = readl(reg_base + CQSPI_REG_RD_DATA_CAPTURE); - - if (bypass) - reg |= CQSPI_REG_RD_DATA_CAPTURE_BYPASS; - else - reg &= ~CQSPI_REG_RD_DATA_CAPTURE_BYPASS; - - reg &= ~(CQSPI_REG_RD_DATA_CAPTURE_DELAY_MASK - << CQSPI_REG_RD_DATA_CAPTURE_DELAY_LSB); - - reg |= (delay & CQSPI_REG_RD_DATA_CAPTURE_DELAY_MASK) - << CQSPI_REG_RD_DATA_CAPTURE_DELAY_LSB; - - writel(reg, reg_base + CQSPI_REG_RD_DATA_CAPTURE); - - cadence_qspi_apb_controller_enable(reg_base); -} - void cadence_qspi_apb_config_baudrate_div(void *reg_base, unsigned int ref_clk_hz, unsigned int sclk_hz) { @@ -211,8 +465,13 @@ void cadence_qspi_apb_config_baudrate_div(void *reg_base, if (div > CQSPI_REG_CONFIG_BAUD_MASK) div = CQSPI_REG_CONFIG_BAUD_MASK; - debug("%s: ref_clk %dHz sclk %dHz Div 0x%x, actual %dHz\n", __func__, - ref_clk_hz, sclk_hz, div, ref_clk_hz / (2 * (div + 1))); + debug("%s: ref_clk %dHz sclk %dHz Div 0x%x, %s %dHz\n", __func__, + ref_clk_hz, sclk_hz, div, + (readl(reg_base + CQSPI_REG_CONFIG) & + CQSPI_REG_CONFIG_PHY_ENABLE_MASK) ? "PHY" : "actual", + (readl(reg_base + CQSPI_REG_CONFIG) & + CQSPI_REG_CONFIG_PHY_ENABLE_MASK) ? + ref_clk_hz : ref_clk_hz / (2 * (div + 1))); reg |= (div << CQSPI_REG_CONFIG_BAUD_LSB); writel(reg, reg_base + CQSPI_REG_CONFIG); @@ -324,8 +583,8 @@ void cadence_qspi_apb_controller_init(struct cadence_spi_priv *priv) /* Clear the previous value */ reg &= ~(CQSPI_REG_SIZE_PAGE_MASK << CQSPI_REG_SIZE_PAGE_LSB); reg &= ~(CQSPI_REG_SIZE_BLOCK_MASK << CQSPI_REG_SIZE_BLOCK_LSB); - reg |= (priv->page_size << CQSPI_REG_SIZE_PAGE_LSB); - reg |= (priv->block_size << CQSPI_REG_SIZE_BLOCK_LSB); + reg |= (priv->plat->page_size << CQSPI_REG_SIZE_PAGE_LSB); + reg |= (priv->plat->block_size << CQSPI_REG_SIZE_BLOCK_LSB); writel(reg, priv->regbase + CQSPI_REG_SIZE); /* Configure the remap address register, no remap */ @@ -877,7 +1136,7 @@ static int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_priv *priv, unsigned int n_tx, const u8 *txbuf) { - unsigned int page_size = priv->page_size; + unsigned int page_size = priv->plat->page_size; unsigned int remaining = n_tx; const u8 *bb_txbuf = txbuf; void *bounce_buf = NULL; @@ -968,13 +1227,23 @@ int cadence_qspi_apb_write_execute(struct cadence_spi_priv *priv, u32 to = op->addr.val; const void *buf = op->data.buf.out; size_t len = op->data.nbytes; + u32 cfg; int retval = 0; cadence_qspi_apb_enable_linear_mode(true); if (op->addr.nbytes && priv->use_dac_mode && (to + len < priv->ahbsize)) { + cfg = readl(priv->regbase + CQSPI_REG_CONFIG); + if (priv->plat->slow_phy_tx && (cfg & CQSPI_REG_CONFIG_PHY_ENABLE_MASK)) + writel(cfg & ~(CQSPI_REG_CONFIG_PHY_ENABLE_MASK), + priv->regbase + CQSPI_REG_CONFIG); + retval = priv->ops.direct_write_copy(priv, buf, to, len); + if (!cadence_qspi_wait_idle(priv->regbase)) retval = -EIO; + + writel(cfg, priv->regbase + CQSPI_REG_CONFIG); + return retval; }