Received: by 2002:a05:7412:2a8a:b0:fc:a2b0:25d7 with SMTP id u10csp482716rdh; Wed, 7 Feb 2024 10:12:00 -0800 (PST) X-Forwarded-Encrypted: i=3; AJvYcCVZv/WPLznrj2DKmWqMBDI2T3S0+E1+eg+53O0us3SXZW36x86IYfpw5JLG9SG56PmQyIB4ZkoLVGro1e6XJ1L+z6sEzpzQzdKFOqm+OQ== X-Google-Smtp-Source: AGHT+IGigyihpzHaLLe/drNkLl+qVV1WSUHT1vPjONusP9jUR99TJ1GyRAACBulxGyGhbddnsW8S X-Received: by 2002:a17:906:3e56:b0:a37:2153:a2ca with SMTP id t22-20020a1709063e5600b00a372153a2camr4445127eji.1.1707329520136; Wed, 07 Feb 2024 10:12:00 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1707329520; cv=pass; d=google.com; s=arc-20160816; b=UWi9ej1JHMQg1mGaJZJhehc3pmGeqXR2xG1Jl7XNhgd6O0svzO+ZeAxYJf9KyXwZPj XUY+gQL6NltT9DRg5pNeOGDDAc883NMfZKbdFEFFPY06iDTTZNjXWb3tJr8b3b7A/jof K/MFulxH99qbkJhknbL9hTOXMKkyb8rimZgOK9JoeiBw7HMK+gMp3h555sMF9fzUz6eh +lizJOSjR8AO7VxSPQr9EzxmaJTzdSkKR3JOPDCoupk/hBM0GquxxM6PUnqbpsYXnI2R l40mDk9CuSz0IPn5WBHwpz+xZucEwP/KDQzVM2260wplPzgTthW15Qy6yfrmbGP8/b8q nm1A== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:references:in-reply-to:message-id :date:subject:cc:to:from:dkim-signature:dkim-filter; bh=wblcgkvxFKNoIBmJlLw5uhCpCh2AOHqRl6jZojMqhfQ=; fh=ZJqrYVfGCIuN3xB5LPjctkXQ9ZMWDPbiSYGUoc2Qv5A=; b=yoqEJYV6vwGRec8DpSmKX2K68tTs81smEE6w1zuTf+xwkKCIwCWZPey2bdZ2Rh3yT+ mUeiqBHlr4sYqAHpa0AI3XYHZ0HKm3iW8x2LpWUsUDO3yvgRX+c+fFZi9gVmbdPxE+4I SMG+fXvjRNkYHjnNzw5jVdt8uUWau5ZTAITLtFkLA1qdZeaX5e0e5Wk303uGW2cLJLt9 aBfxqP6MSEVNXa4/klZCdeG97wBzuY/a2fWImuResNlpLz81UycTAp20MmwCF3ujoEit rmf3/3US6ky9SV945SAl0yb3MpCRO4grwn9+/0PfjUZepTo9yJgt1xI/fhoRY3+oj7+q dbfA==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@savoirfairelinux.com header.s=DFC430D2-D198-11EC-948E-34200CB392D2 header.b=XHzG6Wj7; arc=pass (i=1 spf=pass spfdomain=savoirfairelinux.com dkim=pass dkdomain=savoirfairelinux.com); spf=pass (google.com: domain of linux-kernel+bounces-56885-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) smtp.mailfrom="linux-kernel+bounces-56885-linux.lists.archive=gmail.com@vger.kernel.org" X-Forwarded-Encrypted: i=2; AJvYcCW/zOo8sF3Pvr7vemI7K6dlzWWa/WMtiyUFEngJa3K04YZ/lzUHQpa6KA/kUie9mpo0BX1KS8ZznqybxdnGNyPGixeeMVzcHwZWlE/07A== Return-Path: Received: from am.mirrors.kernel.org (am.mirrors.kernel.org. [147.75.80.249]) by mx.google.com with ESMTPS id s14-20020a170906354e00b00a38488af100si1199690eja.686.2024.02.07.10.12.00 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 07 Feb 2024 10:12:00 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-56885-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) client-ip=147.75.80.249; Authentication-Results: mx.google.com; dkim=pass header.i=@savoirfairelinux.com header.s=DFC430D2-D198-11EC-948E-34200CB392D2 header.b=XHzG6Wj7; arc=pass (i=1 spf=pass spfdomain=savoirfairelinux.com dkim=pass dkdomain=savoirfairelinux.com); spf=pass (google.com: domain of linux-kernel+bounces-56885-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.80.249 as permitted sender) smtp.mailfrom="linux-kernel+bounces-56885-linux.lists.archive=gmail.com@vger.kernel.org" Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by am.mirrors.kernel.org (Postfix) with ESMTPS id A919A1F22703 for ; Wed, 7 Feb 2024 18:11:59 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A56F086AE3; Wed, 7 Feb 2024 18:02:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=savoirfairelinux.com header.i=@savoirfairelinux.com header.b="XHzG6Wj7" Received: from mail.savoirfairelinux.com (mail.savoirfairelinux.com [208.88.110.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B39B68564F; Wed, 7 Feb 2024 18:02:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=208.88.110.44 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707328956; cv=none; b=sMAzN+5v7DV+hZLFOvQD5MDKcGhcQOdf8Kz7QpHimTJ9iBxJI+vEfeEzQGKoGHG55ot9WHp5pwQm2rpy6sgy1UiX+iXP1invD16xlh19dSsE/ZnwICWHxNh7gT5wjsxqqbi5CAIlu8biUNk9+DzlA+JtQ19TbbvZAxkuaUHmYXQ= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707328956; c=relaxed/simple; bh=U9ISTnvY2x0O1yNDgNt7SO01HJoEd3Ht+CK0RGK26aw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rwtib432713E8gmnOUWMglQ/PtTdxd8pAYT5iA0r161DdiYtwSSpmf2+Tlo3Rf2LwCfFrhdkFjCE8Pmu5a6mqhfVJmsyvIJ+6wAx/OjcMnTLBO4oPqWVmr2zHMu0pfG1xB9H8UTJ9egFNw9tmGiOlaE8aVEqvoV9PirT5npqPxQ= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=savoirfairelinux.com; spf=pass smtp.mailfrom=savoirfairelinux.com; dkim=pass (2048-bit key) header.d=savoirfairelinux.com header.i=@savoirfairelinux.com header.b=XHzG6Wj7; arc=none smtp.client-ip=208.88.110.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=savoirfairelinux.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=savoirfairelinux.com Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id 0F8029C43A3; Wed, 7 Feb 2024 13:02:28 -0500 (EST) Received: from mail.savoirfairelinux.com ([127.0.0.1]) by localhost (mail.savoirfairelinux.com [127.0.0.1]) (amavis, port 10032) with ESMTP id 2--gcTS7dtBq; Wed, 7 Feb 2024 13:02:26 -0500 (EST) Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id AC7C89C43D6; Wed, 7 Feb 2024 13:02:26 -0500 (EST) DKIM-Filter: OpenDKIM Filter v2.10.3 mail.savoirfairelinux.com AC7C89C43D6 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=savoirfairelinux.com; s=DFC430D2-D198-11EC-948E-34200CB392D2; t=1707328946; bh=wblcgkvxFKNoIBmJlLw5uhCpCh2AOHqRl6jZojMqhfQ=; h=From:To:Date:Message-ID:MIME-Version; b=XHzG6Wj7cV3u2w7oncLlRujiYyyoCOaB55JyHWhER7sBiFGLp42AtDCyAkKJA8WDo /vwwyD4JmbXG1c45qLCju2enNK7yWh5K7THOYny2gpooXsgP1Qq8DUDZAb7Sn1CfpE 4+uIBMiAAAxSce5BggVrHhlD/Ji7obOVG9lJorwuXSagSkPWEOo/Re988pQGdftpss 6f64+B3HmTdaip+ga7aLJsz2vxakbC8uT1kTTayg2i1N5KVCRnQ6+CIAHc7nnWJtBK CXHu3AfDEPqTLS/SzBK8kBoyQ0Dtln+ZHabA2Z5+vLyiGMzPwdg3O/KNF5GczU97UK xaLqbrrz+xHEQ== X-Virus-Scanned: amavis at mail.savoirfairelinux.com Received: from mail.savoirfairelinux.com ([127.0.0.1]) by localhost (mail.savoirfairelinux.com [127.0.0.1]) (amavis, port 10026) with ESMTP id 0Mz1fvbVnvGN; Wed, 7 Feb 2024 13:02:26 -0500 (EST) Received: from pcperry.mtl.sfl (unknown [192.168.51.254]) by mail.savoirfairelinux.com (Postfix) with ESMTPSA id 8577F9C43A3; Wed, 7 Feb 2024 13:02:26 -0500 (EST) From: Charles Perry To: mdf@kernel.org Cc: avandiver@markem-imaje.com, bcody@markem-imaje.com, Charles Perry , Wu Hao , Xu Yilun , Tom Rix , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michal Simek , kishore Manne , linux-fpga@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Subject: [PATCH v3 1/5] fpga: xilinx-spi: extract a common driver core Date: Wed, 7 Feb 2024 13:01:24 -0500 Message-ID: <20240207180142.79625-2-charles.perry@savoirfairelinux.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240207180142.79625-1-charles.perry@savoirfairelinux.com> References: <20240207180142.79625-1-charles.perry@savoirfairelinux.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Factor out the gpio handshaking (using PROGRAM_B, INIT_B and DONE) protocol in xilinx-core so that it can be reused for another driver. This commit does not change anything functionally to xilinx-spi. xilinx-core expects drivers to provide a single operation: * ->write(const char* buf, size_t count): write to the device As well as a struct device* for resource management. Signed-off-by: Charles Perry --- drivers/fpga/Kconfig | 4 + drivers/fpga/Makefile | 1 + drivers/fpga/xilinx-core.c | 208 ++++++++++++++++++++++++++++++++++++ drivers/fpga/xilinx-core.h | 28 +++++ drivers/fpga/xilinx-spi.c | 212 +++---------------------------------- 5 files changed, 254 insertions(+), 199 deletions(-) create mode 100644 drivers/fpga/xilinx-core.c create mode 100644 drivers/fpga/xilinx-core.h diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 2f689ac4ba3a3..d27a1ebf40838 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -64,9 +64,13 @@ config FPGA_MGR_STRATIX10_SOC help FPGA manager driver support for the Intel Stratix10 SoC. =20 +config FPGA_MGR_XILINX_CORE + tristate + config FPGA_MGR_XILINX_SPI tristate "Xilinx Configuration over Slave Serial (SPI)" depends on SPI + select FPGA_MGR_XILINX_CORE help FPGA manager driver support for Xilinx FPGA configuration over slave serial interface. diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index 352a2612623e0..7ec795b6a5a70 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_FPGA_MGR_SOCFPGA) +=3D socfpga.o obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) +=3D socfpga-a10.o obj-$(CONFIG_FPGA_MGR_STRATIX10_SOC) +=3D stratix10-soc.o obj-$(CONFIG_FPGA_MGR_TS73XX) +=3D ts73xx-fpga.o +obj-$(CONFIG_FPGA_MGR_XILINX_CORE) +=3D xilinx-core.o obj-$(CONFIG_FPGA_MGR_XILINX_SPI) +=3D xilinx-spi.o obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) +=3D zynq-fpga.o obj-$(CONFIG_FPGA_MGR_ZYNQMP_FPGA) +=3D zynqmp-fpga.o diff --git a/drivers/fpga/xilinx-core.c b/drivers/fpga/xilinx-core.c new file mode 100644 index 0000000000000..597e8b7a530b7 --- /dev/null +++ b/drivers/fpga/xilinx-core.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Common parts of the Xilinx Spartan6 and 7 Series FPGA manager drivers= . + * + * Copyright (C) 2017 DENX Software Engineering + * + * Anatolij Gustschin + */ + +#include "xilinx-core.h" + +#include +#include +#include +#include + +static int get_done_gpio(struct fpga_manager *mgr) +{ + struct xilinx_fpga_core *core =3D mgr->priv; + int ret; + + ret =3D gpiod_get_value(core->done); + if (ret < 0) + dev_err(&mgr->dev, "Error reading DONE (%d)\n", ret); + + return ret; +} + +static enum fpga_mgr_states xilinx_core_state(struct fpga_manager *mgr) +{ + if (!get_done_gpio(mgr)) + return FPGA_MGR_STATE_RESET; + + return FPGA_MGR_STATE_UNKNOWN; +} + +/** + * wait_for_init_b - wait for the INIT_B pin to have a given state, or w= ait + * a given delay if the pin is unavailable + * + * @mgr: The FPGA manager object + * @value: Value INIT_B to wait for (1 =3D asserted =3D low) + * @alt_udelay: Delay to wait if the INIT_B GPIO is not available + * + * Returns 0 when the INIT_B GPIO reached the given state or -ETIMEDOUT = if + * too much time passed waiting for that. If no INIT_B GPIO is available + * then always return 0. + */ +static int wait_for_init_b(struct fpga_manager *mgr, int value, + unsigned long alt_udelay) +{ + struct xilinx_fpga_core *core =3D mgr->priv; + unsigned long timeout =3D jiffies + msecs_to_jiffies(1000); + + if (core->init_b) { + while (time_before(jiffies, timeout)) { + int ret =3D gpiod_get_value(core->init_b); + + if (ret =3D=3D value) + return 0; + + if (ret < 0) { + dev_err(&mgr->dev, + "Error reading INIT_B (%d)\n", ret); + return ret; + } + + usleep_range(100, 400); + } + + dev_err(&mgr->dev, "Timeout waiting for INIT_B to %s\n", + value ? "assert" : "deassert"); + return -ETIMEDOUT; + } + + udelay(alt_udelay); + + return 0; +} + +static int xilinx_core_write_init(struct fpga_manager *mgr, + struct fpga_image_info *info, const char *buf, + size_t count) +{ + struct xilinx_fpga_core *core =3D mgr->priv; + int err; + + if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) { + dev_err(&mgr->dev, "Partial reconfiguration not supported\n"); + return -EINVAL; + } + + gpiod_set_value(core->prog_b, 1); + + err =3D wait_for_init_b(mgr, 1, 1); /* min is 500 ns */ + if (err) { + gpiod_set_value(core->prog_b, 0); + return err; + } + + gpiod_set_value(core->prog_b, 0); + + err =3D wait_for_init_b(mgr, 0, 0); + if (err) + return err; + + if (get_done_gpio(mgr)) { + dev_err(&mgr->dev, "Unexpected DONE pin state...\n"); + return -EIO; + } + + /* program latency */ + usleep_range(7500, 7600); + return 0; +} + +static int xilinx_core_write(struct fpga_manager *mgr, const char *buf, + size_t count) +{ + struct xilinx_fpga_core *core =3D mgr->priv; + + return core->write(core, buf, count); +} + +static int xilinx_core_write_complete(struct fpga_manager *mgr, + struct fpga_image_info *info) +{ + struct xilinx_fpga_core *core =3D mgr->priv; + unsigned long timeout =3D + jiffies + usecs_to_jiffies(info->config_complete_timeout_us); + bool expired =3D false; + int done; + int ret; + const char padding[1] =3D { 0xff }; + + /* + * This loop is carefully written such that if the driver is + * scheduled out for more than 'timeout', we still check for DONE + * before giving up and we apply 8 extra CCLK cycles in all cases. + */ + while (!expired) { + expired =3D time_after(jiffies, timeout); + + done =3D get_done_gpio(mgr); + if (done < 0) + return done; + + ret =3D core->write(core, padding, 1); + if (ret) + return ret; + + if (done) + return 0; + } + + if (core->init_b) { + ret =3D gpiod_get_value(core->init_b); + + if (ret < 0) { + dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret); + return ret; + } + + dev_err(&mgr->dev, + ret ? "CRC error or invalid device\n" : + "Missing sync word or incomplete bitstream\n"); + } else { + dev_err(&mgr->dev, "Timeout after config data transfer\n"); + } + + return -ETIMEDOUT; +} + +static const struct fpga_manager_ops xilinx_core_ops =3D { + .state =3D xilinx_core_state, + .write_init =3D xilinx_core_write_init, + .write =3D xilinx_core_write, + .write_complete =3D xilinx_core_write_complete, +}; + +int xilinx_core_probe(struct xilinx_fpga_core *core) +{ + struct fpga_manager *mgr; + + if (!core || !core->dev || !core->write) + return -EINVAL; + + /* PROGRAM_B is active low */ + core->prog_b =3D devm_gpiod_get(core->dev, "prog_b", GPIOD_OUT_LOW); + if (IS_ERR(core->prog_b)) + return dev_err_probe(core->dev, PTR_ERR(core->prog_b), + "Failed to get PROGRAM_B gpio\n"); + + core->init_b =3D devm_gpiod_get_optional(core->dev, "init-b", GPIOD_IN)= ; + if (IS_ERR(core->init_b)) + return dev_err_probe(core->dev, PTR_ERR(core->init_b), + "Failed to get INIT_B gpio\n"); + + core->done =3D devm_gpiod_get(core->dev, "done", GPIOD_IN); + if (IS_ERR(core->done)) + return dev_err_probe(core->dev, PTR_ERR(core->done), + "Failed to get DONE gpio\n"); + + mgr =3D devm_fpga_mgr_register(core->dev, + "Xilinx Slave Serial FPGA Manager", + &xilinx_core_ops, core); + return PTR_ERR_OR_ZERO(mgr); +} diff --git a/drivers/fpga/xilinx-core.h b/drivers/fpga/xilinx-core.h new file mode 100644 index 0000000000000..bea190287b403 --- /dev/null +++ b/drivers/fpga/xilinx-core.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __XILINX_CORE_H +#define __XILINX_CORE_H + +#include + +/** + * struct xilinx_fpga_core - interface between the driver and the core m= anager + * of Xilinx 7 Series FPGA manager + * @dev: device node, must be set by the driver + * @write: write callback of the driver, must be set by the driver + * @prog_b: PROGRAM_B gpio, handled by the core manager + * @init_b: INIT_B gpio, handled by the core manager + * @done: DONE gpio, handled by the core manager + */ +struct xilinx_fpga_core { + struct device *dev; + int (*write)(struct xilinx_fpga_core *core, const char *buf, + size_t count); + struct gpio_desc *prog_b; + struct gpio_desc *init_b; + struct gpio_desc *done; +}; + +int xilinx_core_probe(struct xilinx_fpga_core *core); + +#endif /* __XILINX_CORE_H */ diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c index e1a227e7ff2ae..12f401502a53a 100644 --- a/drivers/fpga/xilinx-spi.c +++ b/drivers/fpga/xilinx-spi.c @@ -10,127 +10,24 @@ * the slave serial configuration interface. */ =20 -#include -#include -#include -#include +#include "xilinx-core.h" + #include #include #include #include -#include =20 struct xilinx_spi_conf { + struct xilinx_fpga_core core; struct spi_device *spi; - struct gpio_desc *prog_b; - struct gpio_desc *init_b; - struct gpio_desc *done; }; =20 -static int get_done_gpio(struct fpga_manager *mgr) -{ - struct xilinx_spi_conf *conf =3D mgr->priv; - int ret; - - ret =3D gpiod_get_value(conf->done); - - if (ret < 0) - dev_err(&mgr->dev, "Error reading DONE (%d)\n", ret); - - return ret; -} - -static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr) -{ - if (!get_done_gpio(mgr)) - return FPGA_MGR_STATE_RESET; - - return FPGA_MGR_STATE_UNKNOWN; -} - -/** - * wait_for_init_b - wait for the INIT_B pin to have a given state, or w= ait - * a given delay if the pin is unavailable - * - * @mgr: The FPGA manager object - * @value: Value INIT_B to wait for (1 =3D asserted =3D low) - * @alt_udelay: Delay to wait if the INIT_B GPIO is not available - * - * Returns 0 when the INIT_B GPIO reached the given state or -ETIMEDOUT = if - * too much time passed waiting for that. If no INIT_B GPIO is available - * then always return 0. - */ -static int wait_for_init_b(struct fpga_manager *mgr, int value, - unsigned long alt_udelay) -{ - struct xilinx_spi_conf *conf =3D mgr->priv; - unsigned long timeout =3D jiffies + msecs_to_jiffies(1000); - - if (conf->init_b) { - while (time_before(jiffies, timeout)) { - int ret =3D gpiod_get_value(conf->init_b); - - if (ret =3D=3D value) - return 0; - - if (ret < 0) { - dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret); - return ret; - } - - usleep_range(100, 400); - } - - dev_err(&mgr->dev, "Timeout waiting for INIT_B to %s\n", - value ? "assert" : "deassert"); - return -ETIMEDOUT; - } - - udelay(alt_udelay); - - return 0; -} - -static int xilinx_spi_write_init(struct fpga_manager *mgr, - struct fpga_image_info *info, - const char *buf, size_t count) -{ - struct xilinx_spi_conf *conf =3D mgr->priv; - int err; - - if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) { - dev_err(&mgr->dev, "Partial reconfiguration not supported\n"); - return -EINVAL; - } - - gpiod_set_value(conf->prog_b, 1); - - err =3D wait_for_init_b(mgr, 1, 1); /* min is 500 ns */ - if (err) { - gpiod_set_value(conf->prog_b, 0); - return err; - } - - gpiod_set_value(conf->prog_b, 0); +#define to_xilinx_spi_conf(obj) container_of(obj, struct xilinx_spi_conf= , core) =20 - err =3D wait_for_init_b(mgr, 0, 0); - if (err) - return err; - - if (get_done_gpio(mgr)) { - dev_err(&mgr->dev, "Unexpected DONE pin state...\n"); - return -EIO; - } - - /* program latency */ - usleep_range(7500, 7600); - return 0; -} - -static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf, +static int xilinx_spi_write(struct xilinx_fpga_core *core, const char *b= uf, size_t count) { - struct xilinx_spi_conf *conf =3D mgr->priv; + struct xilinx_spi_conf *conf =3D to_xilinx_spi_conf(core); const char *fw_data =3D buf; const char *fw_data_end =3D fw_data + count; =20 @@ -143,7 +40,7 @@ static int xilinx_spi_write(struct fpga_manager *mgr, = const char *buf, =20 ret =3D spi_write(conf->spi, fw_data, stride); if (ret) { - dev_err(&mgr->dev, "SPI error in firmware write: %d\n", + dev_err(core->dev, "SPI error in firmware write: %d\n", ret); return ret; } @@ -153,109 +50,26 @@ static int xilinx_spi_write(struct fpga_manager *mg= r, const char *buf, return 0; } =20 -static int xilinx_spi_apply_cclk_cycles(struct xilinx_spi_conf *conf) -{ - struct spi_device *spi =3D conf->spi; - const u8 din_data[1] =3D { 0xff }; - int ret; - - ret =3D spi_write(conf->spi, din_data, sizeof(din_data)); - if (ret) - dev_err(&spi->dev, "applying CCLK cycles failed: %d\n", ret); - - return ret; -} - -static int xilinx_spi_write_complete(struct fpga_manager *mgr, - struct fpga_image_info *info) -{ - struct xilinx_spi_conf *conf =3D mgr->priv; - unsigned long timeout =3D jiffies + usecs_to_jiffies(info->config_compl= ete_timeout_us); - bool expired =3D false; - int done; - int ret; - - /* - * This loop is carefully written such that if the driver is - * scheduled out for more than 'timeout', we still check for DONE - * before giving up and we apply 8 extra CCLK cycles in all cases. - */ - while (!expired) { - expired =3D time_after(jiffies, timeout); - - done =3D get_done_gpio(mgr); - if (done < 0) - return done; - - ret =3D xilinx_spi_apply_cclk_cycles(conf); - if (ret) - return ret; - - if (done) - return 0; - } - - if (conf->init_b) { - ret =3D gpiod_get_value(conf->init_b); - - if (ret < 0) { - dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret); - return ret; - } - - dev_err(&mgr->dev, - ret ? "CRC error or invalid device\n" - : "Missing sync word or incomplete bitstream\n"); - } else { - dev_err(&mgr->dev, "Timeout after config data transfer\n"); - } - - return -ETIMEDOUT; -} - -static const struct fpga_manager_ops xilinx_spi_ops =3D { - .state =3D xilinx_spi_state, - .write_init =3D xilinx_spi_write_init, - .write =3D xilinx_spi_write, - .write_complete =3D xilinx_spi_write_complete, -}; - static int xilinx_spi_probe(struct spi_device *spi) { struct xilinx_spi_conf *conf; - struct fpga_manager *mgr; =20 conf =3D devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL); if (!conf) return -ENOMEM; =20 + conf->core.dev =3D &spi->dev; + conf->core.write =3D xilinx_spi_write; conf->spi =3D spi; =20 - /* PROGRAM_B is active low */ - conf->prog_b =3D devm_gpiod_get(&spi->dev, "prog_b", GPIOD_OUT_LOW); - if (IS_ERR(conf->prog_b)) - return dev_err_probe(&spi->dev, PTR_ERR(conf->prog_b), - "Failed to get PROGRAM_B gpio\n"); - - conf->init_b =3D devm_gpiod_get_optional(&spi->dev, "init-b", GPIOD_IN)= ; - if (IS_ERR(conf->init_b)) - return dev_err_probe(&spi->dev, PTR_ERR(conf->init_b), - "Failed to get INIT_B gpio\n"); - - conf->done =3D devm_gpiod_get(&spi->dev, "done", GPIOD_IN); - if (IS_ERR(conf->done)) - return dev_err_probe(&spi->dev, PTR_ERR(conf->done), - "Failed to get DONE gpio\n"); - - mgr =3D devm_fpga_mgr_register(&spi->dev, - "Xilinx Slave Serial FPGA Manager", - &xilinx_spi_ops, conf); - return PTR_ERR_OR_ZERO(mgr); + return xilinx_core_probe(&conf->core); } =20 #ifdef CONFIG_OF static const struct of_device_id xlnx_spi_of_match[] =3D { - { .compatible =3D "xlnx,fpga-slave-serial", }, + { + .compatible =3D "xlnx,fpga-slave-serial", + }, {} }; MODULE_DEVICE_TABLE(of, xlnx_spi_of_match); --=20 2.43.0