From patchwork Sun Feb 27 11:58:30 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Kestrel X-Patchwork-Id: 12761610 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AB62CC4332F for ; Sun, 27 Feb 2022 11:59:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230322AbiB0L7l (ORCPT ); Sun, 27 Feb 2022 06:59:41 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44870 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230312AbiB0L7j (ORCPT ); Sun, 27 Feb 2022 06:59:39 -0500 Received: from mail-ej1-x634.google.com (mail-ej1-x634.google.com [IPv6:2a00:1450:4864:20::634]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 430913D1C2; Sun, 27 Feb 2022 03:59:03 -0800 (PST) Received: by mail-ej1-x634.google.com with SMTP id d10so19533511eje.10; Sun, 27 Feb 2022 03:59:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=GHH6iYGdl9sRkae4YTPPmdcP2BrajZ72JH5y9Uk/yV0=; b=MvJug4HjpA75M3roZduzKZWtrOEF3mkLSJ8qZAlurSmVYaK/npEZ88lECkBQdYQpdD OLLgn6botJ9sNF+HREocK5xKQz2o/2/f5KQwTLmKo85YIau/mW0qnXQ5gpq6IgEsmMLY 5h46hvPd4sxXxiw1mQNS9800oRktBlphGtvq1ChwHbhZnffONGMSAzoXAOU/894MZtCf pkp97sknPmuFYFf/n/I6ACcpHHwxzhUg1kt6h1WLvXZXRkQ8VmYJgAJ0tvs8zNHaqso0 XGSXZJ7Pc437pWc8jQXQz13gCEH4WH6oRDypaHs9SGjIymaySXoc7EMsbBc5W18sKfxX pnJQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=GHH6iYGdl9sRkae4YTPPmdcP2BrajZ72JH5y9Uk/yV0=; b=rvTDPkpB+o0CVENzIEmdN8F/ayxGdzu4gJ9V1HlsdmfanhRzdnsdhnlOeG9QBEvm9q WCILnyuPbMmkep/e3LyUMzxCX2+3V894iruvsMGNqYTnWdmjs7mHcOye29F46uywS+8A fBnjR1GFeVyI9WheFhyTCEy5ulnq0zXDVeASHOTjRaPbLWVEWiUCtHovNtAsYbOMo8Hp TtteYh4oB7uhQIeViRIV9vNLa88chH5D8Sh3cgK1X+6rbWKZWaYmg3ARsrbJKIRkoRsg 0tGzAJcRTr1ZRe4Ym+SKsDOEwp/ubSFJGr8bfQ7pvuEppbNmcLG0snUnY+F+Sc9R8ZOI 4Y0g== X-Gm-Message-State: AOAM533+0ErhJmFt5VOGS0WqZ8Cm32p55VZ4wPqLylpUbY137H0C/UZT 3Fpf4NY4pGbe0lY1CE50E9k= X-Google-Smtp-Source: ABdhPJzPZO1WVCPmXhqeGZnER7gUT4R8bAF6591QAoHkqNbno0n1D4Sy1PetQ2dVzIbjs/saEMN08Q== X-Received: by 2002:a17:907:920d:b0:6ce:a6fc:1ad8 with SMTP id ka13-20020a170907920d00b006cea6fc1ad8mr12242353ejb.448.1645963141725; Sun, 27 Feb 2022 03:59:01 -0800 (PST) Received: from localhost.localdomain ([155.133.219.250]) by smtp.gmail.com with ESMTPSA id dn14-20020a05640222ee00b00410b88abd6fsm4474577edb.45.2022.02.27.03.59.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Feb 2022 03:59:01 -0800 (PST) From: Daniel Kestrel Cc: Bjorn Andersson , Mathieu Poirier , Rob Herring , Daniel Kestrel , linux-remoteproc@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 1/3] dt-bindings: vendor-prefixes: Add AVM Date: Sun, 27 Feb 2022 12:58:30 +0100 Message-Id: <20220227115832.13490-2-kestrelseventyfour@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220227115832.13490-1-kestrelseventyfour@gmail.com> References: <20220227115832.13490-1-kestrelseventyfour@gmail.com> To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-remoteproc@vger.kernel.org Add vendor prefix for AVM Computersysteme Vertriebs GmbH (http://www.avm.de/en) Signed-off-by: Daniel Kestrel Acked-by: Rob Herring --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 294093d45a23..0be52bb27f2b 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -153,6 +153,8 @@ patternProperties: description: avia semiconductor "^avic,.*": description: Shanghai AVIC Optoelectronics Co., Ltd. + "^avm,.*": + description: AVM Computersysteme Vertriebs GmbH "^avnet,.*": description: Avnet, Inc. "^awinic,.*": From patchwork Sun Feb 27 11:58:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Kestrel X-Patchwork-Id: 12761611 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4B3E0C433FE for ; Sun, 27 Feb 2022 11:59:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230330AbiB0L7m (ORCPT ); Sun, 27 Feb 2022 06:59:42 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44934 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230315AbiB0L7k (ORCPT ); Sun, 27 Feb 2022 06:59:40 -0500 Received: from mail-ed1-x534.google.com (mail-ed1-x534.google.com [IPv6:2a00:1450:4864:20::534]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2AC453DDF1; Sun, 27 Feb 2022 03:59:04 -0800 (PST) Received: by mail-ed1-x534.google.com with SMTP id s14so13747273edw.0; Sun, 27 Feb 2022 03:59:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=+o8a4+sMFmJIptftdwbAGTNrygKBIVBJrDmKwhFceYY=; b=Edh57K8USvUrYtNnVvM9jioHrXrN/0nDElwH2Iq2SZBW0icqf0pXsKmaFnAFuqTBF9 FutLkCioszFXPwbdkw9er37qeeVHPqnzZ3ebYqrB/Nkd7PMAvdSyRJvyJNZGC+lSsJuH wzvzOtS6U/FEGguPsZ1r2K9hAhRxWuOn/jEJReZCLRAYya0YrRB99RR8H/WFPCGEuoBJ Bb2KGtA/BJ3fTT9zbEZDPFr1QHKcmKUY3/KN2yLktfaVUPfzR+p4cd/Ma2KqvElPX1Mc Z0mJ2MYXL8ijbbPE82e3rGUuBHRcAuKWciTc8kDDH6Yh36TTeXET5WDM6Not83KzkCct hFNA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=+o8a4+sMFmJIptftdwbAGTNrygKBIVBJrDmKwhFceYY=; b=AnFSZAfPwqdw7mOQN+uaywvk9GmXegebo7Xk7sK7UlpQC9BP/ERMZs7vEI75oe0u3j Rrmu/qb8pam+3auAoBRD2Zko6J4G6GcSJ/K7J3ZtLK8+b7w7pgGWfnx9omhiqAdBkfE7 PqbEIGhn1VNjQWS09Qe4NOsASuycal4YH4YHtVLMihQoyOP06HuFawE6yvcisdBpJq6R VUFrEsm2jPipGqLuhWo/iXnSqTYuHMkKQxYnUAd/Ygf5/Uc321iNGBLhJ8TWGTtc+xm/ AgVzPXcYhoPaBYZCerQX38/eAplSe23Bmmnqcp6dltHvlwnTAk/avpZT+mCb/Z4RnNaJ xMMA== X-Gm-Message-State: AOAM532urIz+q0oLcLHYhAywZNkT5TmagDgYEJeHxK00EI7AfAcBFRpV L9WYqDl/MmPI4HvVjmJyBK0= X-Google-Smtp-Source: ABdhPJxA03uU5tiQFzyry4IGeCczcdpoSqFDZOy3O6QJ4NicL07h6L5Fj3VLFUnbR1tLdQ8huS3E4w== X-Received: by 2002:a05:6402:4409:b0:412:aac5:4e48 with SMTP id y9-20020a056402440900b00412aac54e48mr14866759eda.75.1645963142763; Sun, 27 Feb 2022 03:59:02 -0800 (PST) Received: from localhost.localdomain ([155.133.219.250]) by smtp.gmail.com with ESMTPSA id dn14-20020a05640222ee00b00410b88abd6fsm4474577edb.45.2022.02.27.03.59.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Feb 2022 03:59:02 -0800 (PST) From: Daniel Kestrel Cc: Bjorn Andersson , Mathieu Poirier , Rob Herring , Daniel Kestrel , linux-remoteproc@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 2/3] dt-bindings: remoteproc: Add AVM WASP Date: Sun, 27 Feb 2022 12:58:31 +0100 Message-Id: <20220227115832.13490-3-kestrelseventyfour@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220227115832.13490-1-kestrelseventyfour@gmail.com> References: <20220227115832.13490-1-kestrelseventyfour@gmail.com> To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-remoteproc@vger.kernel.org AVM Fritzbox router boards may contain an additional ATH79 based SoC that has the wifi cards connected. This patch adds bindings for this remote processor. Signed-off-by: Daniel Kestrel --- .../bindings/remoteproc/avm,wasp-rproc.yaml | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/remoteproc/avm,wasp-rproc.yaml diff --git a/Documentation/devicetree/bindings/remoteproc/avm,wasp-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/avm,wasp-rproc.yaml new file mode 100644 index 000000000000..8b39fafc60aa --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/avm,wasp-rproc.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/avm,wasp-rproc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AVM WASP processor controller bindings + +maintainers: + - Daniel Kestrel + +description: | + This document defines the bindings for the remoteproc component that loads and + boots firmwares on the AVM Wireless Assistent Support Processor (WASP) SoC + that is attached to some AVM Fritzbox devices (3390, 3490, 5490, 5491, 7490). + +properties: + compatible: + enum: + - avm,fritzbox3390-wasp + - avm,fritzboxx490-wasp + + avm,wasp-mdio: + $ref: /schemas/types.yaml#/definitions/phandle + description: Lantiq GSWIP switch mdio. + + avm,wasp-port: + $ref: /schemas/types.yaml#/definitions/phandle + description: Network port, where the WASP SoC is connected to. + + avm,reset-gpios: + description: Reset gpio of the WASP SoC. + + avm,startup-gpios: + description: Startup gpio of the WASP SoC. + +required: + - compatible + - avm,wasp-mdio + - avm,wasp-port + - avm,reset-gpios + - avm,startup-gpios + +additionalProperties: false + +examples: + - | + #include + + remoteproc { + compatible = "avm,fritzboxx490-wasp"; + avm,wasp-mdio = <&gswip_mdio>; + avm,wasp-port = <&port5>; + avm,reset-gpios = <&gpio 34 GPIO_ACTIVE_HIGH>; + avm,startup-gpios = <&gpio 5 GPIO_ACTIVE_HIGH>; + }; From patchwork Sun Feb 27 11:58:32 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Daniel Kestrel X-Patchwork-Id: 12761612 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 76601C4332F for ; Sun, 27 Feb 2022 11:59:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230366AbiB0L7s (ORCPT ); Sun, 27 Feb 2022 06:59:48 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45266 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230338AbiB0L7n (ORCPT ); Sun, 27 Feb 2022 06:59:43 -0500 Received: from mail-ej1-x630.google.com (mail-ej1-x630.google.com [IPv6:2a00:1450:4864:20::630]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5963E3CFC7; Sun, 27 Feb 2022 03:59:05 -0800 (PST) Received: by mail-ej1-x630.google.com with SMTP id qx21so19520801ejb.13; Sun, 27 Feb 2022 03:59:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=L8NeokpDfdkOLfGT6NpTsNmGnakiDAhMMUb2y3Z7w1U=; b=Grgyyb2+67UIOwl5aWW2Vysw5JpqyiHDWsacVZQxPDYxkfWJBcDMcdJEUOKrUPvJWw JpKEgSlj3i94HSFsLvumky5L7PtYiZYuyJJ6WqkwCReoDQ/LHZMVuELkM4Juu1XGhz1m tX7ce+5Jxa1SDbnPu2D8ZeIU52z/fDHojbYdx2RrAf097obJj88/6zZk+c994q7ltkUY x+5GYd7E7ULDI8sFeI2ltBKfKkV5HrfzEd2w/RJ0pDaULbG0bT2LdLuLrYAsZzPY4U9v fepCQHAoyXM2i2qgqd6qEDjyKyuW613bWxo/Ko6rrjkdt2pgHkWGdhrYNVPYo4iMHNWM gJ1A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=L8NeokpDfdkOLfGT6NpTsNmGnakiDAhMMUb2y3Z7w1U=; b=DybSDUJX+yl1wmN0jkhUVdDoHPY1IFqAoSROxJKfYWG5wak5xlo7jvpto9EORFXk38 A5DZPdgU72Wqoy3OdEDBaDzuF/HJQLP2CqTMSGeZnJWLX/NHhJcIMw/zRtC1YuHLAIS9 WuU1jpYGoxmLRqfKa9vE2ZbFxJ9qXw14c3xSaHUmUccCbueqp8G1+byYfcX1suA5JIY/ PePrboz/d7dV18FTFYhUwXS1b9uUjrCZptHNw6tD9YJpAvb53Z3rbl5HHMPtyPS7b6Dd cHYrYmE4+Mzjz17K6FeHhj2kX3JU5YG6HySunPnOgRrZ6E0q/G5+Y5U6NLW0DWhoU7aA F5pw== X-Gm-Message-State: AOAM533LB40N5NoPwviuNbGt3enVZ63hNxp68OR2OE8bMf6FE7+CUpvr +q51AYemARpL1wLLtBoM8K4= X-Google-Smtp-Source: ABdhPJzLZJbDkYdBP6qVDiH6IH9tXwKkXdL3LAYRY4ssxmn2jPuDofn4BDdc5BtKv7VOodvQDpOqGA== X-Received: by 2002:a17:906:b57:b0:6ce:e31a:524 with SMTP id v23-20020a1709060b5700b006cee31a0524mr11891732ejg.290.1645963143718; Sun, 27 Feb 2022 03:59:03 -0800 (PST) Received: from localhost.localdomain ([155.133.219.250]) by smtp.gmail.com with ESMTPSA id dn14-20020a05640222ee00b00410b88abd6fsm4474577edb.45.2022.02.27.03.59.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Feb 2022 03:59:03 -0800 (PST) From: Daniel Kestrel Cc: Bjorn Andersson , Mathieu Poirier , Rob Herring , Daniel Kestrel , linux-remoteproc@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 3/3] remoteproc: Add AVM WASP driver Date: Sun, 27 Feb 2022 12:58:32 +0100 Message-Id: <20220227115832.13490-4-kestrelseventyfour@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220227115832.13490-1-kestrelseventyfour@gmail.com> References: <20220227115832.13490-1-kestrelseventyfour@gmail.com> MIME-Version: 1.0 To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-remoteproc@vger.kernel.org Some AVM Fritzbox router boards (3390, 3490, 5490, 5491, 7490), that are Lantiq XRX200 based, have a memory only ATH79 based WASP (Wireless Assistant Support Processor) SoC that has wifi cards connected to it. It does not share anything with the Lantiq host and has no persistent storage. It has an mdio based connection for bringing up a small network boot firmware and is connected to the Lantiq GSWIP switch via gigabit ethernet. This is used to load an initramfs linux image to it, after the network boot firmware was started. In order to initialize this remote processor we need to: - power on the SoC using startup gpio - reset the SoC using the reset gpio - send the network boot firmware using mdio - send the linux image using raw ethernet frames This driver allows to start and stop the WASP SoC. Signed-off-by: Daniel Kestrel --- drivers/remoteproc/Kconfig | 10 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/avm_wasp.c | 1051 +++++++++++++++++++++++++++++++++ 3 files changed, 1062 insertions(+) create mode 100644 drivers/remoteproc/avm_wasp.c diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 166019786653..a761186c5171 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -23,6 +23,16 @@ config REMOTEPROC_CDEV It's safe to say N if you don't want to use this interface. +config AVM_WASP_REMOTEPROC + tristate "AVM WASP remoteproc support" + depends on NET_DSA_LANTIQ_GSWIP + help + Say y here to support booting the secondary SoC ATH79 target + called Wireless Assistant Support Processor (WASP) that some + AVM Fritzbox devices (3390, 3490, 5490, 5491, 7490) have built in. + + It's safe to say N here. + config IMX_REMOTEPROC tristate "i.MX remoteproc support" depends on ARCH_MXC diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 5478c7cb9e07..0ae175c6722f 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -11,6 +11,7 @@ remoteproc-y += remoteproc_sysfs.o remoteproc-y += remoteproc_virtio.o remoteproc-y += remoteproc_elf_loader.o obj-$(CONFIG_REMOTEPROC_CDEV) += remoteproc_cdev.o +obj-$(CONFIG_AVM_WASP_REMOTEPROC) += avm_wasp.o obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o obj-$(CONFIG_IMX_DSP_REMOTEPROC) += imx_dsp_rproc.o obj-$(CONFIG_INGENIC_VPU_RPROC) += ingenic_rproc.o diff --git a/drivers/remoteproc/avm_wasp.c b/drivers/remoteproc/avm_wasp.c new file mode 100644 index 000000000000..c890368df684 --- /dev/null +++ b/drivers/remoteproc/avm_wasp.c @@ -0,0 +1,1051 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AVM WASP Remote Processor driver + * + * Copyright (c) 2019-2020 Andreas Böhler + * Copyright (c) 2021-2022 Daniel Kestrel + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "remoteproc_internal.h" + +#define WASP_CHUNK_SIZE 14 +#define WASP_ADDR 0x07 +#define WASP_TIMEOUT_COUNT 1000 +#define WASP_WAIT_TIMEOUT_COUNT 20 +#define WASP_CHECK_LEN_DIVBY4_MASK 0x3 + +#define WASP_WRITE_SLEEP_US 20000 +#define WASP_WAIT_SLEEP_US_LOW 50000 +#define WASP_WAIT_SLEEP_US 100000 +#define WASP_POLL_SLEEP_US 200 + +#define WASP_RESP_RETRY 0x0102 +#define WASP_RESP_OK 0x0002 +#define WASP_RESP_WAIT 0x0401 +#define WASP_RESP_COMPLETED 0x0000 +#define WASP_RESP_READY_TO_START 0x0202 +#define WASP_RESP_STARTING 0x00c9 + +#define WASP_CMD_SET_PARAMS 0x0c01 +#define WASP_CMD_SET_CHECKSUM_3390 0x0801 +#define WASP_CMD_SET_CHECKSUM_X490 0x0401 +#define WASP_CMD_SET_DATA 0x0e01 +#define WASP_CMD_START_FIRMWARE_3390 0x0201 +#define WASP_CMD_START_FIRMWARE_X490 0x0001 +#define WASP_CMD_START_FIRMWARE2_X490 0x0101 + +static const u32 start_addr = 0xbd003000; +static const u32 exec_addr = 0xbd003000; + +static const char mac_data[WASP_CHUNK_SIZE] = {0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0x04, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}; + +enum { + MODEL_3390, + MODEL_X490, + MODEL_UNKNOWN +} m_model = MODEL_UNKNOWN; + +#define ETH_TYPE_ATH_ECPS_FRAME 0x88bd +#define ETH_BUF_SIZE 1056 +#define ETH_SEND_LOOP_TIMEOUT_SECS 60 +#define ETH_MAX_DATA_SIZE 1028 +#define ETH_DATA_SIZE 1024 +#define ETH_WASP_PACKET_ID 0x1200 + +#define CMD_FIRMWARE_DATA 0x0104 +#define CMD_START_FIRMWARE 0xd400 + +#define RESP_DISCOVER 0x0000 +#define RESP_OK 0x0100 +#define RESP_STARTING 0x0200 +#define RESP_ERROR 0x0300 + +static const u32 m_load_addr = 0x81a00000; + +static char wasp_mac[] = {0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}; + +struct wasp_packet { + struct ethhdr eh; + struct __packed { + u16 packet_start; + u8 pad_one[5]; + u16 command; + u16 response; + u16 counter; + u8 pad_two; + } hdr; + u8 payload[ETH_MAX_DATA_SIZE]; +} __packed; + +static char *firmware = "ath9k-eeprom-ahb-18100000.wmac.bin"; +module_param(firmware, charp, 0444); +MODULE_PARM_DESC(firmware, + "Filename of the ath9k eeprom to be loaded"); + +static char *caldata = "ath10k/cal-pci-0000:00:00.0.bin"; +module_param(caldata, charp, 0444); +MODULE_PARM_DESC(caldata, + "Filename of the ath10k caldata to be loaded"); + +static char *netboot = "netboot.fw"; +module_param(netboot, charp, 0444); +MODULE_PARM_DESC(netboot, + "Filename of the network boot firmware for WASP"); + +static char *image = "wasp-image.bin"; +module_param(image, charp, 0444); +MODULE_PARM_DESC(image, + "Filename of the linux image to be loaded to WASP"); + +/** + * struct avm_wasp_rproc - avmwasp remote processor priv + * @rproc: rproc handle + * @pdev: pointer to platform device + * @fw_blob: pointer to load and save any firmware + * @linux_blob: pointer to access initramfs image + * @mdio_bus: pointer to mii_bus of gswip device for gpio + * @startup_gpio: store WASP startup gpio descriptor + * @reset_gpio: store WASP reset gpio descriptor + * @loader_port: store name of the port wasp is connected to + * @buffer: recv buffer for feedback from WASP + * @ifindex: interface index used for WASP communication + */ +struct avm_wasp_rproc { + struct rproc *rproc; + struct platform_device *pdev; + const struct firmware *fw_blob, *linux_blob; + struct mii_bus *mdio_bus; + struct gpio_desc *startup_gpio, *reset_gpio; + char *loader_port; + char buffer[ETH_BUF_SIZE]; + int ifindex; +}; + +/** + * avm_wasp_netboot_mdio_write_u32_split() - write 32bit value + * @avmwasp: pointer to drivers private avm_wasp_rproc structure + * @mdio_reg: register start value for two mdio registers to write to + * @value: value to be written to the register + * + * As the mdio registers are 16bit, this function writes a 32bit value + * to two subsequent mdio registers starting with the specified register + * for the mdio address that is used for the connection to the WASP SoC + * + * Return: 0 on Success, -ETIMEDOUT if avm_wasp_netboot_mdio_write fails + */ +static int avm_wasp_netboot_mdio_write_u32_split(struct avm_wasp_rproc *avmwasp, + u32 mdio_reg, const u32 value) +{ + struct device *dev = &avmwasp->pdev->dev; + int ret; + + ret = mdiobus_write(avmwasp->mdio_bus, WASP_ADDR, mdio_reg, + ((value & 0xffff0000) >> 16)); + if (ret < 0) + goto err; + + ret = mdiobus_write(avmwasp->mdio_bus, WASP_ADDR, mdio_reg + 2, + (value & 0x0000ffff)); + if (ret < 0) + goto err; + + return 0; +err: + dev_err(dev, "mdio split write failed\n"); + return ret; +} + +/** + * avm_wasp_read_poll_timeout() - wrap read_poll_timeout_macro + * @avmwasp: pointer to drivers private avm_wasp_rproc structure + * @udelay: microseconds to wait after read + * @utimeout: timeout value in microseconds + * @checkval: value to be checked against + * + * This function checks checkval against the WASP mdio status register, + * waits udelay before repeating the read and times out after utimeout. + * Separate function because every other use of read_poll_timeout makes + * the kernel module around half a kB larger. + * + * Return: 0 when checkval was read or -ETIMEDOUT if not + */ +static int avm_wasp_read_poll_timeout(struct avm_wasp_rproc *avmwasp, + u32 udelay, u32 utimeout, u32 checkval) +{ + u32 val; + + return read_poll_timeout(mdiobus_read, val, + (val == checkval), udelay, utimeout, false, + avmwasp->mdio_bus, WASP_ADDR, 0x0); +} + +/** + * avm_wasp_netboot_write_header() - write header to WASP + * @avmwasp: pointer to drivers private avm_wasp_rproc structure + * @start_addr: address where to load the firmware to on WASP + * @len: length of the network boot firmware + * @exec_addr: address where to start execution on WASP + * + * Writes the header to WASP using mdio to initiate the start of + * transferring the network boot firmware to WASP + * + * Return: 0 on Success, -ETIMEDOUT if writing header failed based on return + * code from WASP or the write methods + */ +static int avm_wasp_netboot_write_header(struct avm_wasp_rproc *avmwasp, + const u32 start_addr, const u32 len, + const u32 exec_addr) +{ + struct device *dev = &avmwasp->pdev->dev; + int ret; + + ret = avm_wasp_netboot_mdio_write_u32_split(avmwasp, 0x2, start_addr); + if (ret < 0) + goto err; + + ret = avm_wasp_netboot_mdio_write_u32_split(avmwasp, 0x6, len); + if (ret < 0) + goto err; + + ret = avm_wasp_netboot_mdio_write_u32_split(avmwasp, 0xA, exec_addr); + if (ret < 0) + goto err; + + ret = mdiobus_write(avmwasp->mdio_bus, WASP_ADDR, 0x0, + WASP_CMD_SET_PARAMS); + if (ret < 0) + goto err; + + ret = avm_wasp_read_poll_timeout(avmwasp, WASP_POLL_SLEEP_US, + WASP_TIMEOUT_COUNT * WASP_POLL_SLEEP_US, + WASP_RESP_OK); + if (ret < 0) + goto err; + + return 0; +err: + dev_err(dev, "mdio write for header failed\n"); + return ret; +} + +/** + * avm_wasp_netboot_write_checksum() - write checksum to WASP + * @avmwasp: pointer to drivers private avm_wasp_rproc structure + * @checksum: calculated checksum value to be sent to WASP + * + * Writes the calculated checksum for the given network boot firmware + * to WASP using mdio as the second step + * + * Return: 0 on Success, -ETIMEDOUT if writing checksum failed based on + * return code from WASP or the write methods + */ +static int avm_wasp_netboot_write_checksum(struct avm_wasp_rproc *avmwasp, + const u32 checksum) +{ + struct device *dev = &avmwasp->pdev->dev; + int ret; + + ret = avm_wasp_netboot_mdio_write_u32_split(avmwasp, 0x2, checksum); + if (ret < 0) + goto err; + + if (m_model == MODEL_3390) { + ret = avm_wasp_netboot_mdio_write_u32_split(avmwasp, 0x6, 0x0000); + if (ret < 0) + goto err; + + ret = mdiobus_write(avmwasp->mdio_bus, WASP_ADDR, 0x0, + WASP_CMD_SET_CHECKSUM_3390); + } else if (m_model == MODEL_X490) { + ret = mdiobus_write(avmwasp->mdio_bus, WASP_ADDR, 0x0, + WASP_CMD_SET_CHECKSUM_X490); + } + if (ret < 0) + goto err; + + ret = avm_wasp_read_poll_timeout(avmwasp, WASP_POLL_SLEEP_US, + WASP_TIMEOUT_COUNT * WASP_POLL_SLEEP_US, + WASP_RESP_OK); + if (ret < 0) + goto err; + + return 0; +err: + dev_err(dev, "mdio write for checksum failed\n"); + return ret; +} + +/** + * avm_wasp_netboot_write_chunk() - write chunk of data to WASP + * @avmwasp: pointer to drivers private avm_wasp_rproc structure + * @data: pointer to data + * @len: length of data (should not exceed 14 bytes) + * + * Writes up to 14 bytes of data into the 7 16bit mdio registers + * to WASP using mdio + * + * Return: 0 on Success, -EFAULT if data length is more than 14 bytes or + * -ETIMEDOUT if writing the data failed based on return code from WASP + * or the write methods + */ +static int avm_wasp_netboot_write_chunk(struct avm_wasp_rproc *avmwasp, + const char *data, size_t len) +{ + struct device *dev = &avmwasp->pdev->dev; + int i, ret; + + if (len > WASP_CHUNK_SIZE || !data) + return -EFAULT; + + for (i = 0; i < len && ret >= 0; i += 2) + ret = mdiobus_write(avmwasp->mdio_bus, WASP_ADDR, i + 2, + *((u16 *)(data + i))); + if (ret < 0) + goto err; + + ret = mdiobus_write(avmwasp->mdio_bus, WASP_ADDR, 0x0, WASP_CMD_SET_DATA); + if (ret < 0) + goto err; + + ret = avm_wasp_read_poll_timeout(avmwasp, WASP_POLL_SLEEP_US, + WASP_TIMEOUT_COUNT * WASP_POLL_SLEEP_US, + WASP_RESP_OK); + if (ret < 0) + goto err; + + return 0; +err: + dev_err(dev, "mdio write for data chunk failed\n"); + return ret; +} + +/** + * avm_wasp_netboot_calc_checksum() - calculate netboot firmware checksum + * @avmwasp: pointer to drivers private avm_wasp_rproc structure + * + * Calculates the checksum by using the firmware in fw_blob from the private + * avm_wasp_rproc structure + * + * Return: Calculated checksum + */ +static u32 avm_wasp_netboot_calc_checksum(struct avm_wasp_rproc *avmwasp) +{ + u32 checksum = U32_MAX, count = U32_MAX, cs; + const u8 *firmware, *firmware_end; + + firmware = avmwasp->fw_blob->data; + firmware_end = firmware + avmwasp->fw_blob->size; + + while (firmware < firmware_end) { + cs = be32_to_cpu(*(u32 *)firmware); + checksum = checksum - cs; + count++; + firmware += 4; + } + + checksum = checksum - count; + return checksum; +} + +/** + * avm_wasp_netboot_load_firmware() - load netboot firmware to WASP + * @avmwasp: pointer to drivers private avm_wasp_rproc structure + * + * Implements the process to send header, checksum and the firmware + * blob in 14 byte chunks to the WASP processor using mdio + * Includes checks between the steps and sending commands to start + * the network boot firmware + * + * Return: 0 on Success, -ENODEV if WASP not ready after reset, + * -ETIMEDOUT if there was a timeout on polling or + * -EFAULT if other errors have occurred + */ +static int avm_wasp_netboot_load_firmware(struct avm_wasp_rproc *avmwasp) +{ + struct device *dev = &avmwasp->pdev->dev; + struct mii_bus *mdio_bus = avmwasp->mdio_bus; + const u8 *firmware, *firmware_end; + int regval, regval2, ret, cont = 1; + u32 checksum, left; + + ret = avm_wasp_read_poll_timeout(avmwasp, WASP_WAIT_SLEEP_US, + WASP_WAIT_TIMEOUT_COUNT * + WASP_WAIT_SLEEP_US, WASP_RESP_OK); + if (ret) { + dev_err(dev, "error WASP processor not in ready status\n"); + return -ENODEV; + } + + firmware = avmwasp->fw_blob->data; + firmware_end = firmware + avmwasp->fw_blob->size; + + if ((avmwasp->fw_blob->size & WASP_CHECK_LEN_DIVBY4_MASK) || + avmwasp->fw_blob->size > U16_MAX) { + dev_err(dev, "error network boot firmware size\n"); + return -EFAULT; + } + + ret = avm_wasp_netboot_write_header(avmwasp, start_addr, + avmwasp->fw_blob->size, + exec_addr); + if (ret < 0) + return ret; + + checksum = avm_wasp_netboot_calc_checksum(avmwasp); + ret = avm_wasp_netboot_write_checksum(avmwasp, checksum); + + if (ret < 0) + return ret; + + while (firmware < firmware_end) { + left = firmware_end - firmware; + if (left > WASP_CHUNK_SIZE) + left = WASP_CHUNK_SIZE; + ret = avm_wasp_netboot_write_chunk(avmwasp, firmware, left); + if (ret < 0) + return ret; + + firmware += left; + } + + usleep_range(WASP_WAIT_SLEEP_US_LOW, WASP_WAIT_SLEEP_US); + + if (m_model == MODEL_3390) + ret = mdiobus_write(mdio_bus, WASP_ADDR, 0x0, + WASP_CMD_START_FIRMWARE_3390); + else if (m_model == MODEL_X490) + ret = mdiobus_write(mdio_bus, WASP_ADDR, 0x0, + WASP_CMD_START_FIRMWARE_X490); + if (ret < 0) { + dev_err(dev, "writing command failed\n"); + return ret; + } + + usleep_range(WASP_WAIT_SLEEP_US_LOW, WASP_WAIT_SLEEP_US); + + ret = avm_wasp_read_poll_timeout(avmwasp, WASP_WAIT_SLEEP_US, + WASP_WAIT_TIMEOUT_COUNT * + WASP_WAIT_SLEEP_US, + WASP_RESP_READY_TO_START); + if (ret) { + dev_err(dev, "timed out waiting for WASP ready to start\n"); + return ret; + } + + if (m_model == MODEL_3390) + ret = mdiobus_write(mdio_bus, WASP_ADDR, 0x0, + WASP_CMD_START_FIRMWARE_3390); + else if (m_model == MODEL_X490) + ret = mdiobus_write(mdio_bus, WASP_ADDR, 0x0, + WASP_CMD_SET_CHECKSUM_X490); + if (ret < 0) { + dev_err(dev, "writing command failed\n"); + return ret; + } + + usleep_range(WASP_WAIT_SLEEP_US_LOW, WASP_WAIT_SLEEP_US); + + if (m_model == MODEL_3390) { + ret = avm_wasp_read_poll_timeout(avmwasp, WASP_WAIT_SLEEP_US, + WASP_WAIT_TIMEOUT_COUNT * + WASP_WAIT_SLEEP_US, + WASP_RESP_OK); + if (ret) { + dev_err(dev, "timed out waiting for WASP OK\n"); + return ret; + } + if (avm_wasp_netboot_write_chunk(avmwasp, mac_data, + ARRAY_SIZE(mac_data)) < 0) { + dev_err(dev, "error sending MAC address!\n"); + return -EFAULT; + } + } else if (m_model == MODEL_X490) { + while (cont) { + ret = avm_wasp_read_poll_timeout(avmwasp, + WASP_WAIT_SLEEP_US, + WASP_WAIT_TIMEOUT_COUNT * + WASP_WAIT_SLEEP_US, + WASP_RESP_OK); + if (ret) { + dev_err(dev, + "timed out waiting for WASP OK\n"); + return ret; + } + regval = mdiobus_read(mdio_bus, WASP_ADDR, 0x2); + if (regval < 0) { + dev_err(dev, "mdio read failed\n"); + return ret; + } + regval2 = mdiobus_read(mdio_bus, WASP_ADDR, 0x4); + if (regval2 < 0) { + dev_err(dev, "mdio read failed\n"); + return ret; + } + ret = mdiobus_write(mdio_bus, WASP_ADDR, 0x0, + WASP_CMD_SET_CHECKSUM_X490); + if (ret < 0) { + dev_err(dev, "writing command failed\n"); + return ret; + } + + if (regval == 0 && regval2 != 0) + cont = regval2; + else + cont--; + } + + ret = avm_wasp_read_poll_timeout(avmwasp, + WASP_POLL_SLEEP_US, + WASP_TIMEOUT_COUNT * + WASP_POLL_SLEEP_US, + WASP_RESP_OK); + if (ret) { + dev_err(dev, + "error waiting for checksum OK response\n"); + return ret; + } + + ret = mdiobus_write(mdio_bus, WASP_ADDR, 0x2, 0x00); + if (ret < 0) { + dev_err(dev, "mdio write failed\n"); + return ret; + } + ret = mdiobus_write(mdio_bus, WASP_ADDR, 0x0, + WASP_CMD_START_FIRMWARE2_X490); + if (ret < 0) { + dev_err(dev, "writing command failed\n"); + return ret; + } + + regval = mdiobus_read(mdio_bus, WASP_ADDR, 0x0); + if (regval != WASP_RESP_OK) { + dev_err(dev, + "error starting WASP network boot: 0x%x\n", + regval); + return -EFAULT; + } + } + + return 0; +} + +/** + * avm_wasp_load_initramfs_image() - load initramfs image to WASP + * @avmwasp: pointer to drivers private avm_wasp_rproc structure + * + * Uses the lan port specified from DT to load the initramfs to + * WASP after the network boot firmware was successfully started. + * Communication is done by using raw sockets. + * There are several commands and status values which are checked. + * First a discovery packet is received and then each data packet + * is acknowledged by the WASP network boot firmware. + * First packet needs to prepend the load address and last packet + * needs to append the execution address. + * + * Return: 0 on Success, -EFAULT if errors with the WASP send protocol + * have occurred, -EAGAIN if the wasp network interface is down or the + * error returned from the failed operating system function or service + */ +static int avm_wasp_load_initramfs_image(struct avm_wasp_rproc *avmwasp) +{ + bool done = false; + int ret; + u32 num_chunks, chunk_counter; + short interface_flags; + const u8 *firmware, *firmware_end; + struct device *dev = &avmwasp->pdev->dev; + struct kvec socket_kvec; + struct msghdr socket_msghdr; + struct net_device *send_netdev; + struct sockaddr send_sock_addr; + struct sockaddr_ll send_socket_address; + struct socket *wasp_socket; + struct wasp_packet *packet = (struct wasp_packet *) + (avmwasp->buffer); + struct __kernel_old_timeval timeout; + time64_t start_time, current_time; + + if (!avmwasp->linux_blob) { + dev_err(dev, "error accessing initramfs image\n"); + goto err; + } + + firmware = avmwasp->linux_blob->data; + firmware_end = firmware + avmwasp->linux_blob->size; + + ret = sock_create_kern(&init_net, PF_PACKET, SOCK_RAW, + htons(ETH_TYPE_ATH_ECPS_FRAME), + &wasp_socket); + if (ret < 0) { + dev_err(dev, "error opening recv socket: %d\n", ret); + goto err; + } + + timeout.tv_sec = 10; + timeout.tv_usec = 0; + ret = sock_setsockopt(wasp_socket, SOL_SOCKET, SO_RCVTIMEO_OLD, + KERNEL_SOCKPTR(&timeout), sizeof(timeout)); + if (ret < 0) { + dev_err(dev, "error SO_RCVTIMEO recv socket: %d\n", ret); + goto err_socket; + } + + ret = sock_setsockopt(wasp_socket, SOL_SOCKET, SO_SNDTIMEO_OLD, + KERNEL_SOCKPTR(&timeout), sizeof(timeout)); + if (ret < 0) { + dev_err(dev, "error SO_SNDTIMEO send socket: %d\n", ret); + goto err_socket; + } + + rcu_read_lock(); + send_netdev = dev_get_by_name_rcu(sock_net(wasp_socket->sk), + avmwasp->loader_port); + if (send_netdev) + interface_flags = (short)dev_get_flags(send_netdev); + rcu_read_unlock(); + + if (IS_ERR_OR_NULL(send_netdev)) { + dev_err(dev, "error accessing net device\n"); + ret = -ENODEV; + goto err_socket; + } + + if (!(interface_flags & IFF_UP && interface_flags & IFF_RUNNING)) { + dev_err(dev, "error wasp interface %s is down\n", + avmwasp->loader_port); + ret = -EAGAIN; + goto err_socket; + } + + avmwasp->ifindex = send_netdev->ifindex; + ret = dev_get_mac_address(&send_sock_addr, &init_net, + avmwasp->loader_port); + if (ret < 0) { + dev_err(dev, "error getting mac address: %d\n", ret); + goto err_socket; + } + + send_socket_address.sll_halen = ETH_ALEN; + send_socket_address.sll_ifindex = avmwasp->ifindex; + memset(&socket_msghdr, 0, sizeof(socket_msghdr)); + socket_msghdr.msg_name = (struct sockaddr *)&send_socket_address; + socket_msghdr.msg_namelen = sizeof(struct sockaddr_ll); + + start_time = ktime_get_seconds(); + + while (!done) { + current_time = ktime_get_seconds(); + if ((current_time - start_time) > ETH_SEND_LOOP_TIMEOUT_SECS) { + dev_err(dev, + "waiting for packet from WASP timed out\n"); + ret = -ETIMEDOUT; + goto err_socket; + } + + socket_kvec.iov_base = avmwasp->buffer; + socket_kvec.iov_len = ETH_BUF_SIZE; + ret = kernel_recvmsg(wasp_socket, + &socket_msghdr, &socket_kvec, 1, + ETH_BUF_SIZE, 0); + + if (ret < 0) { + dev_err(dev, + "error receiving any packet or timeout: %d\n", + ret); + goto err_socket; + } + + if (ret < (sizeof(struct ethhdr) + sizeof(packet->hdr))) { + dev_err(dev, + "packet too small, discard and continue\n"); + continue; + } + + if (packet->eh.h_proto != ETH_TYPE_ATH_ECPS_FRAME) + continue; + + memcpy(wasp_mac, packet->eh.h_source, sizeof(wasp_mac)); + + if (packet->hdr.packet_start == ETH_WASP_PACKET_ID) { + switch (packet->hdr.response) { + case RESP_DISCOVER: + chunk_counter = 1; + num_chunks = DIV_ROUND_UP(avmwasp->linux_blob->size, + ETH_DATA_SIZE); + fallthrough; + case RESP_OK: + memcpy(packet->eh.h_dest, wasp_mac, sizeof(packet->eh.h_dest)); + packet->eh.h_proto = ETH_TYPE_ATH_ECPS_FRAME; + memcpy(packet->eh.h_source, send_sock_addr.sa_data, + sizeof(packet->eh.h_source)); + + if (firmware < firmware_end) { + size_t bytestosend, send_len; + u32 data_offset = 0; + + if (chunk_counter == 1) { + memcpy(packet->payload, + &m_load_addr, + sizeof(m_load_addr)); + data_offset = sizeof(m_load_addr); + } + + if ((firmware_end - firmware) >= + ETH_DATA_SIZE) + bytestosend = ETH_DATA_SIZE; + else + bytestosend = firmware_end - + firmware; + memcpy(&packet->payload[data_offset], + firmware, bytestosend); + firmware = firmware + ETH_DATA_SIZE; + + packet->hdr.packet_start = + ETH_WASP_PACKET_ID; + if (chunk_counter == num_chunks) { + packet->hdr.response = + CMD_START_FIRMWARE; + memcpy(&packet->payload + [data_offset + bytestosend], + &m_load_addr, + sizeof(m_load_addr)); + bytestosend += sizeof(m_load_addr); + } else { + packet->hdr.command = + CMD_FIRMWARE_DATA; + } + packet->hdr.counter = + (chunk_counter - 1) * 4; + + send_len = sizeof(struct ethhdr) + + sizeof(packet->hdr) + bytestosend + + data_offset; + + socket_kvec.iov_len = send_len; + socket_kvec.iov_base = avmwasp->buffer; + + ret = kernel_sendmsg(wasp_socket, + &socket_msghdr, + &socket_kvec, + 1, send_len); + if (ret < 0) { + dev_err(dev, + "error sending to WASP %d\n", + ret); + goto err_socket; + } + + chunk_counter++; + } + break; + case RESP_ERROR: + dev_err(dev, + "received an WASP error packet!\n"); + ret = -EFAULT; + goto err_socket; + case RESP_STARTING: + done = true; + ret = 0; + continue; + break; + default: + dev_err(dev, "unknown packet! Continue\n"); + continue; + break; + } + } + } + +err_socket: + wasp_socket->ops->release(wasp_socket); +err: + return ret; +} + +/** + * avm_wasp_rproc_start() - start the remote processor + * @rproc: pointer to the rproc structure + * + * Starts the remote processor by turning it on using the startup + * gpio and initiating the reset process using the reset_gpio. + * After that the status is checked if poweron and reset were + * successful. + * As the first step, the network boot firmware is tried to be loaded + * and started. + * As a second step, the initramfs image is tried to be loaded + * and started. + * + * Return: 0 on Success, -ENODEV or return code from the called function + * if any other error occurred in the process of starting and loading + * the firmware files to the WASP processor + */ +static int avm_wasp_rproc_start(struct rproc *rproc) +{ + struct avm_wasp_rproc *avmwasp = rproc->priv; + struct device *dev = &avmwasp->pdev->dev; + u32 phandle; + int ret; + + gpiod_set_value(avmwasp->startup_gpio, 1); + usleep_range(WASP_WAIT_SLEEP_US_LOW, WASP_WAIT_SLEEP_US); + gpiod_set_value(avmwasp->reset_gpio, 0); + usleep_range(WASP_WAIT_SLEEP_US_LOW, WASP_WAIT_SLEEP_US); + gpiod_set_value(avmwasp->reset_gpio, 1); + usleep_range(WASP_WAIT_SLEEP_US_LOW, WASP_WAIT_SLEEP_US); + + ret = request_firmware_direct((const struct firmware **) + &avmwasp->fw_blob, netboot, dev); + if (ret) { + dev_err(dev, "could not load network boot firmware\n"); + goto err; + } + + ret = of_property_read_u32(dev->of_node, "avm,wasp-mdio", &phandle); + if (ret) { + dev_err(dev, "no wasp-netboot-mdio given\n"); + goto err_release_fw; + } else { + struct device_node *mdio_node = + of_find_node_by_phandle(phandle); + + if (!mdio_node) { + dev_err(dev, "get wasp-netboot-mdio failed\n"); + ret = -ENODEV; + goto err_release_fw; + } else { + avmwasp->mdio_bus = of_mdio_find_bus(mdio_node); + of_node_put(mdio_node); + if (!avmwasp->mdio_bus) { + dev_err(dev, "mdio bus not found\n"); + ret = -ENODEV; + goto err_release_fw; + } + } + } + + ret = avm_wasp_netboot_load_firmware(avmwasp); + if (ret) + goto err_put_device; + + ret = avm_wasp_load_initramfs_image(avmwasp); + +err_put_device: + put_device(&avmwasp->mdio_bus->dev); +err_release_fw: + release_firmware(avmwasp->fw_blob); +err: + return ret; +} + +/** + * avm_wasp_rproc_stop() - stop the remote processor + * @rproc: pointer to the rproc structure + * + * To stop the remote processor just the startup gpio is set to 0 + * and the WASP processor is powered off + * + * Return: 0 on Success + */ +static int avm_wasp_rproc_stop(struct rproc *rproc) +{ + struct avm_wasp_rproc *avmwasp = rproc->priv; + + gpiod_set_value(avmwasp->startup_gpio, 0); + + return 0; +} + +/** + * avm_wasp_rproc_load() - noop to avoid the ELF binary defaults + * @rproc: pointer to the rproc structure + * @fw: pointer to firmware struct + * + * If a load function is not defined in the rproc_ops, then all the settings + * like checking the firmware binary will default to ELF checks, which fail + * in case of the bootable and compressed initramfs image for WASP. + * This function stores the initramfs image that is loaded by the remote + * processor framework during boot process into the priv for access by + * the initramfs load function avm_wasp_load_initramfs_image(). + * + * Return: Always 0 + */ +static int avm_wasp_rproc_load(struct rproc *rproc, const struct firmware *fw) +{ + struct avm_wasp_rproc *avmwasp = rproc->priv; + + avmwasp->linux_blob = fw; + + return 0; +} + +static const struct rproc_ops avm_wasp_rproc_ops = { + .start = avm_wasp_rproc_start, + .stop = avm_wasp_rproc_stop, + .load = avm_wasp_rproc_load, +}; + +static int avm_wasp_rproc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct avm_wasp_rproc *avmwasp; + struct rproc *rproc; + struct net_device *netdev; + int ret; + short interface_flags; + const u32 *match_data; + u32 phandle; + + match_data = of_device_get_match_data(dev); + if (IS_ERR_OR_NULL(match_data)) { + dev_err_probe(dev, PTR_ERR(match_data), + "model specific data is not defined\n"); + ret = -ENODEV; + goto err; + } + m_model = *match_data; + + rproc = devm_rproc_alloc(dev, "avm,wasp", &avm_wasp_rproc_ops, + image, sizeof(*avmwasp)); + if (!rproc) { + ret = -ENOMEM; + goto err; + } + + rproc->auto_boot = true; + + avmwasp = rproc->priv; + avmwasp->rproc = rproc; + avmwasp->pdev = pdev; + + ret = request_firmware((const struct firmware **)&avmwasp->fw_blob, + firmware, dev); + if (ret) + dev_err(dev, "could not load ath9k firmware\n"); + release_firmware(avmwasp->fw_blob); + + if (m_model == MODEL_X490) { + ret = request_firmware((const struct firmware **) + &avmwasp->fw_blob, caldata, dev); + if (ret) + dev_err(dev, "could not load ath10k caldata\n"); + release_firmware(avmwasp->fw_blob); + } + + ret = of_property_read_u32(dev->of_node, "avm,wasp-port", + &phandle); + if (ret) { + dev_err(dev, "no wasp-port given\n"); + goto err; + } else { + struct device_node *child = of_find_node_by_phandle(phandle); + + if (!child) { + dev_err(dev, "get wasp-port node failed\n"); + ret = -ENODEV; + goto err; + } else { + ret = of_property_read_string(child, "label", + (const char **) + &avmwasp->loader_port); + of_node_put(child); + if (ret) { + dev_err(dev, "get wasp-port label failed\n"); + goto err; + } + } + } + + rcu_read_lock(); + netdev = dev_get_by_name_rcu(&init_net, avmwasp->loader_port); + if (netdev) + interface_flags = (short)dev_get_flags(netdev); + rcu_read_unlock(); + + if (IS_ERR_OR_NULL(netdev)) { + dev_err_probe(dev, PTR_ERR(netdev), + "error accessing net device\n"); + ret = -ENODEV; + goto err; + } + + if (!(interface_flags & IFF_UP && interface_flags & IFF_RUNNING)) { + dev_err(dev, "error wasp interface %s down\n", + avmwasp->loader_port); + ret = -EPROBE_DEFER; + goto err; + } + + avmwasp->startup_gpio = devm_gpiod_get(dev, "avm,startup", + GPIOD_OUT_LOW); + if (IS_ERR(avmwasp->startup_gpio)) { + ret = dev_err_probe(dev, PTR_ERR(avmwasp->startup_gpio), + "failed to get startup gpio\n"); + goto err; + } + + avmwasp->reset_gpio = devm_gpiod_get(dev, "avm,reset", GPIOD_OUT_LOW); + if (IS_ERR(avmwasp->reset_gpio)) { + ret = dev_err_probe(dev, PTR_ERR(avmwasp->reset_gpio), + "failed to get reset gpio\n"); + goto err; + } + + platform_set_drvdata(pdev, rproc); + + ret = devm_rproc_add(dev, rproc); + if (ret) { + dev_err(dev, "rproc_add failed\n"); + goto err; + } + +err: + return ret; +} + +static int avm_wasp_rproc_remove(struct platform_device *pdev) +{ + struct rproc *rproc = platform_get_drvdata(pdev); + struct avm_wasp_rproc *avmwasp = rproc->priv; + + gpiod_set_value(avmwasp->startup_gpio, 0); + + return 0; +} + +static const u32 model_3390 = MODEL_3390; +static const u32 model_x490 = MODEL_X490; + +static const struct of_device_id avm_wasp_rproc_of_match[] = { + { .compatible = "avm,fritzbox3390-wasp", .data = &model_3390 }, + { .compatible = "avm,fritzboxx490-wasp", .data = &model_x490 }, + {}, +}; +MODULE_DEVICE_TABLE(of, avm_wasp_rproc_of_match); + +static struct platform_driver avm_wasp_rproc_driver = { + .probe = avm_wasp_rproc_probe, + .remove = avm_wasp_rproc_remove, + .driver = { + .name = "avm_wasp_rproc", + .of_match_table = avm_wasp_rproc_of_match, + }, +}; + +module_platform_driver(avm_wasp_rproc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("AVM WASP remote processor boot driver"); +MODULE_AUTHOR("Daniel Kestrel ");