From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.6 required=3.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_PASS,USER_AGENT_MUTT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 44ACDC10F13 for ; Mon, 8 Apr 2019 18:05:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id F19B2206C0 for ; Mon, 8 Apr 2019 18:05:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1554746755; bh=aUyIzC9P5c7vp8X97t/26QxdpadmM7O0vTi8a+X6kas=; h=Date:From:To:Cc:Subject:References:In-Reply-To:List-ID:From; b=zhQubPZ0csMFMKvzyDjMY+5tPYOx1df42KjzGFriytEekAo7ig7PEDxhBkoXLjqtQ LK52ox3Z7zM9YECwdqqWy7PJbdbPtkdYqef06YShdtwjdgbuiWPLNS1G0Jmoxd8WGJ YHF0PHA2X8jtz0uZd7H5av9BQDscBLISa5FqHFEo= Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729036AbfDHSFx (ORCPT ); Mon, 8 Apr 2019 14:05:53 -0400 Received: from mail-pf1-f194.google.com ([209.85.210.194]:41834 "EHLO mail-pf1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725983AbfDHSFx (ORCPT ); Mon, 8 Apr 2019 14:05:53 -0400 Received: by mail-pf1-f194.google.com with SMTP id 188so8069103pfd.8; Mon, 08 Apr 2019 11:05:52 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=i1QE+whj+Xlew4p+uSvaE2zunaBTwuk5GoqvNtWSlbU=; b=E87+aVDGXS4ONINS3PHDo4kQpszXf0vu4i6mYop2Se/GHir36BMd6R76K88UF/+eZU 4YX7ABGN3pkQRL0hE2WysHoBAKroJm5++5uyOUw1ZSzxZcdFiJ3Q8mlgryZGxr4hYHoM vhIVeWU+IxRCduxn96oglszT3uJL+DMOT1nTolVjspaLwn0tFu4IXR5L+4riZM01nYgO v1x51QoGhydPJrPNM34ORalxCLo6SOLuydyEnKZrizDTJoDhlCjYmzylTx5ENkpSHL1+ XHhltJCuXLLpOfDQPRxMskE4qkjwKmZU9bFPNeZOOZqp0wVVUXr2Xv7OK+JgjDymTvKd vgUg== X-Gm-Message-State: APjAAAVpTgtRv/qWcIlTQ+o8M99mXYRLcHhbnsF5hhcDKHv+SQsVqgzQ DISMtFz2VYvumK9kCKJpR4c= X-Google-Smtp-Source: APXvYqyPR3+Pln07g9+roZQGQnRoBGLiud72c3oW6DPbm1ayXHihNjPtOXuS0dXaKR1u5ug/snwbZA== X-Received: by 2002:a62:e213:: with SMTP id a19mr31814413pfi.85.1554746752063; Mon, 08 Apr 2019 11:05:52 -0700 (PDT) Received: from localhost ([207.114.172.147]) by smtp.gmail.com with ESMTPSA id k14sm59999239pfb.125.2019.04.08.11.05.50 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Mon, 08 Apr 2019 11:05:51 -0700 (PDT) Date: Mon, 8 Apr 2019 11:05:49 -0700 From: Moritz Fischer To: Anatolij Gustschin Cc: linux-usb@vger.kernel.org, linux-spi@vger.kernel.org, linux-fpga@vger.kernel.org, linux-kernel@vger.kernel.org, gregkh@linuxfoundation.org, broonie@kernel.org, atull@kernel.org, mdf@kernel.org Subject: Re: [PATCH v4 3/3] fpga: Add fpga manager driver for ARRI Altera FPP Message-ID: <20190408180151.GA5643@archbook> References: <20190221202506.17744-1-agust@denx.de> <20190221202506.17744-4-agust@denx.de> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20190221202506.17744-4-agust@denx.de> User-Agent: Mutt/1.11.4 (2019-03-13) Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi Anatolij, looks mostly good, a couple of nits inline. On Thu, Feb 21, 2019 at 09:25:06PM +0100, Anatolij Gustschin wrote: > Add FPGA manager driver for loading ARRI Altera FPGAs via fast > passive parallel (FPP) interface using FTDI FT232H chip. > > Signed-off-by: Anatolij Gustschin > --- > .../ABI/testing/sysfs-driver-ftdi-fifo-fpp | 7 + > drivers/fpga/Kconfig | 7 + > drivers/fpga/Makefile | 1 + > drivers/fpga/ftdi-fifo-fpp.c | 594 ++++++++++++++++++ > 4 files changed, 609 insertions(+) > create mode 100644 Documentation/ABI/testing/sysfs-driver-ftdi-fifo-fpp > create mode 100644 drivers/fpga/ftdi-fifo-fpp.c > > diff --git a/Documentation/ABI/testing/sysfs-driver-ftdi-fifo-fpp b/Documentation/ABI/testing/sysfs-driver-ftdi-fifo-fpp > new file mode 100644 > index 000000000000..f3055100d07e > --- /dev/null > +++ b/Documentation/ABI/testing/sysfs-driver-ftdi-fifo-fpp > @@ -0,0 +1,7 @@ > +What: /sys/bus/platform/devices/ftdi-fifo-fpp-mgr.N/cfg_mode > +Date: Feb 2019 > +Kernel Version: 5.2 > +Contact: Anatolij Gustschin > +Description: > + Contains either "fifo" or "bitbang" and controls if fifo > + of bitbang configuration mode is used in the driver. > diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig > index c20445b867ae..19d82163a0b2 100644 > --- a/drivers/fpga/Kconfig > +++ b/drivers/fpga/Kconfig > @@ -50,6 +50,13 @@ config FPGA_MGR_ALTERA_CVP > FPGA manager driver support for Arria-V, Cyclone-V, Stratix-V > and Arria 10 Altera FPGAs using the CvP interface over PCIe. > > +config FPGA_MGR_FTDI_FIFO_FPP > + tristate "Altera FPP over FT232H FIFO" > + depends on USB_FT232H_INTF > + help > + FPGA manager driver support for Altera fast passive parallel > + interface (FPP) over FT232H FT245 FIFO. > + > config FPGA_MGR_ZYNQ_FPGA > tristate "Xilinx Zynq FPGA" > depends on ARCH_ZYNQ || COMPILE_TEST > diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile > index c0dd4c82fbdb..61725d31e6d1 100644 > --- a/drivers/fpga/Makefile > +++ b/drivers/fpga/Makefile > @@ -9,6 +9,7 @@ obj-$(CONFIG_FPGA) += fpga-mgr.o > # FPGA Manager Drivers > obj-$(CONFIG_FPGA_MGR_ALTERA_CVP) += altera-cvp.o > obj-$(CONFIG_FPGA_MGR_ALTERA_PS_SPI) += altera-ps-spi.o > +obj-$(CONFIG_FPGA_MGR_FTDI_FIFO_FPP) += ftdi-fifo-fpp.o > obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o > obj-$(CONFIG_FPGA_MGR_MACHXO2_SPI) += machxo2-spi.o > obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o > diff --git a/drivers/fpga/ftdi-fifo-fpp.c b/drivers/fpga/ftdi-fifo-fpp.c > new file mode 100644 > index 000000000000..2bc72335cd70 > --- /dev/null > +++ b/drivers/fpga/ftdi-fifo-fpp.c > @@ -0,0 +1,594 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Altera FPGA firmware upload via FPP using FT232H Bitbang/FT245-FIFO. > + * > + * Copyright (C) 2017 - 2018 DENX Software Engineering Maybe bump that to 2019 ;-) > + * Anatolij Gustschin > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define BULK_OUT_BUF_SZ SZ_1M > +#define MAX_RETRIES 10 > + > +/* > + * With logic of CPLD we can write the state of nConfig pin and > + * read back the state of some pins (conf_done, init_done, nStatus). > + * Status header and bit assignment in data register on CPLD. > + */ > +#define INPUT_HEADER_0 0xA5 > +#define INPUT_HEADER_1 0x5A > +#define IN_CONF_DONE BIT(0) > +#define IN_INIT_DONE BIT(1) > +#define IN_ADDR_SELECT BIT(4) > +#define IN_BOARD_REV BIT(5) > +#define OUT_NCONFIG BIT(0) > +#define OUT_RESET_N BIT(1) > + > +enum fpp_board_rev { > + BOARD_REVA = 1, > + BOARD_REVB = 2, > +}; > + > +enum fpp_addr_sel { > + ADDR_SELECT_INVALID, > + ADDR_SELECT_GND, > + ADDR_SELECT_NC > +}; > + > +struct fpp_mgr_ops { > + int (*write_init)(struct fpga_manager *mgr, > + struct fpga_image_info *info, > + const char *buf, size_t count); > + int (*write)(struct fpga_manager *mgr, const char *buf, size_t count); > + int (*write_complete)(struct fpga_manager *mgr, > + struct fpga_image_info *info); > +}; > + > +struct fpp_fpga_mgr_priv { > + struct platform_device *pdev; > + struct usb_interface *intf; > + const struct ft232h_intf_ops *iops; > + struct fpga_manager *mgr; > + struct fpp_mgr_ops *ops; > + struct gpio_desc *nconfig; > + struct gpio_desc *conf_done; > + char cfg_mode[8]; > + u8 out_data_port; > + int index; > + void *bulk_buf; > + char usb_dev_id[32]; > + char fpga_mgr_name[64]; > + enum fpp_board_rev rev; > + enum fpp_addr_sel addr_sel; > +}; > + > +static int fpp_fpga_mgr_set_data_port(struct fpp_fpga_mgr_priv *priv, > + u8 bitmask, u8 value) > +{ > + struct device *dev = &priv->pdev->dev; > + struct bulk_desc desc; > + u8 *data; > + int ret; > + > + /* > + * With CPLD connected (in FT245 FIFO mode) we use ACBUS8&9 > + * pins to switch between data and command mode: > + * ACBUS8&9 == 0, 0 --> normal mode (data communication) > + * ACBUS8&9 == 1, 0 --> command mode > + */ > + gpiod_set_raw_value_cansleep(priv->nconfig, 1); > + gpiod_set_raw_value_cansleep(priv->conf_done, 0); > + msleep(50); You could use a constant here for the 50 ... > + > + /* Write commands to CPLD */ > + ret = priv->iops->set_bitmode(priv->intf, 0x00, BITMODE_SYNCFF); > + if (ret) > + return ret; > + > + if (value) > + priv->out_data_port |= bitmask; > + else > + priv->out_data_port &= ~bitmask; > + > + data = priv->bulk_buf; > + *data = priv->out_data_port; > + > + desc.dir_out = true; > + desc.act_len = 0; > + desc.len = 1; > + desc.data = data; > + desc.timeout = FTDI_USB_WRITE_TIMEOUT; > + > + ret = priv->iops->bulk_xfer(priv->intf, &desc); > + if (ret) { > + dev_err(dev, "Writing in SYNCFF mode failed: %d\n", ret); > + return ret; > + } > + > + msleep(50); and here ... > + /* Switch back to data mode with ACBUS8&9 back to low */ > + gpiod_set_raw_value_cansleep(priv->nconfig, 0); > + gpiod_set_raw_value_cansleep(priv->conf_done, 0); > + msleep(50); and here ... > + > + return 0; > +} > + > +static int fpp_fpga_mgr_bitbang_write_init(struct fpga_manager *mgr, > + struct fpga_image_info *info, > + const char *buf, size_t count) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + struct device *dev = &priv->pdev->dev; > + int retries = MAX_RETRIES; > + int ret; > + > + gpiod_set_value_cansleep(priv->nconfig, 0); > + msleep(50); and here ... > + gpiod_set_value_cansleep(priv->nconfig, 1); > + msleep(50); and here ... > + gpiod_set_value_cansleep(priv->nconfig, 0); > + > + /* Wait for CONF_DONE to get low */ Does the retry number come from datasheet? > + do { > + msleep(50); > + > + ret = gpiod_get_value_cansleep(priv->conf_done); > + if (ret < 0) { > + dev_err(dev, "Failed to get CONF_DONE pin: %d\n", ret); > + return ret; > + } > + > + if (!ret) > + break; > + } while (--retries > 0); > + > + if (!retries) { > + dev_warn(dev, "CONF_DONE low wait timeout\n"); > + return -ETIMEDOUT; > + } > + > + ret = priv->iops->set_bitmode(priv->intf, 0xff, BITMODE_BITBANG); > + if (ret < 0) > + return ret; > + > + /* Set max. working baud rate (for hardware without CPLD) */ > + return priv->iops->set_baudrate(priv->intf, 700000); > +} > + > +static int fpp_fpga_mgr_bitbang_write(struct fpga_manager *mgr, > + const char *buf, size_t count) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + struct bulk_desc desc; > + size_t blk_sz; > + int ret; > + > + desc.data = priv->bulk_buf; > + desc.dir_out = true; > + desc.timeout = FTDI_USB_WRITE_TIMEOUT; > + > + while (count) { > + blk_sz = min_t(size_t, count, BULK_OUT_BUF_SZ); > + memcpy(priv->bulk_buf, buf, blk_sz); > + desc.act_len = 0; > + desc.len = blk_sz; > + ret = priv->iops->bulk_xfer(priv->intf, &desc); > + if (ret < 0) > + return ret; > + > + buf += desc.act_len; > + count -= desc.act_len; > + } > + > + return 0; > +} > + > +static int fpp_fpga_mgr_bitbang_write_complete(struct fpga_manager *mgr, > + struct fpga_image_info *info) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + struct device *dev = &priv->pdev->dev; > + int retries = MAX_RETRIES; > + int ret; > + > + /* Wait for CONF_DONE to get high */ > + do { > + msleep(50); > + > + ret = gpiod_get_value_cansleep(priv->conf_done); > + if (ret < 0) > + return ret; > + > + if (ret) > + break; > + } while (--retries > 0); > + > + if (!retries) { > + dev_warn(dev, "CONF_DONE wait timeout\n"); warn or err? > + return -ETIMEDOUT; > + } > + > + priv->iops->disable_bitbang(priv->intf); > + return 0; > +} > + > +static inline bool status_hdr_is_valid(u8 *buf) > +{ > + return buf[0] == INPUT_HEADER_0 && buf[1] == INPUT_HEADER_1; > +} > + > +static int fpp_fpga_mgr_read_status(struct fpp_fpga_mgr_priv *priv, u8 *status) > +{ > + struct device *dev = &priv->pdev->dev; > + u8 *inbuf = priv->bulk_buf; > + int retries = MAX_RETRIES; > + int ret; > + > + if (!status) > + return -EINVAL; > + > + /* Wait until CPLD sends valid header and status register */ > + do { > + ret = priv->iops->read_data(priv->intf, inbuf, 64); > + if (ret < 0) { > + dev_err(dev, "Can't read status data: %d\n", ret); > + return ret; > + } > + > + /* Check input buffer header */ > + if (ret >= 4 && status_hdr_is_valid(inbuf)) { > + *status = inbuf[2]; > + return 0; > + } > + > + /* Wait and read back status again */ > + msleep(100); /* CPLD sends status every 100ms */ > + } while (--retries > 0); > + > + dev_warn(dev, "Timeout when reading status\n"); > + return -ETIMEDOUT; > +} > + > +static int fpp_fpga_mgr_ft245_fifo_write_init(struct fpga_manager *mgr, > + struct fpga_image_info *info, > + const char *buf, size_t count) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + struct device *dev = &priv->pdev->dev; > + int retries = MAX_RETRIES; > + int ret; > + u8 status; > + > + gpiod_direction_output_raw(priv->conf_done, 0); > + > + /* Set/reset nConfig via commands to CPLD */ > + ret = fpp_fpga_mgr_set_data_port(priv, OUT_NCONFIG, 1); > + if (ret) > + return ret; > + ret = fpp_fpga_mgr_set_data_port(priv, OUT_NCONFIG, 0); > + if (ret) > + return ret; > + ret = fpp_fpga_mgr_set_data_port(priv, OUT_NCONFIG, 1); > + if (ret) > + return ret; > + > + /* In FT245 FIFO mode we need sync FIFO mode to talk to FPGA */ > + ret = priv->iops->set_bitmode(priv->intf, 0xff, BITMODE_SYNCFF); > + if (ret) > + return ret; > + > + /* Wait until FPGA is ready for loading (conf_done zero) or timeout */ > + do { > + ret = fpp_fpga_mgr_read_status(priv, &status); > + if (!ret) { > + /* Check conf_done status */ > + if ((status & IN_CONF_DONE) == 0) > + break; > + } > + } while (--retries > 0); > + > + if (!retries) { > + dev_warn(dev, "CONF_DONE wait timeout\n"); > + return -ETIMEDOUT; > + } > + > + /* Configure for max. baud rate (3MHz * 4 in bitbang mode) */ > + return priv->iops->set_baudrate(priv->intf, 3000000); > +} > + > +static int fpp_fpga_mgr_ft245_fifo_write(struct fpga_manager *mgr, > + const char *buf, size_t count) > +{ > + return fpp_fpga_mgr_bitbang_write(mgr, buf, count); > +} > + > +static int fpp_fpga_mgr_ft245_fifo_write_complete(struct fpga_manager *mgr, > + struct fpga_image_info *info) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + struct device *dev = &priv->pdev->dev; > + int retries = MAX_RETRIES; > + int ret; > + u8 mask, status; Can you swap the two lines, reverse xmas-tree please. > + > + mask = IN_CONF_DONE | IN_INIT_DONE; > + > + do { > + ret = fpp_fpga_mgr_read_status(priv, &status); > + if (!ret) { > + /* Check conf_done/init_done status */ > + if ((status & mask) == mask) > + break; > + } > + } while (--retries > 0); > + > + if (!retries) { > + dev_warn(dev, "INIT_DONE wait timeout\n"); > + return -ETIMEDOUT; > + } > + > + /* Release Reset_n, keep nCONFIG high, too! */ > + return fpp_fpga_mgr_set_data_port(priv, OUT_NCONFIG | OUT_RESET_N, 1); > +} > + > +static enum fpga_mgr_states fpp_fpga_mgr_state(struct fpga_manager *mgr) > +{ > + return FPGA_MGR_STATE_UNKNOWN; > +} > + > +static int fpp_fpga_mgr_write_init(struct fpga_manager *mgr, > + struct fpga_image_info *info, > + const char *buf, size_t count) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + > + if (info && info->flags & FPGA_MGR_PARTIAL_RECONFIG) { > + dev_err(&mgr->dev, "Partial reconfiguration not supported.\n"); > + return -EINVAL; > + } > + > + if (priv->ops->write_init) > + return priv->ops->write_init(mgr, info, buf, count); > + > + return -ENODEV; > +} > + > +static int fpp_fpga_mgr_write(struct fpga_manager *mgr, const char *buf, > + size_t count) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + > + if (priv->ops->write) > + return priv->ops->write(mgr, buf, count); > + > + return -ENODEV; > +} > + > +static int fpp_fpga_mgr_write_complete(struct fpga_manager *mgr, > + struct fpga_image_info *info) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + > + if (priv->ops->write_complete) > + return priv->ops->write_complete(mgr, info); > + > + return -ENODEV; > +} > + > +static struct fpp_mgr_ops fpp_mgr_bitbang_ops = { > + .write_init = fpp_fpga_mgr_bitbang_write_init, > + .write = fpp_fpga_mgr_bitbang_write, > + .write_complete = fpp_fpga_mgr_bitbang_write_complete, > +}; > + > +static struct fpp_mgr_ops fpp_mgr_ft245_fifo_ops = { > + .write_init = fpp_fpga_mgr_ft245_fifo_write_init, > + .write = fpp_fpga_mgr_ft245_fifo_write, > + .write_complete = fpp_fpga_mgr_ft245_fifo_write_complete, > +}; > + > +static const struct fpga_manager_ops fpp_fpga_mgr_ops = { > + .state = fpp_fpga_mgr_state, > + .write_init = fpp_fpga_mgr_write_init, > + .write = fpp_fpga_mgr_write, > + .write_complete = fpp_fpga_mgr_write_complete, > +}; > + > +static ssize_t cfg_mode_show(struct device *dev, struct device_attribute *attr, > + char *buf) > +{ > + struct platform_device *pdev = to_platform_device(dev); > + struct fpga_manager *mgr = platform_get_drvdata(pdev); > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + > + return snprintf(buf, PAGE_SIZE, "%s\n", priv->cfg_mode); > +} > + > +static ssize_t cfg_mode_store(struct device *dev, struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct platform_device *pdev = to_platform_device(dev); > + struct fpga_manager *mgr = platform_get_drvdata(pdev); > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + > + if (!count || count > sizeof(priv->cfg_mode)) > + return -EINVAL; > + > + if (!strncmp(buf, "fifo", 4)) { > + strncpy(priv->cfg_mode, buf, sizeof(priv->cfg_mode) - 1); > + priv->cfg_mode[4] = 0; > + priv->ops = &fpp_mgr_ft245_fifo_ops; > + gpiod_direction_output_raw(priv->conf_done, 0); > + } else if (!strncmp(buf, "bitbang", 7)) { > + strncpy(priv->cfg_mode, buf, sizeof(priv->cfg_mode) - 1); > + priv->cfg_mode[7] = 0; > + priv->ops = &fpp_mgr_bitbang_ops; > + gpiod_direction_input(priv->conf_done); > + } else { > + return -EINVAL; > + } > + > + return count; > +} > + > +static DEVICE_ATTR_RW(cfg_mode); > + > +static int fpp_fpga_mgr_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct fpp_fpga_mgr_priv *priv; > + struct fpga_manager *mgr; > + struct fifo_fpp_mgr_platform_data *pd; > + int ret, retries = MAX_RETRIES; > + char id_string[8]; > + u8 status = 0; > + > + pd = dev->platform_data; > + if (!pd) { > + dev_err(dev, "Missing platform data.\n"); > + return -EINVAL; > + } > + > + if (!pd->ops || > + !pd->ops->bulk_xfer || !pd->ops->ctrl_xfer || > + !pd->ops->read_data || !pd->ops->write_data || > + !pd->ops->set_bitmode || !pd->ops->set_baudrate || > + !pd->ops->disable_bitbang) > + return -EINVAL; > + > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->intf = to_usb_interface(dev->parent); > + priv->iops = pd->ops; > + > + ret = sscanf(dev_name(dev->parent), "%s", priv->usb_dev_id); > + if (ret != 1) { > + dev_err(dev, "Can't get parent device name: %d\n", ret); > + return -ENODEV; > + } > + > + priv->pdev = pdev; > + priv->ops = &fpp_mgr_ft245_fifo_ops; > + strncpy(priv->cfg_mode, "fifo", sizeof(priv->cfg_mode)); > + > + priv->nconfig = devm_gpiod_get(dev, "nconfig", GPIOD_OUT_LOW); > + if (IS_ERR(priv->nconfig)) { > + ret = PTR_ERR(priv->nconfig); > + dev_err(dev, "Failed to get nconfig gpio: %d\n", ret); > + return ret; > + } > + > + priv->conf_done = devm_gpiod_get(dev, "conf_done", GPIOD_OUT_LOW); > + if (IS_ERR(priv->conf_done)) { > + ret = PTR_ERR(priv->conf_done); > + dev_err(dev, "Failed to get conf_done gpio: %d\n", ret); > + goto err_cfg1; > + } > + > + priv->bulk_buf = devm_kmalloc(dev, BULK_OUT_BUF_SZ, > + GFP_KERNEL | GFP_DMA32); > + if (!priv->bulk_buf) { > + ret = -ENOMEM; > + goto err_cfg2; > + } > + > + ret = priv->iops->set_bitmode(priv->intf, 0xff, BITMODE_SYNCFF); > + if (ret) > + goto err_cfg2; > + > + /* Read status register from CPLD */ > + do { > + ret = fpp_fpga_mgr_read_status(priv, &status); > + if (!ret) > + break; > + } while (--retries > 0); > + > + if (!retries) { > + ret = -ETIMEDOUT; > + goto err_cfg2; > + } > + > + priv->rev = (status & IN_BOARD_REV) ? BOARD_REVB : BOARD_REVA; > + > + if (priv->rev == BOARD_REVB) { > + priv->addr_sel = (status & IN_ADDR_SELECT) ? > + ADDR_SELECT_NC : ADDR_SELECT_GND; > + if (priv->addr_sel == ADDR_SELECT_NC) > + strncpy(id_string, "right", sizeof(id_string)); > + else > + strncpy(id_string, "left", sizeof(id_string)); > + } else { > + priv->addr_sel = ADDR_SELECT_INVALID; > + strncpy(id_string, "single", sizeof(id_string)); > + } > + > + dev_info(dev, "Board Rev %d, Addr Sel %d\n", priv->rev, priv->addr_sel); > + > + /* Use unique board ID and USB bus/port in FPGA manager name */ > + snprintf(priv->fpga_mgr_name, sizeof(priv->fpga_mgr_name), > + "ftdi-fpp-fpga-mgr %s %s", id_string, priv->usb_dev_id); > + > + mgr = devm_fpga_mgr_create(dev, priv->fpga_mgr_name, > + &fpp_fpga_mgr_ops, priv); > + if (!mgr) > + goto err_cfg2; > + > + platform_set_drvdata(pdev, mgr); > + > + ret = fpga_mgr_register(mgr); > + if (ret) { > + dev_err(dev, "unable to register FPGA manager\n"); > + goto err_cfg2; > + } > + > + ret = device_create_file(dev, &dev_attr_cfg_mode); > + if (ret) > + dev_warn(dev, "Can't create cfg_mode interface %d\n", ret); > + > + return 0; > + > +err_cfg2: > + devm_gpiod_put(dev, priv->conf_done); > +err_cfg1: > + devm_gpiod_put(dev, priv->nconfig); > + return ret; > +} > + > +static int fpp_fpga_mgr_remove(struct platform_device *pdev) > +{ > + struct fpga_manager *mgr = platform_get_drvdata(pdev); > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + > + device_remove_file(&pdev->dev, &dev_attr_cfg_mode); > + fpga_mgr_unregister(mgr); > + devm_gpiod_put(&pdev->dev, priv->conf_done); > + devm_gpiod_put(&pdev->dev, priv->nconfig); > + return 0; > +} > + > +static struct platform_driver fpp_fpga_mgr_driver = { > + .driver.name = "ftdi-fifo-fpp-mgr", > + .probe = fpp_fpga_mgr_probe, > + .remove = fpp_fpga_mgr_remove, > +}; > + > +module_platform_driver(fpp_fpga_mgr_driver); > + > +MODULE_ALIAS("platform:ftdi-fifo-fpp-mgr"); > +MODULE_AUTHOR("Anatolij Gustschin "); > +MODULE_DESCRIPTION("FT232H Bitbang/FT245-FIFO FPP FPGA Manager Driver"); > +MODULE_LICENSE("GPL v2"); > -- > 2.17.1 > Thanks, Moritz From mboxrd@z Thu Jan 1 00:00:00 1970 Date: Mon, 8 Apr 2019 11:01:51 -0700 From: Moritz Fischer Subject: Re: [PATCH v4 3/3] fpga: Add fpga manager driver for ARRI Altera FPP Message-ID: <20190408180151.GA5643@archbook> References: <20190221202506.17744-1-agust@denx.de> <20190221202506.17744-4-agust@denx.de> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20190221202506.17744-4-agust@denx.de> To: Anatolij Gustschin Cc: linux-usb@vger.kernel.org, linux-spi@vger.kernel.org, linux-fpga@vger.kernel.org, linux-kernel@vger.kernel.org, gregkh@linuxfoundation.org, broonie@kernel.org, atull@kernel.org, mdf@kernel.org List-ID: Hi Anatolij, looks mostly good, a couple of nits inline. On Thu, Feb 21, 2019 at 09:25:06PM +0100, Anatolij Gustschin wrote: > Add FPGA manager driver for loading ARRI Altera FPGAs via fast > passive parallel (FPP) interface using FTDI FT232H chip. > > Signed-off-by: Anatolij Gustschin > --- > .../ABI/testing/sysfs-driver-ftdi-fifo-fpp | 7 + > drivers/fpga/Kconfig | 7 + > drivers/fpga/Makefile | 1 + > drivers/fpga/ftdi-fifo-fpp.c | 594 ++++++++++++++++++ > 4 files changed, 609 insertions(+) > create mode 100644 Documentation/ABI/testing/sysfs-driver-ftdi-fifo-fpp > create mode 100644 drivers/fpga/ftdi-fifo-fpp.c > > diff --git a/Documentation/ABI/testing/sysfs-driver-ftdi-fifo-fpp b/Documentation/ABI/testing/sysfs-driver-ftdi-fifo-fpp > new file mode 100644 > index 000000000000..f3055100d07e > --- /dev/null > +++ b/Documentation/ABI/testing/sysfs-driver-ftdi-fifo-fpp > @@ -0,0 +1,7 @@ > +What: /sys/bus/platform/devices/ftdi-fifo-fpp-mgr.N/cfg_mode > +Date: Feb 2019 > +Kernel Version: 5.2 > +Contact: Anatolij Gustschin > +Description: > + Contains either "fifo" or "bitbang" and controls if fifo > + of bitbang configuration mode is used in the driver. > diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig > index c20445b867ae..19d82163a0b2 100644 > --- a/drivers/fpga/Kconfig > +++ b/drivers/fpga/Kconfig > @@ -50,6 +50,13 @@ config FPGA_MGR_ALTERA_CVP > FPGA manager driver support for Arria-V, Cyclone-V, Stratix-V > and Arria 10 Altera FPGAs using the CvP interface over PCIe. > > +config FPGA_MGR_FTDI_FIFO_FPP > + tristate "Altera FPP over FT232H FIFO" > + depends on USB_FT232H_INTF > + help > + FPGA manager driver support for Altera fast passive parallel > + interface (FPP) over FT232H FT245 FIFO. > + > config FPGA_MGR_ZYNQ_FPGA > tristate "Xilinx Zynq FPGA" > depends on ARCH_ZYNQ || COMPILE_TEST > diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile > index c0dd4c82fbdb..61725d31e6d1 100644 > --- a/drivers/fpga/Makefile > +++ b/drivers/fpga/Makefile > @@ -9,6 +9,7 @@ obj-$(CONFIG_FPGA) += fpga-mgr.o > # FPGA Manager Drivers > obj-$(CONFIG_FPGA_MGR_ALTERA_CVP) += altera-cvp.o > obj-$(CONFIG_FPGA_MGR_ALTERA_PS_SPI) += altera-ps-spi.o > +obj-$(CONFIG_FPGA_MGR_FTDI_FIFO_FPP) += ftdi-fifo-fpp.o > obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o > obj-$(CONFIG_FPGA_MGR_MACHXO2_SPI) += machxo2-spi.o > obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o > diff --git a/drivers/fpga/ftdi-fifo-fpp.c b/drivers/fpga/ftdi-fifo-fpp.c > new file mode 100644 > index 000000000000..2bc72335cd70 > --- /dev/null > +++ b/drivers/fpga/ftdi-fifo-fpp.c > @@ -0,0 +1,594 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Altera FPGA firmware upload via FPP using FT232H Bitbang/FT245-FIFO. > + * > + * Copyright (C) 2017 - 2018 DENX Software Engineering Maybe bump that to 2019 ;-) > + * Anatolij Gustschin > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define BULK_OUT_BUF_SZ SZ_1M > +#define MAX_RETRIES 10 > + > +/* > + * With logic of CPLD we can write the state of nConfig pin and > + * read back the state of some pins (conf_done, init_done, nStatus). > + * Status header and bit assignment in data register on CPLD. > + */ > +#define INPUT_HEADER_0 0xA5 > +#define INPUT_HEADER_1 0x5A > +#define IN_CONF_DONE BIT(0) > +#define IN_INIT_DONE BIT(1) > +#define IN_ADDR_SELECT BIT(4) > +#define IN_BOARD_REV BIT(5) > +#define OUT_NCONFIG BIT(0) > +#define OUT_RESET_N BIT(1) > + > +enum fpp_board_rev { > + BOARD_REVA = 1, > + BOARD_REVB = 2, > +}; > + > +enum fpp_addr_sel { > + ADDR_SELECT_INVALID, > + ADDR_SELECT_GND, > + ADDR_SELECT_NC > +}; > + > +struct fpp_mgr_ops { > + int (*write_init)(struct fpga_manager *mgr, > + struct fpga_image_info *info, > + const char *buf, size_t count); > + int (*write)(struct fpga_manager *mgr, const char *buf, size_t count); > + int (*write_complete)(struct fpga_manager *mgr, > + struct fpga_image_info *info); > +}; > + > +struct fpp_fpga_mgr_priv { > + struct platform_device *pdev; > + struct usb_interface *intf; > + const struct ft232h_intf_ops *iops; > + struct fpga_manager *mgr; > + struct fpp_mgr_ops *ops; > + struct gpio_desc *nconfig; > + struct gpio_desc *conf_done; > + char cfg_mode[8]; > + u8 out_data_port; > + int index; > + void *bulk_buf; > + char usb_dev_id[32]; > + char fpga_mgr_name[64]; > + enum fpp_board_rev rev; > + enum fpp_addr_sel addr_sel; > +}; > + > +static int fpp_fpga_mgr_set_data_port(struct fpp_fpga_mgr_priv *priv, > + u8 bitmask, u8 value) > +{ > + struct device *dev = &priv->pdev->dev; > + struct bulk_desc desc; > + u8 *data; > + int ret; > + > + /* > + * With CPLD connected (in FT245 FIFO mode) we use ACBUS8&9 > + * pins to switch between data and command mode: > + * ACBUS8&9 == 0, 0 --> normal mode (data communication) > + * ACBUS8&9 == 1, 0 --> command mode > + */ > + gpiod_set_raw_value_cansleep(priv->nconfig, 1); > + gpiod_set_raw_value_cansleep(priv->conf_done, 0); > + msleep(50); You could use a constant here for the 50 ... > + > + /* Write commands to CPLD */ > + ret = priv->iops->set_bitmode(priv->intf, 0x00, BITMODE_SYNCFF); > + if (ret) > + return ret; > + > + if (value) > + priv->out_data_port |= bitmask; > + else > + priv->out_data_port &= ~bitmask; > + > + data = priv->bulk_buf; > + *data = priv->out_data_port; > + > + desc.dir_out = true; > + desc.act_len = 0; > + desc.len = 1; > + desc.data = data; > + desc.timeout = FTDI_USB_WRITE_TIMEOUT; > + > + ret = priv->iops->bulk_xfer(priv->intf, &desc); > + if (ret) { > + dev_err(dev, "Writing in SYNCFF mode failed: %d\n", ret); > + return ret; > + } > + > + msleep(50); and here ... > + /* Switch back to data mode with ACBUS8&9 back to low */ > + gpiod_set_raw_value_cansleep(priv->nconfig, 0); > + gpiod_set_raw_value_cansleep(priv->conf_done, 0); > + msleep(50); and here ... > + > + return 0; > +} > + > +static int fpp_fpga_mgr_bitbang_write_init(struct fpga_manager *mgr, > + struct fpga_image_info *info, > + const char *buf, size_t count) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + struct device *dev = &priv->pdev->dev; > + int retries = MAX_RETRIES; > + int ret; > + > + gpiod_set_value_cansleep(priv->nconfig, 0); > + msleep(50); and here ... > + gpiod_set_value_cansleep(priv->nconfig, 1); > + msleep(50); and here ... > + gpiod_set_value_cansleep(priv->nconfig, 0); > + > + /* Wait for CONF_DONE to get low */ Does the retry number come from datasheet? > + do { > + msleep(50); > + > + ret = gpiod_get_value_cansleep(priv->conf_done); > + if (ret < 0) { > + dev_err(dev, "Failed to get CONF_DONE pin: %d\n", ret); > + return ret; > + } > + > + if (!ret) > + break; > + } while (--retries > 0); > + > + if (!retries) { > + dev_warn(dev, "CONF_DONE low wait timeout\n"); > + return -ETIMEDOUT; > + } > + > + ret = priv->iops->set_bitmode(priv->intf, 0xff, BITMODE_BITBANG); > + if (ret < 0) > + return ret; > + > + /* Set max. working baud rate (for hardware without CPLD) */ > + return priv->iops->set_baudrate(priv->intf, 700000); > +} > + > +static int fpp_fpga_mgr_bitbang_write(struct fpga_manager *mgr, > + const char *buf, size_t count) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + struct bulk_desc desc; > + size_t blk_sz; > + int ret; > + > + desc.data = priv->bulk_buf; > + desc.dir_out = true; > + desc.timeout = FTDI_USB_WRITE_TIMEOUT; > + > + while (count) { > + blk_sz = min_t(size_t, count, BULK_OUT_BUF_SZ); > + memcpy(priv->bulk_buf, buf, blk_sz); > + desc.act_len = 0; > + desc.len = blk_sz; > + ret = priv->iops->bulk_xfer(priv->intf, &desc); > + if (ret < 0) > + return ret; > + > + buf += desc.act_len; > + count -= desc.act_len; > + } > + > + return 0; > +} > + > +static int fpp_fpga_mgr_bitbang_write_complete(struct fpga_manager *mgr, > + struct fpga_image_info *info) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + struct device *dev = &priv->pdev->dev; > + int retries = MAX_RETRIES; > + int ret; > + > + /* Wait for CONF_DONE to get high */ > + do { > + msleep(50); > + > + ret = gpiod_get_value_cansleep(priv->conf_done); > + if (ret < 0) > + return ret; > + > + if (ret) > + break; > + } while (--retries > 0); > + > + if (!retries) { > + dev_warn(dev, "CONF_DONE wait timeout\n"); warn or err? > + return -ETIMEDOUT; > + } > + > + priv->iops->disable_bitbang(priv->intf); > + return 0; > +} > + > +static inline bool status_hdr_is_valid(u8 *buf) > +{ > + return buf[0] == INPUT_HEADER_0 && buf[1] == INPUT_HEADER_1; > +} > + > +static int fpp_fpga_mgr_read_status(struct fpp_fpga_mgr_priv *priv, u8 *status) > +{ > + struct device *dev = &priv->pdev->dev; > + u8 *inbuf = priv->bulk_buf; > + int retries = MAX_RETRIES; > + int ret; > + > + if (!status) > + return -EINVAL; > + > + /* Wait until CPLD sends valid header and status register */ > + do { > + ret = priv->iops->read_data(priv->intf, inbuf, 64); > + if (ret < 0) { > + dev_err(dev, "Can't read status data: %d\n", ret); > + return ret; > + } > + > + /* Check input buffer header */ > + if (ret >= 4 && status_hdr_is_valid(inbuf)) { > + *status = inbuf[2]; > + return 0; > + } > + > + /* Wait and read back status again */ > + msleep(100); /* CPLD sends status every 100ms */ > + } while (--retries > 0); > + > + dev_warn(dev, "Timeout when reading status\n"); > + return -ETIMEDOUT; > +} > + > +static int fpp_fpga_mgr_ft245_fifo_write_init(struct fpga_manager *mgr, > + struct fpga_image_info *info, > + const char *buf, size_t count) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + struct device *dev = &priv->pdev->dev; > + int retries = MAX_RETRIES; > + int ret; > + u8 status; > + > + gpiod_direction_output_raw(priv->conf_done, 0); > + > + /* Set/reset nConfig via commands to CPLD */ > + ret = fpp_fpga_mgr_set_data_port(priv, OUT_NCONFIG, 1); > + if (ret) > + return ret; > + ret = fpp_fpga_mgr_set_data_port(priv, OUT_NCONFIG, 0); > + if (ret) > + return ret; > + ret = fpp_fpga_mgr_set_data_port(priv, OUT_NCONFIG, 1); > + if (ret) > + return ret; > + > + /* In FT245 FIFO mode we need sync FIFO mode to talk to FPGA */ > + ret = priv->iops->set_bitmode(priv->intf, 0xff, BITMODE_SYNCFF); > + if (ret) > + return ret; > + > + /* Wait until FPGA is ready for loading (conf_done zero) or timeout */ > + do { > + ret = fpp_fpga_mgr_read_status(priv, &status); > + if (!ret) { > + /* Check conf_done status */ > + if ((status & IN_CONF_DONE) == 0) > + break; > + } > + } while (--retries > 0); > + > + if (!retries) { > + dev_warn(dev, "CONF_DONE wait timeout\n"); > + return -ETIMEDOUT; > + } > + > + /* Configure for max. baud rate (3MHz * 4 in bitbang mode) */ > + return priv->iops->set_baudrate(priv->intf, 3000000); > +} > + > +static int fpp_fpga_mgr_ft245_fifo_write(struct fpga_manager *mgr, > + const char *buf, size_t count) > +{ > + return fpp_fpga_mgr_bitbang_write(mgr, buf, count); > +} > + > +static int fpp_fpga_mgr_ft245_fifo_write_complete(struct fpga_manager *mgr, > + struct fpga_image_info *info) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + struct device *dev = &priv->pdev->dev; > + int retries = MAX_RETRIES; > + int ret; > + u8 mask, status; Can you swap the two lines, reverse xmas-tree please. > + > + mask = IN_CONF_DONE | IN_INIT_DONE; > + > + do { > + ret = fpp_fpga_mgr_read_status(priv, &status); > + if (!ret) { > + /* Check conf_done/init_done status */ > + if ((status & mask) == mask) > + break; > + } > + } while (--retries > 0); > + > + if (!retries) { > + dev_warn(dev, "INIT_DONE wait timeout\n"); > + return -ETIMEDOUT; > + } > + > + /* Release Reset_n, keep nCONFIG high, too! */ > + return fpp_fpga_mgr_set_data_port(priv, OUT_NCONFIG | OUT_RESET_N, 1); > +} > + > +static enum fpga_mgr_states fpp_fpga_mgr_state(struct fpga_manager *mgr) > +{ > + return FPGA_MGR_STATE_UNKNOWN; > +} > + > +static int fpp_fpga_mgr_write_init(struct fpga_manager *mgr, > + struct fpga_image_info *info, > + const char *buf, size_t count) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + > + if (info && info->flags & FPGA_MGR_PARTIAL_RECONFIG) { > + dev_err(&mgr->dev, "Partial reconfiguration not supported.\n"); > + return -EINVAL; > + } > + > + if (priv->ops->write_init) > + return priv->ops->write_init(mgr, info, buf, count); > + > + return -ENODEV; > +} > + > +static int fpp_fpga_mgr_write(struct fpga_manager *mgr, const char *buf, > + size_t count) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + > + if (priv->ops->write) > + return priv->ops->write(mgr, buf, count); > + > + return -ENODEV; > +} > + > +static int fpp_fpga_mgr_write_complete(struct fpga_manager *mgr, > + struct fpga_image_info *info) > +{ > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + > + if (priv->ops->write_complete) > + return priv->ops->write_complete(mgr, info); > + > + return -ENODEV; > +} > + > +static struct fpp_mgr_ops fpp_mgr_bitbang_ops = { > + .write_init = fpp_fpga_mgr_bitbang_write_init, > + .write = fpp_fpga_mgr_bitbang_write, > + .write_complete = fpp_fpga_mgr_bitbang_write_complete, > +}; > + > +static struct fpp_mgr_ops fpp_mgr_ft245_fifo_ops = { > + .write_init = fpp_fpga_mgr_ft245_fifo_write_init, > + .write = fpp_fpga_mgr_ft245_fifo_write, > + .write_complete = fpp_fpga_mgr_ft245_fifo_write_complete, > +}; > + > +static const struct fpga_manager_ops fpp_fpga_mgr_ops = { > + .state = fpp_fpga_mgr_state, > + .write_init = fpp_fpga_mgr_write_init, > + .write = fpp_fpga_mgr_write, > + .write_complete = fpp_fpga_mgr_write_complete, > +}; > + > +static ssize_t cfg_mode_show(struct device *dev, struct device_attribute *attr, > + char *buf) > +{ > + struct platform_device *pdev = to_platform_device(dev); > + struct fpga_manager *mgr = platform_get_drvdata(pdev); > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + > + return snprintf(buf, PAGE_SIZE, "%s\n", priv->cfg_mode); > +} > + > +static ssize_t cfg_mode_store(struct device *dev, struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct platform_device *pdev = to_platform_device(dev); > + struct fpga_manager *mgr = platform_get_drvdata(pdev); > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + > + if (!count || count > sizeof(priv->cfg_mode)) > + return -EINVAL; > + > + if (!strncmp(buf, "fifo", 4)) { > + strncpy(priv->cfg_mode, buf, sizeof(priv->cfg_mode) - 1); > + priv->cfg_mode[4] = 0; > + priv->ops = &fpp_mgr_ft245_fifo_ops; > + gpiod_direction_output_raw(priv->conf_done, 0); > + } else if (!strncmp(buf, "bitbang", 7)) { > + strncpy(priv->cfg_mode, buf, sizeof(priv->cfg_mode) - 1); > + priv->cfg_mode[7] = 0; > + priv->ops = &fpp_mgr_bitbang_ops; > + gpiod_direction_input(priv->conf_done); > + } else { > + return -EINVAL; > + } > + > + return count; > +} > + > +static DEVICE_ATTR_RW(cfg_mode); > + > +static int fpp_fpga_mgr_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct fpp_fpga_mgr_priv *priv; > + struct fpga_manager *mgr; > + struct fifo_fpp_mgr_platform_data *pd; > + int ret, retries = MAX_RETRIES; > + char id_string[8]; > + u8 status = 0; > + > + pd = dev->platform_data; > + if (!pd) { > + dev_err(dev, "Missing platform data.\n"); > + return -EINVAL; > + } > + > + if (!pd->ops || > + !pd->ops->bulk_xfer || !pd->ops->ctrl_xfer || > + !pd->ops->read_data || !pd->ops->write_data || > + !pd->ops->set_bitmode || !pd->ops->set_baudrate || > + !pd->ops->disable_bitbang) > + return -EINVAL; > + > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->intf = to_usb_interface(dev->parent); > + priv->iops = pd->ops; > + > + ret = sscanf(dev_name(dev->parent), "%s", priv->usb_dev_id); > + if (ret != 1) { > + dev_err(dev, "Can't get parent device name: %d\n", ret); > + return -ENODEV; > + } > + > + priv->pdev = pdev; > + priv->ops = &fpp_mgr_ft245_fifo_ops; > + strncpy(priv->cfg_mode, "fifo", sizeof(priv->cfg_mode)); > + > + priv->nconfig = devm_gpiod_get(dev, "nconfig", GPIOD_OUT_LOW); > + if (IS_ERR(priv->nconfig)) { > + ret = PTR_ERR(priv->nconfig); > + dev_err(dev, "Failed to get nconfig gpio: %d\n", ret); > + return ret; > + } > + > + priv->conf_done = devm_gpiod_get(dev, "conf_done", GPIOD_OUT_LOW); > + if (IS_ERR(priv->conf_done)) { > + ret = PTR_ERR(priv->conf_done); > + dev_err(dev, "Failed to get conf_done gpio: %d\n", ret); > + goto err_cfg1; > + } > + > + priv->bulk_buf = devm_kmalloc(dev, BULK_OUT_BUF_SZ, > + GFP_KERNEL | GFP_DMA32); > + if (!priv->bulk_buf) { > + ret = -ENOMEM; > + goto err_cfg2; > + } > + > + ret = priv->iops->set_bitmode(priv->intf, 0xff, BITMODE_SYNCFF); > + if (ret) > + goto err_cfg2; > + > + /* Read status register from CPLD */ > + do { > + ret = fpp_fpga_mgr_read_status(priv, &status); > + if (!ret) > + break; > + } while (--retries > 0); > + > + if (!retries) { > + ret = -ETIMEDOUT; > + goto err_cfg2; > + } > + > + priv->rev = (status & IN_BOARD_REV) ? BOARD_REVB : BOARD_REVA; > + > + if (priv->rev == BOARD_REVB) { > + priv->addr_sel = (status & IN_ADDR_SELECT) ? > + ADDR_SELECT_NC : ADDR_SELECT_GND; > + if (priv->addr_sel == ADDR_SELECT_NC) > + strncpy(id_string, "right", sizeof(id_string)); > + else > + strncpy(id_string, "left", sizeof(id_string)); > + } else { > + priv->addr_sel = ADDR_SELECT_INVALID; > + strncpy(id_string, "single", sizeof(id_string)); > + } > + > + dev_info(dev, "Board Rev %d, Addr Sel %d\n", priv->rev, priv->addr_sel); > + > + /* Use unique board ID and USB bus/port in FPGA manager name */ > + snprintf(priv->fpga_mgr_name, sizeof(priv->fpga_mgr_name), > + "ftdi-fpp-fpga-mgr %s %s", id_string, priv->usb_dev_id); > + > + mgr = devm_fpga_mgr_create(dev, priv->fpga_mgr_name, > + &fpp_fpga_mgr_ops, priv); > + if (!mgr) > + goto err_cfg2; > + > + platform_set_drvdata(pdev, mgr); > + > + ret = fpga_mgr_register(mgr); > + if (ret) { > + dev_err(dev, "unable to register FPGA manager\n"); > + goto err_cfg2; > + } > + > + ret = device_create_file(dev, &dev_attr_cfg_mode); > + if (ret) > + dev_warn(dev, "Can't create cfg_mode interface %d\n", ret); > + > + return 0; > + > +err_cfg2: > + devm_gpiod_put(dev, priv->conf_done); > +err_cfg1: > + devm_gpiod_put(dev, priv->nconfig); > + return ret; > +} > + > +static int fpp_fpga_mgr_remove(struct platform_device *pdev) > +{ > + struct fpga_manager *mgr = platform_get_drvdata(pdev); > + struct fpp_fpga_mgr_priv *priv = mgr->priv; > + > + device_remove_file(&pdev->dev, &dev_attr_cfg_mode); > + fpga_mgr_unregister(mgr); > + devm_gpiod_put(&pdev->dev, priv->conf_done); > + devm_gpiod_put(&pdev->dev, priv->nconfig); > + return 0; > +} > + > +static struct platform_driver fpp_fpga_mgr_driver = { > + .driver.name = "ftdi-fifo-fpp-mgr", > + .probe = fpp_fpga_mgr_probe, > + .remove = fpp_fpga_mgr_remove, > +}; > + > +module_platform_driver(fpp_fpga_mgr_driver); > + > +MODULE_ALIAS("platform:ftdi-fifo-fpp-mgr"); > +MODULE_AUTHOR("Anatolij Gustschin "); > +MODULE_DESCRIPTION("FT232H Bitbang/FT245-FIFO FPP FPGA Manager Driver"); > +MODULE_LICENSE("GPL v2"); > -- > 2.17.1 > Thanks, Moritz From mboxrd@z Thu Jan 1 00:00:00 1970 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Subject: [v4,3/3] fpga: Add fpga manager driver for ARRI Altera FPP From: mdf@kernel.org Message-Id: <20190408180151.GA5643@archbook> Date: Mon, 8 Apr 2019 11:05:49 -0700 To: Anatolij Gustschin Cc: linux-usb@vger.kernel.org, linux-spi@vger.kernel.org, linux-fpga@vger.kernel.org, linux-kernel@vger.kernel.org, gregkh@linuxfoundation.org, broonie@kernel.org, atull@kernel.org, mdf@kernel.org List-ID: SGkgQW5hdG9saWosCgpsb29rcyBtb3N0bHkgZ29vZCwgYSBjb3VwbGUgb2Ygbml0cyBpbmxpbmUu CgpPbiBUaHUsIEZlYiAyMSwgMjAxOSBhdCAwOToyNTowNlBNICswMTAwLCBBbmF0b2xpaiBHdXN0 c2NoaW4gd3JvdGU6Cj4gQWRkIEZQR0EgbWFuYWdlciBkcml2ZXIgZm9yIGxvYWRpbmcgQVJSSSBB bHRlcmEgRlBHQXMgdmlhIGZhc3QKPiBwYXNzaXZlIHBhcmFsbGVsIChGUFApIGludGVyZmFjZSB1 c2luZyBGVERJIEZUMjMySCBjaGlwLgo+IAo+IFNpZ25lZC1vZmYtYnk6IEFuYXRvbGlqIEd1c3Rz Y2hpbiA8YWd1c3RAZGVueC5kZT4KPiAtLS0KPiAgLi4uL0FCSS90ZXN0aW5nL3N5c2ZzLWRyaXZl ci1mdGRpLWZpZm8tZnBwICAgIHwgICA3ICsKPiAgZHJpdmVycy9mcGdhL0tjb25maWcgICAgICAg ICAgICAgICAgICAgICAgICAgIHwgICA3ICsKPiAgZHJpdmVycy9mcGdhL01ha2VmaWxlICAgICAg ICAgICAgICAgICAgICAgICAgIHwgICAxICsKPiAgZHJpdmVycy9mcGdhL2Z0ZGktZmlmby1mcHAu YyAgICAgICAgICAgICAgICAgIHwgNTk0ICsrKysrKysrKysrKysrKysrKwo+ICA0IGZpbGVzIGNo YW5nZWQsIDYwOSBpbnNlcnRpb25zKCspCj4gIGNyZWF0ZSBtb2RlIDEwMDY0NCBEb2N1bWVudGF0 aW9uL0FCSS90ZXN0aW5nL3N5c2ZzLWRyaXZlci1mdGRpLWZpZm8tZnBwCj4gIGNyZWF0ZSBtb2Rl IDEwMDY0NCBkcml2ZXJzL2ZwZ2EvZnRkaS1maWZvLWZwcC5jCj4gCj4gZGlmZiAtLWdpdCBhL0Rv Y3VtZW50YXRpb24vQUJJL3Rlc3Rpbmcvc3lzZnMtZHJpdmVyLWZ0ZGktZmlmby1mcHAgYi9Eb2N1 bWVudGF0aW9uL0FCSS90ZXN0aW5nL3N5c2ZzLWRyaXZlci1mdGRpLWZpZm8tZnBwCj4gbmV3IGZp bGUgbW9kZSAxMDA2NDQKPiBpbmRleCAwMDAwMDAwMDAwMDAuLmYzMDU1MTAwZDA3ZQo+IC0tLSAv ZGV2L251bGwKPiArKysgYi9Eb2N1bWVudGF0aW9uL0FCSS90ZXN0aW5nL3N5c2ZzLWRyaXZlci1m dGRpLWZpZm8tZnBwCj4gQEAgLTAsMCArMSw3IEBACj4gK1doYXQ6CQkvc3lzL2J1cy9wbGF0Zm9y bS9kZXZpY2VzL2Z0ZGktZmlmby1mcHAtbWdyLk4vY2ZnX21vZGUKPiArRGF0ZToJCUZlYiAyMDE5 Cj4gK0tlcm5lbCBWZXJzaW9uOgk1LjIKPiArQ29udGFjdDoJQW5hdG9saWogR3VzdHNjaGluIDxh Z3VzdEBkZW54LmRlPgo+ICtEZXNjcmlwdGlvbjoKPiArCQlDb250YWlucyBlaXRoZXIgImZpZm8i IG9yICJiaXRiYW5nIiBhbmQgY29udHJvbHMgaWYgZmlmbwo+ICsJCW9mIGJpdGJhbmcgY29uZmln dXJhdGlvbiBtb2RlIGlzIHVzZWQgaW4gdGhlIGRyaXZlci4KPiBkaWZmIC0tZ2l0IGEvZHJpdmVy cy9mcGdhL0tjb25maWcgYi9kcml2ZXJzL2ZwZ2EvS2NvbmZpZwo+IGluZGV4IGMyMDQ0NWI4Njdh ZS4uMTlkODIxNjNhMGIyIDEwMDY0NAo+IC0tLSBhL2RyaXZlcnMvZnBnYS9LY29uZmlnCj4gKysr IGIvZHJpdmVycy9mcGdhL0tjb25maWcKPiBAQCAtNTAsNiArNTAsMTMgQEAgY29uZmlnIEZQR0Ff TUdSX0FMVEVSQV9DVlAKPiAgCSAgRlBHQSBtYW5hZ2VyIGRyaXZlciBzdXBwb3J0IGZvciBBcnJp YS1WLCBDeWNsb25lLVYsIFN0cmF0aXgtVgo+ICAJICBhbmQgQXJyaWEgMTAgQWx0ZXJhIEZQR0Fz IHVzaW5nIHRoZSBDdlAgaW50ZXJmYWNlIG92ZXIgUENJZS4KPiAgCj4gK2NvbmZpZyBGUEdBX01H Ul9GVERJX0ZJRk9fRlBQCj4gKwl0cmlzdGF0ZSAiQWx0ZXJhIEZQUCBvdmVyIEZUMjMySCBGSUZP Igo+ICsJZGVwZW5kcyBvbiBVU0JfRlQyMzJIX0lOVEYKPiArCWhlbHAKPiArCSAgRlBHQSBtYW5h Z2VyIGRyaXZlciBzdXBwb3J0IGZvciBBbHRlcmEgZmFzdCBwYXNzaXZlIHBhcmFsbGVsCj4gKwkg IGludGVyZmFjZSAoRlBQKSBvdmVyIEZUMjMySCBGVDI0NSBGSUZPLgo+ICsKPiAgY29uZmlnIEZQ R0FfTUdSX1pZTlFfRlBHQQo+ICAJdHJpc3RhdGUgIlhpbGlueCBaeW5xIEZQR0EiCj4gIAlkZXBl bmRzIG9uIEFSQ0hfWllOUSB8fCBDT01QSUxFX1RFU1QKPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9m cGdhL01ha2VmaWxlIGIvZHJpdmVycy9mcGdhL01ha2VmaWxlCj4gaW5kZXggYzBkZDRjODJmYmRi Li42MTcyNWQzMWU2ZDEgMTAwNjQ0Cj4gLS0tIGEvZHJpdmVycy9mcGdhL01ha2VmaWxlCj4gKysr IGIvZHJpdmVycy9mcGdhL01ha2VmaWxlCj4gQEAgLTksNiArOSw3IEBAIG9iai0kKENPTkZJR19G UEdBKQkJCSs9IGZwZ2EtbWdyLm8KPiAgIyBGUEdBIE1hbmFnZXIgRHJpdmVycwo+ICBvYmotJChD T05GSUdfRlBHQV9NR1JfQUxURVJBX0NWUCkJKz0gYWx0ZXJhLWN2cC5vCj4gIG9iai0kKENPTkZJ R19GUEdBX01HUl9BTFRFUkFfUFNfU1BJKQkrPSBhbHRlcmEtcHMtc3BpLm8KPiArb2JqLSQoQ09O RklHX0ZQR0FfTUdSX0ZURElfRklGT19GUFApCSs9IGZ0ZGktZmlmby1mcHAubwo+ICBvYmotJChD T05GSUdfRlBHQV9NR1JfSUNFNDBfU1BJKQkrPSBpY2U0MC1zcGkubwo+ICBvYmotJChDT05GSUdf RlBHQV9NR1JfTUFDSFhPMl9TUEkpCSs9IG1hY2h4bzItc3BpLm8KPiAgb2JqLSQoQ09ORklHX0ZQ R0FfTUdSX1NPQ0ZQR0EpCQkrPSBzb2NmcGdhLm8KPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9mcGdh L2Z0ZGktZmlmby1mcHAuYyBiL2RyaXZlcnMvZnBnYS9mdGRpLWZpZm8tZnBwLmMKPiBuZXcgZmls ZSBtb2RlIDEwMDY0NAo+IGluZGV4IDAwMDAwMDAwMDAwMC4uMmJjNzIzMzVjZDcwCj4gLS0tIC9k ZXYvbnVsbAo+ICsrKyBiL2RyaXZlcnMvZnBnYS9mdGRpLWZpZm8tZnBwLmMKPiBAQCAtMCwwICsx LDU5NCBAQAo+ICsvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogR1BMLTIuMAo+ICsvKgo+ICsg KiBBbHRlcmEgRlBHQSBmaXJtd2FyZSB1cGxvYWQgdmlhIEZQUCB1c2luZyBGVDIzMkggQml0YmFu Zy9GVDI0NS1GSUZPLgo+ICsgKgo+ICsgKiBDb3B5cmlnaHQgKEMpIDIwMTcgLSAyMDE4IERFTlgg U29mdHdhcmUgRW5naW5lZXJpbmcKTWF5YmUgYnVtcCB0aGF0IHRvIDIwMTkgOy0pCgo+ICsgKiBB bmF0b2xpaiBHdXN0c2NoaW4gPGFndXN0QGRlbnguZGU+Cj4gKyAqLwo+ICsKPiArI2luY2x1ZGUg PGxpbnV4L2JpdG9wcy5oPgo+ICsjaW5jbHVkZSA8bGludXgvZGVsYXkuaD4KPiArI2luY2x1ZGUg PGxpbnV4L2ZwZ2EvZnBnYS1tZ3IuaD4KPiArI2luY2x1ZGUgPGxpbnV4L21vZHVsZS5oPgo+ICsj aW5jbHVkZSA8bGludXgva2VybmVsLmg+Cj4gKyNpbmNsdWRlIDxsaW51eC9zaXplcy5oPgo+ICsj aW5jbHVkZSA8bGludXgvc2xhYi5oPgo+ICsjaW5jbHVkZSA8bGludXgvZ3Bpby9jb25zdW1lci5o Pgo+ICsjaW5jbHVkZSA8bGludXgvcGxhdGZvcm1fZGV2aWNlLmg+Cj4gKyNpbmNsdWRlIDxsaW51 eC91c2IuaD4KPiArI2luY2x1ZGUgPGxpbnV4L3VzYi9mdDIzMmgtaW50Zi5oPgo+ICsKPiArI2Rl ZmluZSBCVUxLX09VVF9CVUZfU1oJU1pfMU0KPiArI2RlZmluZSBNQVhfUkVUUklFUwkxMAo+ICsK PiArLyoKPiArICogV2l0aCBsb2dpYyBvZiBDUExEIHdlIGNhbiB3cml0ZSB0aGUgc3RhdGUgb2Yg bkNvbmZpZyBwaW4gYW5kCj4gKyAqIHJlYWQgYmFjayB0aGUgc3RhdGUgb2Ygc29tZSBwaW5zIChj b25mX2RvbmUsIGluaXRfZG9uZSwgblN0YXR1cykuCj4gKyAqIFN0YXR1cyBoZWFkZXIgYW5kIGJp dCBhc3NpZ25tZW50IGluIGRhdGEgcmVnaXN0ZXIgb24gQ1BMRC4KPiArICovCj4gKyNkZWZpbmUg SU5QVVRfSEVBREVSXzAJMHhBNQo+ICsjZGVmaW5lIElOUFVUX0hFQURFUl8xCTB4NUEKPiArI2Rl ZmluZSBJTl9DT05GX0RPTkUJQklUKDApCj4gKyNkZWZpbmUgSU5fSU5JVF9ET05FCUJJVCgxKQo+ ICsjZGVmaW5lIElOX0FERFJfU0VMRUNUCUJJVCg0KQo+ICsjZGVmaW5lIElOX0JPQVJEX1JFVglC SVQoNSkKPiArI2RlZmluZSBPVVRfTkNPTkZJRwlCSVQoMCkKPiArI2RlZmluZSBPVVRfUkVTRVRf TglCSVQoMSkKPiArCj4gK2VudW0gZnBwX2JvYXJkX3JldiB7Cj4gKwlCT0FSRF9SRVZBID0gMSwK PiArCUJPQVJEX1JFVkIgPSAyLAo+ICt9Owo+ICsKPiArZW51bSBmcHBfYWRkcl9zZWwgewo+ICsJ QUREUl9TRUxFQ1RfSU5WQUxJRCwKPiArCUFERFJfU0VMRUNUX0dORCwKPiArCUFERFJfU0VMRUNU X05DCj4gK307Cj4gKwo+ICtzdHJ1Y3QgZnBwX21ncl9vcHMgewo+ICsJaW50ICgqd3JpdGVfaW5p dCkoc3RydWN0IGZwZ2FfbWFuYWdlciAqbWdyLAo+ICsJCQkgIHN0cnVjdCBmcGdhX2ltYWdlX2lu Zm8gKmluZm8sCj4gKwkJCSAgY29uc3QgY2hhciAqYnVmLCBzaXplX3QgY291bnQpOwo+ICsJaW50 ICgqd3JpdGUpKHN0cnVjdCBmcGdhX21hbmFnZXIgKm1nciwgY29uc3QgY2hhciAqYnVmLCBzaXpl X3QgY291bnQpOwo+ICsJaW50ICgqd3JpdGVfY29tcGxldGUpKHN0cnVjdCBmcGdhX21hbmFnZXIg Km1nciwKPiArCQkJICAgICAgc3RydWN0IGZwZ2FfaW1hZ2VfaW5mbyAqaW5mbyk7Cj4gK307Cj4g Kwo+ICtzdHJ1Y3QgZnBwX2ZwZ2FfbWdyX3ByaXYgewo+ICsJc3RydWN0IHBsYXRmb3JtX2Rldmlj ZQkJKnBkZXY7Cj4gKwlzdHJ1Y3QgdXNiX2ludGVyZmFjZQkJKmludGY7Cj4gKwljb25zdCBzdHJ1 Y3QgZnQyMzJoX2ludGZfb3BzCSppb3BzOwo+ICsJc3RydWN0IGZwZ2FfbWFuYWdlcgkqbWdyOwo+ ICsJc3RydWN0IGZwcF9tZ3Jfb3BzCSpvcHM7Cj4gKwlzdHJ1Y3QgZ3Bpb19kZXNjCSpuY29uZmln Owo+ICsJc3RydWN0IGdwaW9fZGVzYwkqY29uZl9kb25lOwo+ICsJY2hhcgkJCWNmZ19tb2RlWzhd Owo+ICsJdTgJCQlvdXRfZGF0YV9wb3J0Owo+ICsJaW50CQkJaW5kZXg7Cj4gKwl2b2lkCQkJKmJ1 bGtfYnVmOwo+ICsJY2hhcgkJCXVzYl9kZXZfaWRbMzJdOwo+ICsJY2hhcgkJCWZwZ2FfbWdyX25h bWVbNjRdOwo+ICsJZW51bSBmcHBfYm9hcmRfcmV2CXJldjsKPiArCWVudW0gZnBwX2FkZHJfc2Vs CWFkZHJfc2VsOwo+ICt9Owo+ICsKPiArc3RhdGljIGludCBmcHBfZnBnYV9tZ3Jfc2V0X2RhdGFf cG9ydChzdHJ1Y3QgZnBwX2ZwZ2FfbWdyX3ByaXYgKnByaXYsCj4gKwkJCQkgICAgICB1OCBiaXRt YXNrLCB1OCB2YWx1ZSkKPiArewo+ICsJc3RydWN0IGRldmljZSAqZGV2ID0gJnByaXYtPnBkZXYt PmRldjsKPiArCXN0cnVjdCBidWxrX2Rlc2MgZGVzYzsKPiArCXU4ICpkYXRhOwo+ICsJaW50IHJl dDsKPiArCj4gKwkvKgo+ICsJICogV2l0aCBDUExEIGNvbm5lY3RlZCAoaW4gRlQyNDUgRklGTyBt b2RlKSB3ZSB1c2UgQUNCVVM4JjkKPiArCSAqIHBpbnMgdG8gc3dpdGNoIGJldHdlZW4gZGF0YSBh bmQgY29tbWFuZCBtb2RlOgo+ICsJICogQUNCVVM4JjkgPT0gMCwgMCAgLS0+IG5vcm1hbCBtb2Rl IChkYXRhIGNvbW11bmljYXRpb24pCj4gKwkgKiBBQ0JVUzgmOSA9PSAxLCAwICAtLT4gY29tbWFu ZCBtb2RlCj4gKwkgKi8KPiArCWdwaW9kX3NldF9yYXdfdmFsdWVfY2Fuc2xlZXAocHJpdi0+bmNv bmZpZywgMSk7Cj4gKwlncGlvZF9zZXRfcmF3X3ZhbHVlX2NhbnNsZWVwKHByaXYtPmNvbmZfZG9u ZSwgMCk7Cj4gKwltc2xlZXAoNTApOwoKWW91IGNvdWxkIHVzZSBhIGNvbnN0YW50IGhlcmUgZm9y IHRoZSA1MCAuLi4KPiArCj4gKwkvKiBXcml0ZSBjb21tYW5kcyB0byBDUExEICovCj4gKwlyZXQg PSBwcml2LT5pb3BzLT5zZXRfYml0bW9kZShwcml2LT5pbnRmLCAweDAwLCBCSVRNT0RFX1NZTkNG Rik7Cj4gKwlpZiAocmV0KQo+ICsJCXJldHVybiByZXQ7Cj4gKwo+ICsJaWYgKHZhbHVlKQo+ICsJ CXByaXYtPm91dF9kYXRhX3BvcnQgfD0gYml0bWFzazsKPiArCWVsc2UKPiArCQlwcml2LT5vdXRf ZGF0YV9wb3J0ICY9IH5iaXRtYXNrOwo+ICsKPiArCWRhdGEgPSBwcml2LT5idWxrX2J1ZjsKPiAr CSpkYXRhID0gcHJpdi0+b3V0X2RhdGFfcG9ydDsKPiArCj4gKwlkZXNjLmRpcl9vdXQgPSB0cnVl Owo+ICsJZGVzYy5hY3RfbGVuID0gMDsKPiArCWRlc2MubGVuID0gMTsKPiArCWRlc2MuZGF0YSA9 IGRhdGE7Cj4gKwlkZXNjLnRpbWVvdXQgPSBGVERJX1VTQl9XUklURV9USU1FT1VUOwo+ICsKPiAr CXJldCA9IHByaXYtPmlvcHMtPmJ1bGtfeGZlcihwcml2LT5pbnRmLCAmZGVzYyk7Cj4gKwlpZiAo cmV0KSB7Cj4gKwkJZGV2X2VycihkZXYsICJXcml0aW5nIGluIFNZTkNGRiBtb2RlIGZhaWxlZDog JWRcbiIsIHJldCk7Cj4gKwkJcmV0dXJuIHJldDsKPiArCX0KPiArCj4gKwltc2xlZXAoNTApOwoK YW5kIGhlcmUgLi4uCj4gKwkvKiBTd2l0Y2ggYmFjayB0byBkYXRhIG1vZGUgd2l0aCBBQ0JVUzgm OSBiYWNrIHRvIGxvdyAqLwo+ICsJZ3Bpb2Rfc2V0X3Jhd192YWx1ZV9jYW5zbGVlcChwcml2LT5u Y29uZmlnLCAwKTsKPiArCWdwaW9kX3NldF9yYXdfdmFsdWVfY2Fuc2xlZXAocHJpdi0+Y29uZl9k b25lLCAwKTsKPiArCW1zbGVlcCg1MCk7CmFuZCBoZXJlIC4uLgo+ICsKPiArCXJldHVybiAwOwo+ ICt9Cj4gKwo+ICtzdGF0aWMgaW50IGZwcF9mcGdhX21ncl9iaXRiYW5nX3dyaXRlX2luaXQoc3Ry dWN0IGZwZ2FfbWFuYWdlciAqbWdyLAo+ICsJCQkJCSAgIHN0cnVjdCBmcGdhX2ltYWdlX2luZm8g KmluZm8sCj4gKwkJCQkJICAgY29uc3QgY2hhciAqYnVmLCBzaXplX3QgY291bnQpCj4gK3sKPiAr CXN0cnVjdCBmcHBfZnBnYV9tZ3JfcHJpdiAqcHJpdiA9IG1nci0+cHJpdjsKPiArCXN0cnVjdCBk ZXZpY2UgKmRldiA9ICZwcml2LT5wZGV2LT5kZXY7Cj4gKwlpbnQgcmV0cmllcyA9IE1BWF9SRVRS SUVTOwo+ICsJaW50IHJldDsKPiArCj4gKwlncGlvZF9zZXRfdmFsdWVfY2Fuc2xlZXAocHJpdi0+ bmNvbmZpZywgMCk7Cj4gKwltc2xlZXAoNTApOwoKYW5kIGhlcmUgLi4uCj4gKwlncGlvZF9zZXRf dmFsdWVfY2Fuc2xlZXAocHJpdi0+bmNvbmZpZywgMSk7Cj4gKwltc2xlZXAoNTApOwphbmQgaGVy ZSAuLi4KPiArCWdwaW9kX3NldF92YWx1ZV9jYW5zbGVlcChwcml2LT5uY29uZmlnLCAwKTsKPiAr Cj4gKwkvKiBXYWl0IGZvciBDT05GX0RPTkUgdG8gZ2V0IGxvdyAqLwpEb2VzIHRoZSByZXRyeSBu dW1iZXIgY29tZSBmcm9tIGRhdGFzaGVldD8KPiArCWRvIHsKPiArCQltc2xlZXAoNTApOwo+ICsK PiArCQlyZXQgPSBncGlvZF9nZXRfdmFsdWVfY2Fuc2xlZXAocHJpdi0+Y29uZl9kb25lKTsKPiAr CQlpZiAocmV0IDwgMCkgewo+ICsJCQlkZXZfZXJyKGRldiwgIkZhaWxlZCB0byBnZXQgQ09ORl9E T05FIHBpbjogJWRcbiIsIHJldCk7Cj4gKwkJCXJldHVybiByZXQ7Cj4gKwkJfQo+ICsKPiArCQlp ZiAoIXJldCkKPiArCQkJYnJlYWs7Cj4gKwl9IHdoaWxlICgtLXJldHJpZXMgPiAwKTsKPiArCj4g KwlpZiAoIXJldHJpZXMpIHsKPiArCQlkZXZfd2FybihkZXYsICJDT05GX0RPTkUgbG93IHdhaXQg dGltZW91dFxuIik7Cj4gKwkJcmV0dXJuIC1FVElNRURPVVQ7Cj4gKwl9Cj4gKwo+ICsJcmV0ID0g cHJpdi0+aW9wcy0+c2V0X2JpdG1vZGUocHJpdi0+aW50ZiwgMHhmZiwgQklUTU9ERV9CSVRCQU5H KTsKPiArCWlmIChyZXQgPCAwKQo+ICsJCXJldHVybiByZXQ7Cj4gKwo+ICsJLyogU2V0IG1heC4g d29ya2luZyBiYXVkIHJhdGUgKGZvciBoYXJkd2FyZSB3aXRob3V0IENQTEQpICovCj4gKwlyZXR1 cm4gcHJpdi0+aW9wcy0+c2V0X2JhdWRyYXRlKHByaXYtPmludGYsIDcwMDAwMCk7Cj4gK30KPiAr Cj4gK3N0YXRpYyBpbnQgZnBwX2ZwZ2FfbWdyX2JpdGJhbmdfd3JpdGUoc3RydWN0IGZwZ2FfbWFu YWdlciAqbWdyLAo+ICsJCQkJICAgICAgY29uc3QgY2hhciAqYnVmLCBzaXplX3QgY291bnQpCj4g K3sKPiArCXN0cnVjdCBmcHBfZnBnYV9tZ3JfcHJpdiAqcHJpdiA9IG1nci0+cHJpdjsKPiArCXN0 cnVjdCBidWxrX2Rlc2MgZGVzYzsKPiArCXNpemVfdCBibGtfc3o7Cj4gKwlpbnQgcmV0Owo+ICsK PiArCWRlc2MuZGF0YSA9IHByaXYtPmJ1bGtfYnVmOwo+ICsJZGVzYy5kaXJfb3V0ID0gdHJ1ZTsK PiArCWRlc2MudGltZW91dCA9IEZURElfVVNCX1dSSVRFX1RJTUVPVVQ7Cj4gKwo+ICsJd2hpbGUg KGNvdW50KSB7Cj4gKwkJYmxrX3N6ID0gbWluX3Qoc2l6ZV90LCBjb3VudCwgQlVMS19PVVRfQlVG X1NaKTsKPiArCQltZW1jcHkocHJpdi0+YnVsa19idWYsIGJ1ZiwgYmxrX3N6KTsKPiArCQlkZXNj LmFjdF9sZW4gPSAwOwo+ICsJCWRlc2MubGVuID0gYmxrX3N6Owo+ICsJCXJldCA9IHByaXYtPmlv cHMtPmJ1bGtfeGZlcihwcml2LT5pbnRmLCAmZGVzYyk7Cj4gKwkJaWYgKHJldCA8IDApCj4gKwkJ CXJldHVybiByZXQ7Cj4gKwo+ICsJCWJ1ZiArPSBkZXNjLmFjdF9sZW47Cj4gKwkJY291bnQgLT0g ZGVzYy5hY3RfbGVuOwo+ICsJfQo+ICsKPiArCXJldHVybiAwOwo+ICt9Cj4gKwo+ICtzdGF0aWMg aW50IGZwcF9mcGdhX21ncl9iaXRiYW5nX3dyaXRlX2NvbXBsZXRlKHN0cnVjdCBmcGdhX21hbmFn ZXIgKm1nciwKPiArCQkJCQkgICAgICAgc3RydWN0IGZwZ2FfaW1hZ2VfaW5mbyAqaW5mbykKPiAr ewo+ICsJc3RydWN0IGZwcF9mcGdhX21ncl9wcml2ICpwcml2ID0gbWdyLT5wcml2Owo+ICsJc3Ry dWN0IGRldmljZSAqZGV2ID0gJnByaXYtPnBkZXYtPmRldjsKPiArCWludCByZXRyaWVzID0gTUFY X1JFVFJJRVM7Cj4gKwlpbnQgcmV0Owo+ICsKPiArCS8qIFdhaXQgZm9yIENPTkZfRE9ORSB0byBn ZXQgaGlnaCAqLwo+ICsJZG8gewo+ICsJCW1zbGVlcCg1MCk7Cj4gKwo+ICsJCXJldCA9IGdwaW9k X2dldF92YWx1ZV9jYW5zbGVlcChwcml2LT5jb25mX2RvbmUpOwo+ICsJCWlmIChyZXQgPCAwKQo+ ICsJCQlyZXR1cm4gcmV0Owo+ICsKPiArCQlpZiAocmV0KQo+ICsJCQlicmVhazsKPiArCX0gd2hp bGUgKC0tcmV0cmllcyA+IDApOwo+ICsKPiArCWlmICghcmV0cmllcykgewo+ICsJCWRldl93YXJu KGRldiwgIkNPTkZfRE9ORSB3YWl0IHRpbWVvdXRcbiIpOwoKd2FybiBvciBlcnI/Cgo+ICsJCXJl dHVybiAtRVRJTUVET1VUOwo+ICsJfQo+ICsKPiArCXByaXYtPmlvcHMtPmRpc2FibGVfYml0YmFu Zyhwcml2LT5pbnRmKTsKPiArCXJldHVybiAwOwo+ICt9Cj4gKwo+ICtzdGF0aWMgaW5saW5lIGJv b2wgc3RhdHVzX2hkcl9pc192YWxpZCh1OCAqYnVmKQo+ICt7Cj4gKwlyZXR1cm4gYnVmWzBdID09 IElOUFVUX0hFQURFUl8wICYmIGJ1ZlsxXSA9PSBJTlBVVF9IRUFERVJfMTsKPiArfQo+ICsKPiAr c3RhdGljIGludCBmcHBfZnBnYV9tZ3JfcmVhZF9zdGF0dXMoc3RydWN0IGZwcF9mcGdhX21ncl9w cml2ICpwcml2LCB1OCAqc3RhdHVzKQo+ICt7Cj4gKwlzdHJ1Y3QgZGV2aWNlICpkZXYgPSAmcHJp di0+cGRldi0+ZGV2Owo+ICsJdTggKmluYnVmID0gcHJpdi0+YnVsa19idWY7Cj4gKwlpbnQgcmV0 cmllcyA9IE1BWF9SRVRSSUVTOwo+ICsJaW50IHJldDsKPiArCj4gKwlpZiAoIXN0YXR1cykKPiAr CQlyZXR1cm4gLUVJTlZBTDsKPiArCj4gKwkvKiBXYWl0IHVudGlsIENQTEQgc2VuZHMgdmFsaWQg aGVhZGVyIGFuZCBzdGF0dXMgcmVnaXN0ZXIgKi8KPiArCWRvIHsKPiArCQlyZXQgPSBwcml2LT5p b3BzLT5yZWFkX2RhdGEocHJpdi0+aW50ZiwgaW5idWYsIDY0KTsKPiArCQlpZiAocmV0IDwgMCkg ewo+ICsJCQlkZXZfZXJyKGRldiwgIkNhbid0IHJlYWQgc3RhdHVzIGRhdGE6ICVkXG4iLCByZXQp Owo+ICsJCQlyZXR1cm4gcmV0Owo+ICsJCX0KPiArCj4gKwkJLyogQ2hlY2sgaW5wdXQgYnVmZmVy IGhlYWRlciAqLwo+ICsJCWlmIChyZXQgPj0gNCAmJiBzdGF0dXNfaGRyX2lzX3ZhbGlkKGluYnVm KSkgewo+ICsJCQkqc3RhdHVzID0gaW5idWZbMl07Cj4gKwkJCXJldHVybiAwOwo+ICsJCX0KPiAr Cj4gKwkJLyogV2FpdCBhbmQgcmVhZCBiYWNrIHN0YXR1cyBhZ2FpbiAqLwo+ICsJCW1zbGVlcCgx MDApOyAvKiBDUExEIHNlbmRzIHN0YXR1cyBldmVyeSAxMDBtcyAqLwo+ICsJfSB3aGlsZSAoLS1y ZXRyaWVzID4gMCk7Cj4gKwo+ICsJZGV2X3dhcm4oZGV2LCAiVGltZW91dCB3aGVuIHJlYWRpbmcg c3RhdHVzXG4iKTsKPiArCXJldHVybiAtRVRJTUVET1VUOwo+ICt9Cj4gKwo+ICtzdGF0aWMgaW50 IGZwcF9mcGdhX21ncl9mdDI0NV9maWZvX3dyaXRlX2luaXQoc3RydWN0IGZwZ2FfbWFuYWdlciAq bWdyLAo+ICsJCQkJCSAgICAgIHN0cnVjdCBmcGdhX2ltYWdlX2luZm8gKmluZm8sCj4gKwkJCQkJ ICAgICAgY29uc3QgY2hhciAqYnVmLCBzaXplX3QgY291bnQpCj4gK3sKPiArCXN0cnVjdCBmcHBf ZnBnYV9tZ3JfcHJpdiAqcHJpdiA9IG1nci0+cHJpdjsKPiArCXN0cnVjdCBkZXZpY2UgKmRldiA9 ICZwcml2LT5wZGV2LT5kZXY7Cj4gKwlpbnQgcmV0cmllcyA9IE1BWF9SRVRSSUVTOwo+ICsJaW50 IHJldDsKPiArCXU4IHN0YXR1czsKPiArCj4gKwlncGlvZF9kaXJlY3Rpb25fb3V0cHV0X3Jhdyhw cml2LT5jb25mX2RvbmUsIDApOwo+ICsKPiArCS8qIFNldC9yZXNldCBuQ29uZmlnIHZpYSBjb21t YW5kcyB0byBDUExEICovCj4gKwlyZXQgPSBmcHBfZnBnYV9tZ3Jfc2V0X2RhdGFfcG9ydChwcml2 LCBPVVRfTkNPTkZJRywgMSk7Cj4gKwlpZiAocmV0KQo+ICsJCXJldHVybiByZXQ7Cj4gKwlyZXQg PSBmcHBfZnBnYV9tZ3Jfc2V0X2RhdGFfcG9ydChwcml2LCBPVVRfTkNPTkZJRywgMCk7Cj4gKwlp ZiAocmV0KQo+ICsJCXJldHVybiByZXQ7Cj4gKwlyZXQgPSBmcHBfZnBnYV9tZ3Jfc2V0X2RhdGFf cG9ydChwcml2LCBPVVRfTkNPTkZJRywgMSk7Cj4gKwlpZiAocmV0KQo+ICsJCXJldHVybiByZXQ7 Cj4gKwo+ICsJLyogSW4gRlQyNDUgRklGTyBtb2RlIHdlIG5lZWQgc3luYyBGSUZPIG1vZGUgdG8g dGFsayB0byBGUEdBICovCj4gKwlyZXQgPSBwcml2LT5pb3BzLT5zZXRfYml0bW9kZShwcml2LT5p bnRmLCAweGZmLCBCSVRNT0RFX1NZTkNGRik7Cj4gKwlpZiAocmV0KQo+ICsJCXJldHVybiByZXQ7 Cj4gKwo+ICsJLyogV2FpdCB1bnRpbCBGUEdBIGlzIHJlYWR5IGZvciBsb2FkaW5nIChjb25mX2Rv bmUgemVybykgb3IgdGltZW91dCAqLwo+ICsJZG8gewo+ICsJCXJldCA9IGZwcF9mcGdhX21ncl9y ZWFkX3N0YXR1cyhwcml2LCAmc3RhdHVzKTsKPiArCQlpZiAoIXJldCkgewo+ICsJCQkvKiBDaGVj ayBjb25mX2RvbmUgc3RhdHVzICovCj4gKwkJCWlmICgoc3RhdHVzICYgSU5fQ09ORl9ET05FKSA9 PSAwKQo+ICsJCQkJYnJlYWs7Cj4gKwkJfQo+ICsJfSB3aGlsZSAoLS1yZXRyaWVzID4gMCk7Cj4g Kwo+ICsJaWYgKCFyZXRyaWVzKSB7Cj4gKwkJZGV2X3dhcm4oZGV2LCAiQ09ORl9ET05FIHdhaXQg dGltZW91dFxuIik7Cj4gKwkJcmV0dXJuIC1FVElNRURPVVQ7Cj4gKwl9Cj4gKwo+ICsJLyogQ29u ZmlndXJlIGZvciBtYXguIGJhdWQgcmF0ZSAoM01IeiAqIDQgaW4gYml0YmFuZyBtb2RlKSAqLwo+ ICsJcmV0dXJuIHByaXYtPmlvcHMtPnNldF9iYXVkcmF0ZShwcml2LT5pbnRmLCAzMDAwMDAwKTsK PiArfQo+ICsKPiArc3RhdGljIGludCBmcHBfZnBnYV9tZ3JfZnQyNDVfZmlmb193cml0ZShzdHJ1 Y3QgZnBnYV9tYW5hZ2VyICptZ3IsCj4gKwkJCQkJIGNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGNv dW50KQo+ICt7Cj4gKwlyZXR1cm4gZnBwX2ZwZ2FfbWdyX2JpdGJhbmdfd3JpdGUobWdyLCBidWYs IGNvdW50KTsKPiArfQo+ICsKPiArc3RhdGljIGludCBmcHBfZnBnYV9tZ3JfZnQyNDVfZmlmb193 cml0ZV9jb21wbGV0ZShzdHJ1Y3QgZnBnYV9tYW5hZ2VyICptZ3IsCj4gKwkJCQkJCSAgc3RydWN0 IGZwZ2FfaW1hZ2VfaW5mbyAqaW5mbykKPiArewo+ICsJc3RydWN0IGZwcF9mcGdhX21ncl9wcml2 ICpwcml2ID0gbWdyLT5wcml2Owo+ICsJc3RydWN0IGRldmljZSAqZGV2ID0gJnByaXYtPnBkZXYt PmRldjsKPiArCWludCByZXRyaWVzID0gTUFYX1JFVFJJRVM7Cj4gKwlpbnQgcmV0Owo+ICsJdTgg bWFzaywgc3RhdHVzOwoKQ2FuIHlvdSBzd2FwIHRoZSB0d28gbGluZXMsIHJldmVyc2UgeG1hcy10 cmVlIHBsZWFzZS4KPiArCj4gKwltYXNrID0gSU5fQ09ORl9ET05FIHwgSU5fSU5JVF9ET05FOwo+ ICsKPiArCWRvIHsKPiArCQlyZXQgPSBmcHBfZnBnYV9tZ3JfcmVhZF9zdGF0dXMocHJpdiwgJnN0 YXR1cyk7Cj4gKwkJaWYgKCFyZXQpIHsKPiArCQkJLyogQ2hlY2sgY29uZl9kb25lL2luaXRfZG9u ZSBzdGF0dXMgKi8KPiArCQkJaWYgKChzdGF0dXMgJiBtYXNrKSA9PSBtYXNrKQo+ICsJCQkJYnJl YWs7Cj4gKwkJfQo+ICsJfSB3aGlsZSAoLS1yZXRyaWVzID4gMCk7Cj4gKwo+ICsJaWYgKCFyZXRy aWVzKSB7Cj4gKwkJZGV2X3dhcm4oZGV2LCAiSU5JVF9ET05FIHdhaXQgdGltZW91dFxuIik7Cj4g KwkJcmV0dXJuIC1FVElNRURPVVQ7Cj4gKwl9Cj4gKwo+ICsJLyogUmVsZWFzZSBSZXNldF9uLCBr ZWVwIG5DT05GSUcgaGlnaCwgdG9vISAqLwo+ICsJcmV0dXJuIGZwcF9mcGdhX21ncl9zZXRfZGF0 YV9wb3J0KHByaXYsIE9VVF9OQ09ORklHIHwgT1VUX1JFU0VUX04sIDEpOwo+ICt9Cj4gKwo+ICtz dGF0aWMgZW51bSBmcGdhX21ncl9zdGF0ZXMgZnBwX2ZwZ2FfbWdyX3N0YXRlKHN0cnVjdCBmcGdh X21hbmFnZXIgKm1ncikKPiArewo+ICsJcmV0dXJuIEZQR0FfTUdSX1NUQVRFX1VOS05PV047Cj4g K30KPiArCj4gK3N0YXRpYyBpbnQgZnBwX2ZwZ2FfbWdyX3dyaXRlX2luaXQoc3RydWN0IGZwZ2Ff bWFuYWdlciAqbWdyLAo+ICsJCQkJICAgc3RydWN0IGZwZ2FfaW1hZ2VfaW5mbyAqaW5mbywKPiAr CQkJCSAgIGNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGNvdW50KQo+ICt7Cj4gKwlzdHJ1Y3QgZnBw X2ZwZ2FfbWdyX3ByaXYgKnByaXYgPSBtZ3ItPnByaXY7Cj4gKwo+ICsJaWYgKGluZm8gJiYgaW5m by0+ZmxhZ3MgJiBGUEdBX01HUl9QQVJUSUFMX1JFQ09ORklHKSB7Cj4gKwkJZGV2X2VycigmbWdy LT5kZXYsICJQYXJ0aWFsIHJlY29uZmlndXJhdGlvbiBub3Qgc3VwcG9ydGVkLlxuIik7Cj4gKwkJ cmV0dXJuIC1FSU5WQUw7Cj4gKwl9Cj4gKwo+ICsJaWYgKHByaXYtPm9wcy0+d3JpdGVfaW5pdCkK PiArCQlyZXR1cm4gcHJpdi0+b3BzLT53cml0ZV9pbml0KG1nciwgaW5mbywgYnVmLCBjb3VudCk7 Cj4gKwo+ICsJcmV0dXJuIC1FTk9ERVY7Cj4gK30KPiArCj4gK3N0YXRpYyBpbnQgZnBwX2ZwZ2Ff bWdyX3dyaXRlKHN0cnVjdCBmcGdhX21hbmFnZXIgKm1nciwgY29uc3QgY2hhciAqYnVmLAo+ICsJ CQkgICAgICBzaXplX3QgY291bnQpCj4gK3sKPiArCXN0cnVjdCBmcHBfZnBnYV9tZ3JfcHJpdiAq cHJpdiA9IG1nci0+cHJpdjsKPiArCj4gKwlpZiAocHJpdi0+b3BzLT53cml0ZSkKPiArCQlyZXR1 cm4gcHJpdi0+b3BzLT53cml0ZShtZ3IsIGJ1ZiwgY291bnQpOwo+ICsKPiArCXJldHVybiAtRU5P REVWOwo+ICt9Cj4gKwo+ICtzdGF0aWMgaW50IGZwcF9mcGdhX21ncl93cml0ZV9jb21wbGV0ZShz dHJ1Y3QgZnBnYV9tYW5hZ2VyICptZ3IsCj4gKwkJCQkgICAgICAgc3RydWN0IGZwZ2FfaW1hZ2Vf aW5mbyAqaW5mbykKPiArewo+ICsJc3RydWN0IGZwcF9mcGdhX21ncl9wcml2ICpwcml2ID0gbWdy LT5wcml2Owo+ICsKPiArCWlmIChwcml2LT5vcHMtPndyaXRlX2NvbXBsZXRlKQo+ICsJCXJldHVy biBwcml2LT5vcHMtPndyaXRlX2NvbXBsZXRlKG1nciwgaW5mbyk7Cj4gKwo+ICsJcmV0dXJuIC1F Tk9ERVY7Cj4gK30KPiArCj4gK3N0YXRpYyBzdHJ1Y3QgZnBwX21ncl9vcHMgZnBwX21ncl9iaXRi YW5nX29wcyA9IHsKPiArCS53cml0ZV9pbml0CT0gZnBwX2ZwZ2FfbWdyX2JpdGJhbmdfd3JpdGVf aW5pdCwKPiArCS53cml0ZQkJPSBmcHBfZnBnYV9tZ3JfYml0YmFuZ193cml0ZSwKPiArCS53cml0 ZV9jb21wbGV0ZQk9IGZwcF9mcGdhX21ncl9iaXRiYW5nX3dyaXRlX2NvbXBsZXRlLAo+ICt9Owo+ ICsKPiArc3RhdGljIHN0cnVjdCBmcHBfbWdyX29wcyBmcHBfbWdyX2Z0MjQ1X2ZpZm9fb3BzID0g ewo+ICsJLndyaXRlX2luaXQJPSBmcHBfZnBnYV9tZ3JfZnQyNDVfZmlmb193cml0ZV9pbml0LAo+ ICsJLndyaXRlCQk9IGZwcF9mcGdhX21ncl9mdDI0NV9maWZvX3dyaXRlLAo+ICsJLndyaXRlX2Nv bXBsZXRlCT0gZnBwX2ZwZ2FfbWdyX2Z0MjQ1X2ZpZm9fd3JpdGVfY29tcGxldGUsCj4gK307Cj4g Kwo+ICtzdGF0aWMgY29uc3Qgc3RydWN0IGZwZ2FfbWFuYWdlcl9vcHMgZnBwX2ZwZ2FfbWdyX29w cyA9IHsKPiArCS5zdGF0ZQkJPSBmcHBfZnBnYV9tZ3Jfc3RhdGUsCj4gKwkud3JpdGVfaW5pdAk9 IGZwcF9mcGdhX21ncl93cml0ZV9pbml0LAo+ICsJLndyaXRlCQk9IGZwcF9mcGdhX21ncl93cml0 ZSwKPiArCS53cml0ZV9jb21wbGV0ZQk9IGZwcF9mcGdhX21ncl93cml0ZV9jb21wbGV0ZSwKPiAr fTsKPiArCj4gK3N0YXRpYyBzc2l6ZV90IGNmZ19tb2RlX3Nob3coc3RydWN0IGRldmljZSAqZGV2 LCBzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwKPiArCQkJICAgICBjaGFyICpidWYpCj4g K3sKPiArCXN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYgPSB0b19wbGF0Zm9ybV9kZXZpY2Uo ZGV2KTsKPiArCXN0cnVjdCBmcGdhX21hbmFnZXIgKm1nciA9IHBsYXRmb3JtX2dldF9kcnZkYXRh KHBkZXYpOwo+ICsJc3RydWN0IGZwcF9mcGdhX21ncl9wcml2ICpwcml2ID0gbWdyLT5wcml2Owo+ ICsKPiArCXJldHVybiBzbnByaW50ZihidWYsIFBBR0VfU0laRSwgIiVzXG4iLCBwcml2LT5jZmdf bW9kZSk7Cj4gK30KPiArCj4gK3N0YXRpYyBzc2l6ZV90IGNmZ19tb2RlX3N0b3JlKHN0cnVjdCBk ZXZpY2UgKmRldiwgc3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmF0dHIsCj4gKwkJCSAgICAgIGNv bnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGNvdW50KQo+ICt7Cj4gKwlzdHJ1Y3QgcGxhdGZvcm1fZGV2 aWNlICpwZGV2ID0gdG9fcGxhdGZvcm1fZGV2aWNlKGRldik7Cj4gKwlzdHJ1Y3QgZnBnYV9tYW5h Z2VyICptZ3IgPSBwbGF0Zm9ybV9nZXRfZHJ2ZGF0YShwZGV2KTsKPiArCXN0cnVjdCBmcHBfZnBn YV9tZ3JfcHJpdiAqcHJpdiA9IG1nci0+cHJpdjsKPiArCj4gKwlpZiAoIWNvdW50IHx8IGNvdW50 ID4gc2l6ZW9mKHByaXYtPmNmZ19tb2RlKSkKPiArCQlyZXR1cm4gLUVJTlZBTDsKPiArCj4gKwlp ZiAoIXN0cm5jbXAoYnVmLCAiZmlmbyIsIDQpKSB7Cj4gKwkJc3RybmNweShwcml2LT5jZmdfbW9k ZSwgYnVmLCBzaXplb2YocHJpdi0+Y2ZnX21vZGUpIC0gMSk7Cj4gKwkJcHJpdi0+Y2ZnX21vZGVb NF0gPSAwOwo+ICsJCXByaXYtPm9wcyA9ICZmcHBfbWdyX2Z0MjQ1X2ZpZm9fb3BzOwo+ICsJCWdw aW9kX2RpcmVjdGlvbl9vdXRwdXRfcmF3KHByaXYtPmNvbmZfZG9uZSwgMCk7Cj4gKwl9IGVsc2Ug aWYgKCFzdHJuY21wKGJ1ZiwgImJpdGJhbmciLCA3KSkgewo+ICsJCXN0cm5jcHkocHJpdi0+Y2Zn X21vZGUsIGJ1Ziwgc2l6ZW9mKHByaXYtPmNmZ19tb2RlKSAtIDEpOwo+ICsJCXByaXYtPmNmZ19t b2RlWzddID0gMDsKPiArCQlwcml2LT5vcHMgPSAmZnBwX21ncl9iaXRiYW5nX29wczsKPiArCQln cGlvZF9kaXJlY3Rpb25faW5wdXQocHJpdi0+Y29uZl9kb25lKTsKPiArCX0gZWxzZSB7Cj4gKwkJ cmV0dXJuIC1FSU5WQUw7Cj4gKwl9Cj4gKwo+ICsJcmV0dXJuIGNvdW50Owo+ICt9Cj4gKwo+ICtz dGF0aWMgREVWSUNFX0FUVFJfUlcoY2ZnX21vZGUpOwo+ICsKPiArc3RhdGljIGludCBmcHBfZnBn YV9tZ3JfcHJvYmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikKPiArewo+ICsJc3RydWN0 IGRldmljZSAqZGV2ID0gJnBkZXYtPmRldjsKPiArCXN0cnVjdCBmcHBfZnBnYV9tZ3JfcHJpdiAq cHJpdjsKPiArCXN0cnVjdCBmcGdhX21hbmFnZXIgKm1ncjsKPiArCXN0cnVjdCBmaWZvX2ZwcF9t Z3JfcGxhdGZvcm1fZGF0YSAqcGQ7Cj4gKwlpbnQgcmV0LCByZXRyaWVzID0gTUFYX1JFVFJJRVM7 Cj4gKwljaGFyIGlkX3N0cmluZ1s4XTsKPiArCXU4IHN0YXR1cyA9IDA7Cj4gKwo+ICsJcGQgPSBk ZXYtPnBsYXRmb3JtX2RhdGE7Cj4gKwlpZiAoIXBkKSB7Cj4gKwkJZGV2X2VycihkZXYsICJNaXNz aW5nIHBsYXRmb3JtIGRhdGEuXG4iKTsKPiArCQlyZXR1cm4gLUVJTlZBTDsKPiArCX0KPiArCj4g KwlpZiAoIXBkLT5vcHMgfHwKPiArCSAgICAhcGQtPm9wcy0+YnVsa194ZmVyIHx8ICFwZC0+b3Bz LT5jdHJsX3hmZXIgfHwKPiArCSAgICAhcGQtPm9wcy0+cmVhZF9kYXRhIHx8ICFwZC0+b3BzLT53 cml0ZV9kYXRhIHx8Cj4gKwkgICAgIXBkLT5vcHMtPnNldF9iaXRtb2RlIHx8ICFwZC0+b3BzLT5z ZXRfYmF1ZHJhdGUgfHwKPiArCSAgICAhcGQtPm9wcy0+ZGlzYWJsZV9iaXRiYW5nKQo+ICsJCXJl dHVybiAtRUlOVkFMOwo+ICsKPiArCXByaXYgPSBkZXZtX2t6YWxsb2MoZGV2LCBzaXplb2YoKnBy aXYpLCBHRlBfS0VSTkVMKTsKPiArCWlmICghcHJpdikKPiArCQlyZXR1cm4gLUVOT01FTTsKPiAr Cj4gKwlwcml2LT5pbnRmID0gdG9fdXNiX2ludGVyZmFjZShkZXYtPnBhcmVudCk7Cj4gKwlwcml2 LT5pb3BzID0gcGQtPm9wczsKPiArCj4gKwlyZXQgPSBzc2NhbmYoZGV2X25hbWUoZGV2LT5wYXJl bnQpLCAiJXMiLCBwcml2LT51c2JfZGV2X2lkKTsKPiArCWlmIChyZXQgIT0gMSkgewo+ICsJCWRl dl9lcnIoZGV2LCAiQ2FuJ3QgZ2V0IHBhcmVudCBkZXZpY2UgbmFtZTogJWRcbiIsIHJldCk7Cj4g KwkJcmV0dXJuIC1FTk9ERVY7Cj4gKwl9Cj4gKwo+ICsJcHJpdi0+cGRldiA9IHBkZXY7Cj4gKwlw cml2LT5vcHMgPSAmZnBwX21ncl9mdDI0NV9maWZvX29wczsKPiArCXN0cm5jcHkocHJpdi0+Y2Zn X21vZGUsICJmaWZvIiwgc2l6ZW9mKHByaXYtPmNmZ19tb2RlKSk7Cj4gKwo+ICsJcHJpdi0+bmNv bmZpZyA9IGRldm1fZ3Bpb2RfZ2V0KGRldiwgIm5jb25maWciLCBHUElPRF9PVVRfTE9XKTsKPiAr CWlmIChJU19FUlIocHJpdi0+bmNvbmZpZykpIHsKPiArCQlyZXQgPSBQVFJfRVJSKHByaXYtPm5j b25maWcpOwo+ICsJCWRldl9lcnIoZGV2LCAiRmFpbGVkIHRvIGdldCBuY29uZmlnIGdwaW86ICVk XG4iLCByZXQpOwo+ICsJCXJldHVybiByZXQ7Cj4gKwl9Cj4gKwo+ICsJcHJpdi0+Y29uZl9kb25l ID0gZGV2bV9ncGlvZF9nZXQoZGV2LCAiY29uZl9kb25lIiwgR1BJT0RfT1VUX0xPVyk7Cj4gKwlp ZiAoSVNfRVJSKHByaXYtPmNvbmZfZG9uZSkpIHsKPiArCQlyZXQgPSBQVFJfRVJSKHByaXYtPmNv bmZfZG9uZSk7Cj4gKwkJZGV2X2VycihkZXYsICJGYWlsZWQgdG8gZ2V0IGNvbmZfZG9uZSBncGlv OiAlZFxuIiwgcmV0KTsKPiArCQlnb3RvIGVycl9jZmcxOwo+ICsJfQo+ICsKPiArCXByaXYtPmJ1 bGtfYnVmID0gZGV2bV9rbWFsbG9jKGRldiwgQlVMS19PVVRfQlVGX1NaLAo+ICsJCQkJICAgICAg R0ZQX0tFUk5FTCB8IEdGUF9ETUEzMik7Cj4gKwlpZiAoIXByaXYtPmJ1bGtfYnVmKSB7Cj4gKwkJ cmV0ID0gLUVOT01FTTsKPiArCQlnb3RvIGVycl9jZmcyOwo+ICsJfQo+ICsKPiArCXJldCA9IHBy aXYtPmlvcHMtPnNldF9iaXRtb2RlKHByaXYtPmludGYsIDB4ZmYsIEJJVE1PREVfU1lOQ0ZGKTsK PiArCWlmIChyZXQpCj4gKwkJZ290byBlcnJfY2ZnMjsKPiArCj4gKwkvKiBSZWFkIHN0YXR1cyBy ZWdpc3RlciBmcm9tIENQTEQgKi8KPiArCWRvIHsKPiArCQlyZXQgPSBmcHBfZnBnYV9tZ3JfcmVh ZF9zdGF0dXMocHJpdiwgJnN0YXR1cyk7Cj4gKwkJaWYgKCFyZXQpCj4gKwkJCWJyZWFrOwo+ICsJ fSB3aGlsZSAoLS1yZXRyaWVzID4gMCk7Cj4gKwo+ICsJaWYgKCFyZXRyaWVzKSB7Cj4gKwkJcmV0 ID0gLUVUSU1FRE9VVDsKPiArCQlnb3RvIGVycl9jZmcyOwo+ICsJfQo+ICsKPiArCXByaXYtPnJl diA9IChzdGF0dXMgJiBJTl9CT0FSRF9SRVYpID8gQk9BUkRfUkVWQiA6IEJPQVJEX1JFVkE7Cj4g Kwo+ICsJaWYgKHByaXYtPnJldiA9PSBCT0FSRF9SRVZCKSB7Cj4gKwkJcHJpdi0+YWRkcl9zZWwg PSAoc3RhdHVzICYgSU5fQUREUl9TRUxFQ1QpID8KPiArCQkJCSBBRERSX1NFTEVDVF9OQyA6IEFE RFJfU0VMRUNUX0dORDsKPiArCQlpZiAocHJpdi0+YWRkcl9zZWwgPT0gQUREUl9TRUxFQ1RfTkMp Cj4gKwkJCXN0cm5jcHkoaWRfc3RyaW5nLCAicmlnaHQiLCBzaXplb2YoaWRfc3RyaW5nKSk7Cj4g KwkJZWxzZQo+ICsJCQlzdHJuY3B5KGlkX3N0cmluZywgImxlZnQiLCBzaXplb2YoaWRfc3RyaW5n KSk7Cj4gKwl9IGVsc2Ugewo+ICsJCXByaXYtPmFkZHJfc2VsID0gQUREUl9TRUxFQ1RfSU5WQUxJ RDsKPiArCQlzdHJuY3B5KGlkX3N0cmluZywgInNpbmdsZSIsIHNpemVvZihpZF9zdHJpbmcpKTsK PiArCX0KPiArCj4gKwlkZXZfaW5mbyhkZXYsICJCb2FyZCBSZXYgJWQsIEFkZHIgU2VsICVkXG4i LCBwcml2LT5yZXYsIHByaXYtPmFkZHJfc2VsKTsKPiArCj4gKwkvKiBVc2UgdW5pcXVlIGJvYXJk IElEIGFuZCBVU0IgYnVzL3BvcnQgaW4gRlBHQSBtYW5hZ2VyIG5hbWUgKi8KPiArCXNucHJpbnRm KHByaXYtPmZwZ2FfbWdyX25hbWUsIHNpemVvZihwcml2LT5mcGdhX21ncl9uYW1lKSwKPiArCQkg ImZ0ZGktZnBwLWZwZ2EtbWdyICVzICVzIiwgaWRfc3RyaW5nLCBwcml2LT51c2JfZGV2X2lkKTsK PiArCj4gKwltZ3IgPSBkZXZtX2ZwZ2FfbWdyX2NyZWF0ZShkZXYsIHByaXYtPmZwZ2FfbWdyX25h bWUsCj4gKwkJCQkgICAmZnBwX2ZwZ2FfbWdyX29wcywgcHJpdik7Cj4gKwlpZiAoIW1ncikKPiAr CQlnb3RvIGVycl9jZmcyOwo+ICsKPiArCXBsYXRmb3JtX3NldF9kcnZkYXRhKHBkZXYsIG1ncik7 Cj4gKwo+ICsJcmV0ID0gZnBnYV9tZ3JfcmVnaXN0ZXIobWdyKTsKPiArCWlmIChyZXQpIHsKPiAr CQlkZXZfZXJyKGRldiwgInVuYWJsZSB0byByZWdpc3RlciBGUEdBIG1hbmFnZXJcbiIpOwo+ICsJ CWdvdG8gZXJyX2NmZzI7Cj4gKwl9Cj4gKwo+ICsJcmV0ID0gZGV2aWNlX2NyZWF0ZV9maWxlKGRl diwgJmRldl9hdHRyX2NmZ19tb2RlKTsKPiArCWlmIChyZXQpCj4gKwkJZGV2X3dhcm4oZGV2LCAi Q2FuJ3QgY3JlYXRlIGNmZ19tb2RlIGludGVyZmFjZSAlZFxuIiwgcmV0KTsKPiArCj4gKwlyZXR1 cm4gMDsKPiArCj4gK2Vycl9jZmcyOgo+ICsJZGV2bV9ncGlvZF9wdXQoZGV2LCBwcml2LT5jb25m X2RvbmUpOwo+ICtlcnJfY2ZnMToKPiArCWRldm1fZ3Bpb2RfcHV0KGRldiwgcHJpdi0+bmNvbmZp Zyk7Cj4gKwlyZXR1cm4gcmV0Owo+ICt9Cj4gKwo+ICtzdGF0aWMgaW50IGZwcF9mcGdhX21ncl9y ZW1vdmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikKPiArewo+ICsJc3RydWN0IGZwZ2Ff bWFuYWdlciAqbWdyID0gcGxhdGZvcm1fZ2V0X2RydmRhdGEocGRldik7Cj4gKwlzdHJ1Y3QgZnBw X2ZwZ2FfbWdyX3ByaXYgKnByaXYgPSBtZ3ItPnByaXY7Cj4gKwo+ICsJZGV2aWNlX3JlbW92ZV9m aWxlKCZwZGV2LT5kZXYsICZkZXZfYXR0cl9jZmdfbW9kZSk7Cj4gKwlmcGdhX21ncl91bnJlZ2lz dGVyKG1ncik7Cj4gKwlkZXZtX2dwaW9kX3B1dCgmcGRldi0+ZGV2LCBwcml2LT5jb25mX2RvbmUp Owo+ICsJZGV2bV9ncGlvZF9wdXQoJnBkZXYtPmRldiwgcHJpdi0+bmNvbmZpZyk7Cj4gKwlyZXR1 cm4gMDsKPiArfQo+ICsKPiArc3RhdGljIHN0cnVjdCBwbGF0Zm9ybV9kcml2ZXIgZnBwX2ZwZ2Ff bWdyX2RyaXZlciA9IHsKPiArCS5kcml2ZXIubmFtZQk9ICJmdGRpLWZpZm8tZnBwLW1nciIsCj4g KwkucHJvYmUJCT0gZnBwX2ZwZ2FfbWdyX3Byb2JlLAo+ICsJLnJlbW92ZQkJPSBmcHBfZnBnYV9t Z3JfcmVtb3ZlLAo+ICt9Owo+ICsKPiArbW9kdWxlX3BsYXRmb3JtX2RyaXZlcihmcHBfZnBnYV9t Z3JfZHJpdmVyKTsKPiArCj4gK01PRFVMRV9BTElBUygicGxhdGZvcm06ZnRkaS1maWZvLWZwcC1t Z3IiKTsKPiArTU9EVUxFX0FVVEhPUigiQW5hdG9saWogR3VzdHNjaGluIDxhZ3VzdEBkZW54LmRl PiIpOwo+ICtNT0RVTEVfREVTQ1JJUFRJT04oIkZUMjMySCBCaXRiYW5nL0ZUMjQ1LUZJRk8gRlBQ IEZQR0EgTWFuYWdlciBEcml2ZXIiKTsKPiArTU9EVUxFX0xJQ0VOU0UoIkdQTCB2MiIpOwo+IC0t IAo+IDIuMTcuMQo+IAoKVGhhbmtzLAoKTW9yaXR6Cg==