From patchwork Fri Oct 16 22:57:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1383568 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: 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=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=G9prgkI0; dkim-atps=neutral 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 RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4CChSb6GzJz9sSf for ; Sat, 17 Oct 2020 10:00:15 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 6036C82490; Sat, 17 Oct 2020 00:58:40 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.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=gmail.com header.i=@gmail.com header.b="G9prgkI0"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id C3B7082469; Sat, 17 Oct 2020 00:58:34 +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=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-io1-xd44.google.com (mail-io1-xd44.google.com [IPv6:2607:f8b0:4864:20::d44]) (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 7D4D382425 for ; Sat, 17 Oct 2020 00:58:31 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-io1-xd44.google.com with SMTP id q9so5901577iow.6 for ; Fri, 16 Oct 2020 15:58:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=hI6AyoH+Eue1nJc95FOjryIH8rE075rl8l2hphsL4/E=; b=G9prgkI02hHSLul8CXUkZjg5MX/PmmF3kTTY8sl/ipeu/8fs7z7q0CROsnNJEIQiPQ dfW5DKCuHQy2ehVDUIlBUUAPzDRQnmfXM+2xEHpgMdVcBO9OSWG4v42qO38kB0VltxeN jySfYju8/tZxemqLDUJ3xbluZjbnDK/elj/NuEFI6WxUW5+6y8rD0kZlKTPsL3KCpAB2 a3RZA8XzcjKriOO4i9uSf/tAk8kuh0pFuHb6dqYUvZ0IVvBJX4uwmPVNQ7/jTodm1MQD RGHXi8P0swlYK+M7Pg9GTpqNA0Ox30xYgrxj09wmzjySvL3N19xq//UiqVX4oCpDG4Vc ITNw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=hI6AyoH+Eue1nJc95FOjryIH8rE075rl8l2hphsL4/E=; b=oluYRkW1r/E2f0GHuT8EarIB+WOneiXc1cO/FwxoMaXZJmXR1Apkf7flAayh1NVg6p Uqd8aoYGseQHM+YawGWiKW9l4nIAalNVRhVsykKPP4uPKo0FWzHzp1TV4MpWVM1gbQAp /cW+0uKx6nyh/lKoOveah9okuTkOSK26/Fr5AmJQ5Tcm1FUBen4FLv+/OJUYFh64p6i4 tqsl8LJwBjWaVAWIkRBiRFxPmDswKu7bU9o4BWgoAtqaZ3Q/8Zunsk9MbAs1+FWC3G6b FWnyfrrAxbs81vefQhcMTPwKvALEhWCcTyZLON64OFTMEhoj4UahvAI1Fzf3KaQr4j+B xM6w== X-Gm-Message-State: AOAM532HyHQTCTRwYesbn+0FuSEhKB/hrEvdEgrKKQTM7he/XNuQQei9 O6HAQkyJfwkp1z6wV97tLmDn8DF7I8owdHlW X-Google-Smtp-Source: ABdhPJzA2B+UKG1JA48nSBfeKwI3PHKNkV7HZjkD+m2ek1kw8VVpbWwNGf1HUDMKyfQ5TR95WhYG6A== X-Received: by 2002:a6b:b208:: with SMTP id b8mr4177510iof.36.1602889109959; Fri, 16 Oct 2020 15:58:29 -0700 (PDT) Received: from godwin.fios-router.home (pool-108-51-35-162.washdc.fios.verizon.net. [108.51.35.162]) by smtp.gmail.com with ESMTPSA id 12sm3117558ioz.33.2020.10.16.15.58.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Oct 2020 15:58:29 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, uboot-snps-arc@synopsys.com Cc: Jagan Teki , Marek Vasut , Heinrich Schuchardt , Eugeniy Paltsev , Horatiu Vultur , Sean Anderson , Alexey Brodkin , Daniel Schwierzeck , Gregory CLEMENT , Lars Povlsen , Ley Foon Tan , Simon Goldschmidt Subject: [PATCH v4 09/13] spi: dw: Add support for multiple CTRLR0 layouts Date: Fri, 16 Oct 2020 18:57:51 -0400 Message-Id: <20201016225755.302659-10-seanga2@gmail.com> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20201016225755.302659-1-seanga2@gmail.com> References: <20201016225755.302659-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 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.102.3 at phobos.denx.de X-Virus-Status: Clean CTRLR0 can have several different layouts depending on the specific device (dw-apb-ssi vs dwc-ssi), and specific parameters set during synthesis. Update the driver to support three specific configurations: dw-apb-ssi with SSI_MAX_XFER_SIZE=16, dw-apb-ssi with SSI_MAX_XFER_SIZE=32, and dwc-ssi. dw-apb-ssi is the version of the device on Altera/Intel SoCFPGAs, MSCC SoCs, and Canaan Kendryte K210 SoCs. This is the only version this driver supported before this change. The register layout before version 3.23a is: | 31 .. 16 | | other stuff | | 15 .. 10 | 9 .. 8 | 7 .. 6 | 5 .. 4 | 3 .. 0 | | other stuff | TMOD | MODE | FRF | DFS | Note that DFS (Data Frame Size) is only 4 bits, limiting transfers to data frames of 16 bits or less. In version 3.23a, the SSI_MAX_XFER_SIZE parameter was introduced. This parameter defaults to 16 (resulting in the same layout as prior versions), but may also be set to 32. To allow setting longer data frame sizes, a new DFS_32 register was introduced: | 31 .. 21 | 20 .. 16 | | other stuff | DFS_32 | | 15 .. 10 | 9 .. 8 | 7 .. 6 | 5 .. 4 | 3 .. 0 | | other stuff | TMOD | MODE | FRF | all zeros | The old DFS field no longer controls the data frame size. To detect this layout, we try writing 0xF to DFS. If we read back 0x0, then this device has SSI_MAX_XFER_SIZE=32. dwc-ssi is the version of the device on Intel Keem Bay SoCs and Canaan Kendryte K210 SoCs. The layout of ctrlr0 is: | 31 .. 16 | | other stuff | | 15 .. 12 | 11 .. 10 | 9 .. 8 | 7 .. 6 | 4 .. 0 | | other stuff | TMOD | MODE | FRF | DFS_32 | The semantics of the fields have not changed since the previous version. However, SSI_MAX_XFER_SIZE is effectively always 32. To support these different layouts, we model our approach on the one which the Linux kernel has taken. During probe, the driver calls an init function stored in driver_data. This init function is responsible for determining the layout of CTRLR0, and supplying the update_cr0 function. The style of and information behind this commit is based on the Linux MMIO driver for these devices. Specific reference was made to the series adding support for Intel Keem Bay SoCs [1]. [1] https://lore.kernel.org/linux-spi/20200505130618.554-1-wan.ahmad.zainie.wan.mohamad@intel.com/ Signed-off-by: Sean Anderson --- Changes in v4: - Auto-detect SSI_MAX_XFER_SIZE Changes in v3: - Prefix log messages with SPI@
Changes in v2: - Configure ctrlr0 register layout based on compatible string - Split documentation off into its own patch drivers/spi/designware_spi.c | 176 +++++++++++++++++++++++++++++------ 1 file changed, 145 insertions(+), 31 deletions(-) diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c index 0ebd2cf3cb..9e02fce6c6 100644 --- a/drivers/spi/designware_spi.c +++ b/drivers/spi/designware_spi.c @@ -3,6 +3,7 @@ * Designware master SPI core controller driver * * Copyright (C) 2014 Stefan Roese + * Copyright (C) 2020 Sean Anderson * * Very loosely based on the Linux driver: * drivers/spi/spi-dw.c, which is: @@ -22,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -54,28 +56,48 @@ #define DW_SPI_DR 0x60 /* Bit fields in CTRLR0 */ -#define SPI_DFS_OFFSET 0 +/* + * Only present when SSI_MAX_XFER_SIZE=16. This is the default, and the only + * option before version 3.23a. + */ +#define CTRLR0_DFS_MASK GENMASK(3, 0) -#define SPI_FRF_OFFSET 4 -#define SPI_FRF_SPI 0x0 -#define SPI_FRF_SSP 0x1 -#define SPI_FRF_MICROWIRE 0x2 -#define SPI_FRF_RESV 0x3 +#define CTRLR0_FRF_MASK GENMASK(5, 4) +#define CTRLR0_FRF_SPI 0x0 +#define CTRLR0_FRF_SSP 0x1 +#define CTRLR0_FRF_MICROWIRE 0x2 +#define CTRLR0_FRF_RESV 0x3 -#define SPI_MODE_OFFSET 6 -#define SPI_SCPH_OFFSET 6 -#define SPI_SCOL_OFFSET 7 +#define CTRLR0_MODE_MASK GENMASK(7, 6) +#define CTRLR0_MODE_SCPH 0x1 +#define CTRLR0_MODE_SCPOL 0x2 -#define SPI_TMOD_OFFSET 8 -#define SPI_TMOD_MASK (0x3 << SPI_TMOD_OFFSET) -#define SPI_TMOD_TR 0x0 /* xmit & recv */ -#define SPI_TMOD_TO 0x1 /* xmit only */ -#define SPI_TMOD_RO 0x2 /* recv only */ -#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */ +#define CTRLR0_TMOD_MASK GENMASK(9, 8) +#define CTRLR0_TMOD_TR 0x0 /* xmit & recv */ +#define CTRLR0_TMOD_TO 0x1 /* xmit only */ +#define CTRLR0_TMOD_RO 0x2 /* recv only */ +#define CTRLR0_TMOD_EPROMREAD 0x3 /* eeprom read mode */ -#define SPI_SLVOE_OFFSET 10 -#define SPI_SRL_OFFSET 11 -#define SPI_CFS_OFFSET 12 +#define CTRLR0_SLVOE_OFFSET 10 +#define CTRLR0_SRL_OFFSET 11 +#define CTRLR0_CFS_MASK GENMASK(15, 12) + +/* Only present when SSI_MAX_XFER_SIZE=32 */ +#define CTRLR0_DFS_32_MASK GENMASK(20, 16) + +/* The next field is only present on versions after 4.00a */ +#define CTRLR0_SPI_FRF_MASK GENMASK(22, 21) +#define CTRLR0_SPI_FRF_BYTE 0x0 +#define CTRLR0_SPI_FRF_DUAL 0x1 +#define CTRLR0_SPI_FRF_QUAD 0x2 + +/* Bit fields in CTRLR0 based on DWC_ssi_databook.pdf v1.01a */ +#define DWC_SSI_CTRLR0_DFS_MASK GENMASK(4, 0) +#define DWC_SSI_CTRLR0_FRF_MASK GENMASK(7, 6) +#define DWC_SSI_CTRLR0_MODE_MASK GENMASK(9, 8) +#define DWC_SSI_CTRLR0_TMOD_MASK GENMASK(11, 10) +#define DWC_SSI_CTRLR0_SRL_OFFSET 13 +#define DWC_SSI_CTRLR0_SPI_FRF_MASK GENMASK(23, 22) /* Bit fields in SR, 7 bits */ #define SR_MASK GENMASK(6, 0) /* cover 7 bits */ @@ -99,6 +121,8 @@ struct dw_spi_priv { struct reset_ctl_bulk resets; struct gpio_desc cs_gpio; /* External chip-select gpio */ + u32 (*update_cr0)(struct dw_spi_priv *priv); + void __iomem *regs; unsigned long bus_clk_rate; unsigned int freq; /* Default frequency */ @@ -109,6 +133,7 @@ struct dw_spi_priv { void *rx; void *rx_end; u32 fifo_len; /* depth of the FIFO buffer */ + u32 max_xfer; /* Maximum transfer size (in bits) */ int bits_per_word; int len; @@ -127,6 +152,53 @@ static inline void dw_write(struct dw_spi_priv *priv, u32 offset, u32 val) __raw_writel(val, priv->regs + offset); } +static u32 dw_spi_dw16_update_cr0(struct dw_spi_priv *priv) +{ + return FIELD_PREP(CTRLR0_DFS_MASK, priv->bits_per_word - 1) + | FIELD_PREP(CTRLR0_FRF_MASK, priv->type) + | FIELD_PREP(CTRLR0_MODE_MASK, priv->mode) + | FIELD_PREP(CTRLR0_TMOD_MASK, priv->tmode); +} + +static u32 dw_spi_dw32_update_cr0(struct dw_spi_priv *priv) +{ + return FIELD_PREP(CTRLR0_DFS_32_MASK, priv->bits_per_word - 1) + | FIELD_PREP(CTRLR0_FRF_MASK, priv->type) + | FIELD_PREP(CTRLR0_MODE_MASK, priv->mode) + | FIELD_PREP(CTRLR0_TMOD_MASK, priv->tmode); +} + +static u32 dw_spi_dwc_update_cr0(struct dw_spi_priv *priv) +{ + return FIELD_PREP(DWC_SSI_CTRLR0_DFS_MASK, priv->bits_per_word - 1) + | FIELD_PREP(DWC_SSI_CTRLR0_FRF_MASK, priv->type) + | FIELD_PREP(DWC_SSI_CTRLR0_MODE_MASK, priv->mode) + | FIELD_PREP(DWC_SSI_CTRLR0_TMOD_MASK, priv->tmode); +} + +static int dw_spi_apb_init(struct udevice *bus, struct dw_spi_priv *priv) +{ + /* If we read zeros from DFS, then we need to use DFS_32 instead */ + dw_write(priv, DW_SPI_SSIENR, 0); + dw_write(priv, DW_SPI_CTRLR0, 0xffffffff); + if (FIELD_GET(CTRLR0_DFS_MASK, dw_read(priv, DW_SPI_CTRLR0))) { + priv->max_xfer = 16; + priv->update_cr0 = dw_spi_dw16_update_cr0; + } else { + priv->max_xfer = 32; + priv->update_cr0 = dw_spi_dw32_update_cr0; + } + + return 0; +} + +static int dw_spi_dwc_init(struct udevice *bus, struct dw_spi_priv *priv) +{ + priv->max_xfer = 32; + priv->update_cr0 = dw_spi_dwc_update_cr0; + return 0; +} + static int request_gpio_cs(struct udevice *bus) { #if CONFIG_IS_ENABLED(DM_GPIO) && !defined(CONFIG_SPL_BUILD) @@ -165,6 +237,10 @@ static int dw_spi_ofdata_to_platdata(struct udevice *bus) /* Use 500KHz as a suitable default */ plat->frequency = dev_read_u32_default(bus, "spi-max-frequency", 500000); + + if (dev_read_bool(bus, "spi-slave")) + return -EINVAL; + dev_info(bus, "max-frequency=%d\n", plat->frequency); return request_gpio_cs(bus); @@ -259,11 +335,15 @@ static int dw_spi_reset(struct udevice *bus) return 0; } +typedef int (*dw_spi_init_t)(struct udevice *bus, struct dw_spi_priv *priv); + static int dw_spi_probe(struct udevice *bus) { + dw_spi_init_t init = (dw_spi_init_t)dev_get_driver_data(bus); struct dw_spi_platdata *plat = dev_get_platdata(bus); struct dw_spi_priv *priv = dev_get_priv(bus); int ret; + u32 version; priv->regs = plat->regs; priv->freq = plat->frequency; @@ -276,6 +356,17 @@ static int dw_spi_probe(struct udevice *bus) if (ret) return ret; + if (!init) + return -EINVAL; + ret = init(bus, priv); + if (ret) + return ret; + + version = dw_read(priv, DW_SPI_VERSION); + dev_dbg(bus, "ssi_version_id=%c.%c%c%c ssi_max_xfer_size=%u\n", + version >> 24, version >> 16, version >> 8, version, + priv->max_xfer); + /* Currently only bits_per_word == 8 supported */ priv->bits_per_word = 8; @@ -320,7 +411,7 @@ static inline u32 rx_max(struct dw_spi_priv *priv) static void dw_writer(struct dw_spi_priv *priv) { u32 max = tx_max(priv); - u16 txw = 0xFFFF; + u32 txw = 0xFFFFFFFF; while (max--) { /* Set the tx word if the transfer's original "tx" is not null */ @@ -406,23 +497,18 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen, if (flags & SPI_XFER_BEGIN) external_cs_manage(dev, false); - cr0 = (priv->bits_per_word - 1) | (priv->type << SPI_FRF_OFFSET) | - (priv->mode << SPI_MODE_OFFSET) | - (priv->tmode << SPI_TMOD_OFFSET); - if (rx && tx) - priv->tmode = SPI_TMOD_TR; + priv->tmode = CTRLR0_TMOD_TR; else if (rx) - priv->tmode = SPI_TMOD_RO; + priv->tmode = CTRLR0_TMOD_RO; else /* - * In transmit only mode (SPI_TMOD_TO) input FIFO never gets + * In transmit only mode (CTRL0_TMOD_TO) input FIFO never gets * any data which breaks our logic in poll_transfer() above. */ - priv->tmode = SPI_TMOD_TR; + priv->tmode = CTRLR0_TMOD_TR; - cr0 &= ~SPI_TMOD_MASK; - cr0 |= (priv->tmode << SPI_TMOD_OFFSET); + cr0 = priv->update_cr0(priv); priv->len = bitlen >> 3; @@ -476,7 +562,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen, static int dw_spi_set_speed(struct udevice *bus, uint speed) { - struct dw_spi_platdata *plat = bus->platdata; + struct dw_spi_platdata *plat = dev_get_platdata(bus); struct dw_spi_priv *priv = dev_get_priv(bus); u16 clk_div; @@ -547,7 +633,35 @@ static const struct dm_spi_ops dw_spi_ops = { }; static const struct udevice_id dw_spi_ids[] = { - { .compatible = "snps,dw-apb-ssi" }, + /* Generic compatible strings */ + + { .compatible = "snps,dw-apb-ssi", .data = (ulong)dw_spi_apb_init }, + { .compatible = "snps,dw-apb-ssi-3.20a", .data = (ulong)dw_spi_apb_init }, + { .compatible = "snps,dw-apb-ssi-3.22a", .data = (ulong)dw_spi_apb_init }, + /* First version with SSI_MAX_XFER_SIZE */ + { .compatible = "snps,dw-apb-ssi-3.23a", .data = (ulong)dw_spi_apb_init }, + /* First version with Dual/Quad SPI; unused by this driver */ + { .compatible = "snps,dw-apb-ssi-4.00a", .data = (ulong)dw_spi_apb_init }, + { .compatible = "snps,dw-apb-ssi-4.01", .data = (ulong)dw_spi_apb_init }, + { .compatible = "snps,dwc-ssi-1.01a", .data = (ulong)dw_spi_dwc_init }, + + /* Compatible strings for specific SoCs */ + + /* + * Both the Cyclone V and Arria V share a device tree and have the same + * version of this device. This compatible string is used for those + * devices, and is not used for sofpgas in general. + */ + { .compatible = "altr,socfpga-spi", .data = (ulong)dw_spi_apb_init }, + { .compatible = "altr,socfpga-arria10-spi", .data = (ulong)dw_spi_apb_init }, + { .compatible = "canaan,kendryte-k210-spi", .data = (ulong)dw_spi_apb_init }, + { .compatible = "canaan,kendryte-k210-ssi", .data = (ulong)dw_spi_dwc_init }, + { .compatible = "intel,stratix10-spi", .data = (ulong)dw_spi_apb_init }, + { .compatible = "intel,agilex-spi", .data = (ulong)dw_spi_apb_init }, + { .compatible = "mscc,ocelot-spi", .data = (ulong)dw_spi_apb_init }, + { .compatible = "mscc,jaguar2-spi", .data = (ulong)dw_spi_apb_init }, + { .compatible = "snps,axs10x-spi", .data = (ulong)dw_spi_apb_init }, + { .compatible = "snps,hsdk-spi", .data = (ulong)dw_spi_apb_init }, { } };