From nobody Wed Sep 17 23:49:03 2025 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 EE7B0C10F1D for ; Tue, 13 Dec 2022 14:11:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235946AbiLMOLg (ORCPT ); Tue, 13 Dec 2022 09:11:36 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36262 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235845AbiLMOK4 (ORCPT ); Tue, 13 Dec 2022 09:10:56 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C85B720F4D for ; Tue, 13 Dec 2022 06:10:16 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id i10-20020a25f20a000000b006ea4f43c0ddso17001555ybe.21 for ; Tue, 13 Dec 2022 06:10:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=WKLCmxMh/0oMa/RFMdPYhTjDsIyBb/o0D8I2JPsPmJ8=; b=hhfLM6HYRp+I/JCShV28bephMf7EC49RiAzuhPMAdKthApzYufcNFgN89M1nuSfAAM FZp/lR24eITT4xSBwF+tjqdVwaS5aOiyzXMvClyToUdg39lj8KsVqZp8Wqur/Nr6Mlik wP8/gXyRqjDlJzfGesUPuglIT+KH1EW9XYXTzrm25V/T2S8vLo0Y10BQpkfV5/KdV+KH dXqTLD2L841VXiys09Dv48cbdKjMmBwwo5N5tytTw1WWaz6bYtfBltAqxgb3EPHXz0Jd MKmXKdvVsLGKCDDX6B5Od/oupTAMrs0yj5nE/IiUyPafNnc02NcCLlOQ5LIkV1YOJMTL nn2g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=WKLCmxMh/0oMa/RFMdPYhTjDsIyBb/o0D8I2JPsPmJ8=; b=LLGM1IIiavi+IIbtc9Yf1fLLUw5A/lbWuMG6oeZrpBxXTGdv3/EhMnLHt1C6MJ5IbC J2pdMvoSyZ2GL/wqpVoaKIIbFeZh7gt180psWPkal2qkUdwXHlQh7d/xyr9CfAPSgDBx 7/KZvvFVVUB9OojVy0+p/ui5gBiV1n3kJyKoLDCwbY9i65JA0RjnGHaqAE5qMG6TLJNK KdMuXjSQNWWVIAjSJP/e/zfpLo9o/+d3EkRxDRIJa16OJ6OySfDjiURRzGKrF6HkOjLf 7CwPNGXMYALwu2zmP1kEnTtl9A7HOr1vzN1zNBiihEWNVnPPkA4+McvLEQu9/CtFv6KL OY/A== X-Gm-Message-State: ANoB5pkufFssS0nGV2YctJCMoyt3SOfAB5y2Mc/WFtkFF+iSV/EPVEJy ZXlxW7+jdLvqq3b+yXEm7a2ky1ixpn7eWOUTPtA= X-Google-Smtp-Source: AA0mqf7iy16bpZk3PK9QwspGUSN9cHuL3KxoEc71DdPs2hEYldBBusN0+Ok41KEWot0IF5cCf+h6u2Rw/lsFIXcCi5I= X-Received: from albertccwang.c.googlers.com ([fda3:e722:ac3:cc00:3:22c1:c0a8:83d]) (user=albertccwang job=sendgmr) by 2002:a25:cc0b:0:b0:6f3:f978:efaf with SMTP id l11-20020a25cc0b000000b006f3f978efafmr56711427ybf.596.1670940616095; Tue, 13 Dec 2022 06:10:16 -0800 (PST) Date: Tue, 13 Dec 2022 14:10:03 +0000 In-Reply-To: <20221213141005.3068792-1-albertccwang@google.com> Mime-Version: 1.0 References: <20221213141005.3068792-1-albertccwang@google.com> X-Mailer: git-send-email 2.39.0.rc1.256.g54fd8350bd-goog Message-ID: <20221213141005.3068792-2-albertccwang@google.com> Subject: [PATCH v3 1/3] usb: host: add xhci hooks for USB offload From: Albert Wang To: mathias.nyman@intel.com, gregkh@linuxfoundation.org Cc: badhri@google.com, howardyen@google.com, pumahsu@google.com, raychi@google.com, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Howard Yen This change is to provide USB offload function which allows to offload some xHCI operations on co-processor. This is especially designed for USB audio usecase. The co-processor is able to manipulate some USB structures in his own memory, like SRAM. There are several offload_ops introduced by this patch: struct xhci_offload_ops - function callbacks for offlad specific operations { @offload_init: - called for vendor init process during xhci-plat-hcd probe. @offload_cleanup: - called for vendor cleanup process during xhci-plat-hcd remove. @is_usb_offload_enabled: - called to check if usb offload enabled. @alloc_dcbaa: - called when allocating vendor specific dcbaa during memory initializtion. @free_dcbaa: - called to free vendor specific dcbaa when cleanup the memory. @alloc_transfer_ring: - called when vendor specific transfer ring allocation is required @free_transfer_ring: - called to free vendor specific transfer ring @usb_offload_skip_urb: - called to check if need to skip urb enqueue } The xhci hooks with prefix "xhci_vendor_" on the ops in xhci_offload_ops. For example, offload_init ops will be invoked by xhci_vendor_offload_init() hook,is_usb_offload_enabled ops will be invoked by xhci_vendor_is_usb_offload_enabled(), and so on. Signed-off-by: Howard Yen --- drivers/usb/host/xhci-mem.c | 97 +++++++++++++++++++++++++++++++----- drivers/usb/host/xhci-plat.c | 23 +++++++++ drivers/usb/host/xhci-plat.h | 1 + drivers/usb/host/xhci.c | 21 ++++++++ drivers/usb/host/xhci.h | 31 ++++++++++++ 5 files changed, 160 insertions(+), 13 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 81ca2bc1f0be..ab0ef19d4fa3 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -361,6 +361,38 @@ static int xhci_alloc_segments_for_ring(struct xhci_hc= d *xhci, return 0; } =20 +static struct xhci_ring *xhci_vendor_alloc_transfer_ring(struct xhci_hcd *= xhci, + u32 endpoint_type, enum xhci_ring_type ring_type, + unsigned int max_packet, gfp_t mem_flags) +{ + struct xhci_offload_ops *ops =3D xhci_offload_get_ops(xhci); + + if (ops && ops->alloc_transfer_ring) + return ops->alloc_transfer_ring(xhci, endpoint_type, ring_type, + max_packet, mem_flags); + return 0; +} + +static void xhci_vendor_free_transfer_ring(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, unsigned int ep_index) +{ + struct xhci_offload_ops *ops =3D xhci_offload_get_ops(xhci); + + if (ops && ops->free_transfer_ring) + ops->free_transfer_ring(xhci, virt_dev, ep_index); +} + +static bool xhci_vendor_is_offload_enabled(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, unsigned int ep_index) +{ + struct xhci_offload_ops *ops =3D xhci_offload_get_ops(xhci); + + if (ops && ops->is_offload_enabled) + return ops->is_offload_enabled(xhci, virt_dev, ep_index); + + return false; +} + /* * Create a new ring with zero or more segments. * @@ -412,7 +444,11 @@ void xhci_free_endpoint_ring(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, unsigned int ep_index) { - xhci_ring_free(xhci, virt_dev->eps[ep_index].ring); + if (xhci_vendor_is_offload_enabled(xhci, virt_dev, ep_index)) + xhci_vendor_free_transfer_ring(xhci, virt_dev, ep_index); + else + xhci_ring_free(xhci, virt_dev->eps[ep_index].ring); + virt_dev->eps[ep_index].ring =3D NULL; } =20 @@ -885,7 +921,7 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int s= lot_id) =20 for (i =3D 0; i < 31; i++) { if (dev->eps[i].ring) - xhci_ring_free(xhci, dev->eps[i].ring); + xhci_free_endpoint_ring(xhci, dev, i); if (dev->eps[i].stream_info) xhci_free_stream_info(xhci, dev->eps[i].stream_info); @@ -1487,8 +1523,16 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, mult =3D 0; =20 /* Set up the endpoint ring */ - virt_dev->eps[ep_index].new_ring =3D - xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags); + if (xhci_vendor_is_offload_enabled(xhci, virt_dev, ep_index) && + usb_endpoint_xfer_isoc(&ep->desc)) { + virt_dev->eps[ep_index].new_ring =3D + xhci_vendor_alloc_transfer_ring(xhci, endpoint_type, ring_type, + max_packet, mem_flags); + } else { + virt_dev->eps[ep_index].new_ring =3D + xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags); + } + if (!virt_dev->eps[ep_index].new_ring) return -ENOMEM; =20 @@ -1832,6 +1876,23 @@ void xhci_free_erst(struct xhci_hcd *xhci, struct xh= ci_erst *erst) erst->entries =3D NULL; } =20 +static void xhci_vendor_alloc_dcbaa( + struct xhci_hcd *xhci, gfp_t flags) +{ + struct xhci_offload_ops *ops =3D xhci_offload_get_ops(xhci); + + if (ops && ops->alloc_dcbaa) + return ops->alloc_dcbaa(xhci, flags); +} + +static void xhci_vendor_free_dcbaa(struct xhci_hcd *xhci) +{ + struct xhci_offload_ops *ops =3D xhci_offload_get_ops(xhci); + + if (ops && ops->free_dcbaa) + ops->free_dcbaa(xhci); +} + void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev =3D xhci_to_hcd(xhci)->self.sysdev; @@ -1883,9 +1944,13 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed medium stream array pool"); =20 - if (xhci->dcbaa) - dma_free_coherent(dev, sizeof(*xhci->dcbaa), - xhci->dcbaa, xhci->dcbaa->dma); + if (xhci_vendor_is_offload_enabled(xhci, NULL, 0)) { + xhci_vendor_free_dcbaa(xhci); + } else { + if (xhci->dcbaa) + dma_free_coherent(dev, sizeof(*xhci->dcbaa), + xhci->dcbaa, xhci->dcbaa->dma); + } xhci->dcbaa =3D NULL; =20 scratchpad_free(xhci); @@ -2422,15 +2487,21 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flag= s) * xHCI section 5.4.6 - Device Context array must be * "physically contiguous and 64-byte (cache line) aligned". */ - xhci->dcbaa =3D dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, - flags); - if (!xhci->dcbaa) - goto fail; - xhci->dcbaa->dma =3D dma; + if (xhci_vendor_is_offload_enabled(xhci, NULL, 0)) { + xhci_vendor_alloc_dcbaa(xhci, flags); + if (!xhci->dcbaa) + goto fail; + } else { + xhci->dcbaa =3D dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, + flags); + if (!xhci->dcbaa) + goto fail; + xhci->dcbaa->dma =3D dma; + } xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Device context base array address =3D 0x%llx (DMA), %p (virt)", (unsigned long long)xhci->dcbaa->dma, xhci->dcbaa); - xhci_write_64(xhci, dma, &xhci->op_regs->dcbaa_ptr); + xhci_write_64(xhci, xhci->dcbaa->dma, &xhci->op_regs->dcbaa_ptr); =20 /* * Initialize the ring segment pool. The ring must be a contiguous diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 5fb55bf19493..2f04acb42fa6 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -173,6 +173,23 @@ static const struct of_device_id usb_xhci_of_match[] = =3D { MODULE_DEVICE_TABLE(of, usb_xhci_of_match); #endif =20 +static int xhci_vendor_init(struct xhci_hcd *xhci) +{ + struct xhci_offload_ops *ops =3D xhci_offload_get_ops(xhci); + + if (ops && ops->offload_init) + return ops->offload_init(xhci); + return 0; +} + +static void xhci_vendor_cleanup(struct xhci_hcd *xhci) +{ + struct xhci_offload_ops *ops =3D xhci_offload_get_ops(xhci); + + if (ops && ops->offload_cleanup) + ops->offload_cleanup(xhci); +} + static int xhci_plat_probe(struct platform_device *pdev) { const struct xhci_plat_priv *priv_match; @@ -317,6 +334,10 @@ static int xhci_plat_probe(struct platform_device *pde= v) goto disable_clk; } =20 + ret =3D xhci_vendor_init(xhci); + if (ret) + goto disable_usb_phy; + hcd->tpl_support =3D of_usb_host_tpl_support(sysdev->of_node); =20 if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT)) @@ -410,6 +431,8 @@ static int xhci_plat_remove(struct platform_device *dev) if (shared_hcd) usb_put_hcd(shared_hcd); =20 + xhci_vendor_cleanup(xhci); + clk_disable_unprepare(clk); clk_disable_unprepare(reg_clk); usb_put_hcd(hcd); diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h index 1fb149d1fbce..5aa0d38fa01a 100644 --- a/drivers/usb/host/xhci-plat.h +++ b/drivers/usb/host/xhci-plat.h @@ -13,6 +13,7 @@ struct xhci_plat_priv { const char *firmware_name; unsigned long long quirks; + struct xhci_offload_ops *offload_ops; void (*plat_start)(struct usb_hcd *); int (*init_quirk)(struct usb_hcd *); int (*suspend_quirk)(struct usb_hcd *); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 79d7931c048a..75d39fe0d44d 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -22,6 +22,7 @@ #include "xhci-trace.h" #include "xhci-debugfs.h" #include "xhci-dbgcap.h" +#include "xhci-plat.h" =20 #define DRIVER_AUTHOR "Sarah Sharp" #define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver" @@ -1669,6 +1670,11 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, str= uct urb *urb, gfp_t mem_flag return -ENODEV; } =20 + if (xhci_vendor_usb_offload_skip_urb(xhci, urb)) { + xhci_dbg(xhci, "skip urb for usb offload\n"); + return -EOPNOTSUPP; + } + if (usb_endpoint_xfer_isoc(&urb->ep->desc)) num_tds =3D urb->number_of_packets; else if (usb_endpoint_is_bulk_out(&urb->ep->desc) && @@ -4441,6 +4447,21 @@ static int __maybe_unused xhci_change_max_exit_laten= cy(struct xhci_hcd *xhci, return ret; } =20 +struct xhci_offload_ops *xhci_offload_get_ops(struct xhci_hcd *xhci) +{ + return xhci_to_priv(xhci)->offload_ops; +} +EXPORT_SYMBOL_GPL(xhci_offload_get_ops); + +bool xhci_vendor_usb_offload_skip_urb(struct xhci_hcd *xhci, struct urb *u= rb) +{ + struct xhci_offload_ops *ops =3D xhci_offload_get_ops(xhci); + + if (ops && ops->usb_offload_skip_urb) + return ops->usb_offload_skip_urb(xhci, urb); + return false; +} + #ifdef CONFIG_PM =20 /* BESL to HIRD Encoding array for USB2 LPM */ diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index c9f06c5e4e9d..4ffcc8f01bd7 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -2229,6 +2229,37 @@ static inline struct xhci_ring *xhci_urb_to_transfer= _ring(struct xhci_hcd *xhci, urb->stream_id); } =20 +/** + * struct xhci_offload_ops - function callbacks for offload specific opera= tions + * @offload_init: called for offload init process + * @offload_cleanup: called for offload cleanup process + * @is_usb_offload_enabled: called to check if xhci offload enabled + * @alloc_dcbaa: called when allocating specific dcbaa for offload + * @free_dcbaa: called to free specific dcbaa for offload + * @alloc_transfer_ring: called when remote transfer ring allocation is re= quired + * @free_transfer_ring: called to free specific transfer ring for offload + * @usb_offload_skip_urb: called to check if need to skip urb + */ +struct xhci_offload_ops { + int (*offload_init)(struct xhci_hcd *xhci); + void (*offload_cleanup)(struct xhci_hcd *xhci); + bool (*is_offload_enabled)(struct xhci_hcd *xhci, + struct xhci_virt_device *vdev, + unsigned int ep_index); + void (*alloc_dcbaa)(struct xhci_hcd *xhci, gfp_t flags); + void (*free_dcbaa)(struct xhci_hcd *xhci); + + struct xhci_ring *(*alloc_transfer_ring)(struct xhci_hcd *xhci, + u32 endpoint_type, enum xhci_ring_type ring_type, + unsigned int max_packet, gfp_t mem_flags); + void (*free_transfer_ring)(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, unsigned int ep_index); + bool (*usb_offload_skip_urb)(struct xhci_hcd *xhci, struct urb *urb); +}; + +struct xhci_offload_ops *xhci_offload_get_ops(struct xhci_hcd *xhci); +bool xhci_vendor_usb_offload_skip_urb(struct xhci_hcd *xhci, struct urb *u= rb); + /* * TODO: As per spec Isochronous IDT transmissions are supported. We bypass * them anyways as we where unable to find a device that matches the --=20 2.39.0.rc1.256.g54fd8350bd-goog From nobody Wed Sep 17 23:49:03 2025 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 AF518C4332F for ; Tue, 13 Dec 2022 14:12:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235838AbiLMOMN (ORCPT ); Tue, 13 Dec 2022 09:12:13 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37098 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235342AbiLMOLV (ORCPT ); Tue, 13 Dec 2022 09:11:21 -0500 Received: from mail-pf1-x449.google.com (mail-pf1-x449.google.com [IPv6:2607:f8b0:4864:20::449]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CCA6421255 for ; Tue, 13 Dec 2022 06:10:34 -0800 (PST) Received: by mail-pf1-x449.google.com with SMTP id z13-20020aa79f8d000000b00576b614b7d2so2130168pfr.14 for ; Tue, 13 Dec 2022 06:10:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=MKI07Bx0MT4z42ihUE6Ms1XD+dup6KrK+Vea+qzaT8o=; b=rY5gG2Ir0bwLKC240UeBvFVur1c/4YpMgDS5iaNAtxBXer2SEpIADomFcS+TypZdlC gkeQIaiS6z3TZbTHAdjZYch55Z0DDUnWvLdburjIXT3n4wIrOxklAXRDQLtTcMVXFlC8 ECbfIoJ6uxcYjlXlATeMxQdIbGwgdyN2548vWVmy1rpRFVIxVHaM27dfDgfU1zunkSlv Izd9BwW3HOj0bl0Cyxj4pASAwO845iNzolWve6lbUGi0c4UGaVUrhzAW7+oTcXwb/igD +WvSHbrLdVC8gTQvF/WDucwaK+1nDs7notdjny0Qh2WTFf6oDccsp1rNNPYl6STK9Pz9 hd7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=MKI07Bx0MT4z42ihUE6Ms1XD+dup6KrK+Vea+qzaT8o=; b=zfP4T3oR8cgF30hUt/tJwyynI6Cc9wx2kAijQt7A8ymZIlfpU5/KQ15jF8WY0AUYtf HL6Vi6EvwafCqJ4w5+QYT8p68QOsuu/pKWnyIkBHWJjQuqa+gJsBYr90+mNC+wRULZlU E2yd5dW+rKRz9vzyHKMgUkkOoDX1RS4Yq7lntzk1u5oKNrC8qSjRyGy18FQ7qBaeaJ/C xSe71WdMtnoRGZGSwM+b63TMZi4VxqEAFlAkxfSjNpkDpkP0XSpwuB3Gt90GxTmr2BZx n/DF49ts6PrMW+5A2vHUstxMuhGQaOvJi9lANIm2oYGO/x7m/QsPMlAGP66h9c0/NjoB jRPQ== X-Gm-Message-State: ANoB5pkutb+47u3WK0PXz2nk0jS3XxOkOy5oryBKeIMDuYZ1TDXhIRT0 Vc0Mi69oylES6mVAoE+izqhuD9bwKITAz/Iftv4= X-Google-Smtp-Source: AA0mqf4ED9AjZvtuotnnanTk1xUkXF09so4t5LMDhw646LjDd+sbIc63QDGMrIyBbxoWYh7eG4Q5e367VTb2GwrDLJE= X-Received: from albertccwang.c.googlers.com ([fda3:e722:ac3:cc00:3:22c1:c0a8:83d]) (user=albertccwang job=sendgmr) by 2002:a17:90a:8407:b0:21a:150d:fe63 with SMTP id j7-20020a17090a840700b0021a150dfe63mr446633pjn.73.1670940634139; Tue, 13 Dec 2022 06:10:34 -0800 (PST) Date: Tue, 13 Dec 2022 14:10:04 +0000 In-Reply-To: <20221213141005.3068792-1-albertccwang@google.com> Mime-Version: 1.0 References: <20221213141005.3068792-1-albertccwang@google.com> X-Mailer: git-send-email 2.39.0.rc1.256.g54fd8350bd-goog Message-ID: <20221213141005.3068792-3-albertccwang@google.com> Subject: [PATCH v3 2/3] usb: xhci-plat: add xhci_plat_priv_overwrite From: Albert Wang To: mathias.nyman@intel.com, gregkh@linuxfoundation.org Cc: badhri@google.com, howardyen@google.com, pumahsu@google.com, raychi@google.com, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Howard Yen Add an overwrite to platform specific callback for setting up the xhci_offload_ops, allow vendor to store the xhci_offload_ops and overwrite them when xhci_plat_probe invoked. Signed-off-by: Howard Yen --- drivers/usb/host/xhci-plat.c | 20 ++++++++++++++++++++ drivers/usb/host/xhci-plat.h | 7 +++++++ 2 files changed, 27 insertions(+) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 2f04acb42fa6..11ff89f722b7 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -173,9 +173,26 @@ static const struct of_device_id usb_xhci_of_match[] = =3D { MODULE_DEVICE_TABLE(of, usb_xhci_of_match); #endif =20 +static struct xhci_plat_priv_overwrite xhci_plat_vendor_overwrite; + +int xhci_plat_register_offload_ops(struct xhci_offload_ops *offload_ops) +{ + if (offload_ops =3D=3D NULL) + return -EINVAL; + + xhci_plat_vendor_overwrite.offload_ops =3D offload_ops; + + return 0; +} +EXPORT_SYMBOL_GPL(xhci_plat_register_offload_ops); + static int xhci_vendor_init(struct xhci_hcd *xhci) { struct xhci_offload_ops *ops =3D xhci_offload_get_ops(xhci); + struct xhci_plat_priv *priv =3D xhci_to_priv(xhci); + + if (xhci_plat_vendor_overwrite.offload_ops) + ops =3D priv->offload_ops =3D xhci_plat_vendor_overwrite.offload_ops; =20 if (ops && ops->offload_init) return ops->offload_init(xhci); @@ -185,9 +202,12 @@ static int xhci_vendor_init(struct xhci_hcd *xhci) static void xhci_vendor_cleanup(struct xhci_hcd *xhci) { struct xhci_offload_ops *ops =3D xhci_offload_get_ops(xhci); + struct xhci_plat_priv *priv =3D xhci_to_priv(xhci); =20 if (ops && ops->offload_cleanup) ops->offload_cleanup(xhci); + + priv->offload_ops =3D NULL; } =20 static int xhci_plat_probe(struct platform_device *pdev) diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h index 5aa0d38fa01a..0656d6daa194 100644 --- a/drivers/usb/host/xhci-plat.h +++ b/drivers/usb/host/xhci-plat.h @@ -22,4 +22,11 @@ struct xhci_plat_priv { =20 #define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv) #define xhci_to_priv(x) ((struct xhci_plat_priv *)(x)->priv) + +struct xhci_plat_priv_overwrite { + struct xhci_offload_ops *offload_ops; +}; + +int xhci_plat_register_offload_ops(struct xhci_offload_ops *offload_ops); + #endif /* _XHCI_PLAT_H */ --=20 2.39.0.rc1.256.g54fd8350bd-goog From nobody Wed Sep 17 23:49:03 2025 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 79E25C4332F for ; Tue, 13 Dec 2022 14:12:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235266AbiLMOMQ (ORCPT ); Tue, 13 Dec 2022 09:12:16 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36218 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235864AbiLMOLr (ORCPT ); Tue, 13 Dec 2022 09:11:47 -0500 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E88442188C for ; Tue, 13 Dec 2022 06:10:43 -0800 (PST) Received: by mail-yb1-xb4a.google.com with SMTP id t9-20020a5b03c9000000b006cff5077dc9so16705630ybp.3 for ; Tue, 13 Dec 2022 06:10:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=IazsSpG7fcgPawhilo+Akx8DgvbU0pRdUVn94g1nQSg=; b=ZFDLWcExas1fbQAmMKyGhT3OkbQxOQaEdfriBTS6JdZQI00n/GQbuN66Lco5XImtjT kZIESnkH67i2T9PFNraNCKHUpMCXy8YU8SM3W2w44yU1e74PD6FMPXW4Datn8jzdqZh6 qc5wAFe6Od8GC7LU52GGwuQJpQpjACC8RKyeacKMWHSWg2cHJ9PG7o5TPpx5Csee1j+e LFHjIEfQXKc4iy7RRb8uvGdFFlNWyfnb+nMkD+/Gji6Czb34n7DpG2iSdCmXBLjVoYdR chlU38qpEI2YaSfu5jMANepvAtfASD3QZy+zDCQCCdN73mSHs5VwgFP36rY9ixjoC4iA XpCA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=IazsSpG7fcgPawhilo+Akx8DgvbU0pRdUVn94g1nQSg=; b=yAGUJQ6yqrDe9apQ2gWBNP8q+aCTj/Q+L0hoZ2M6/RAEGAYkxZgADDhc4pWQ9NQFuD Ti6tAakNei4c4e6bXb8INmxmtKKS3vX53Kqx7vsM2wXMzCFH3An6zpyJJlB2YqDpZC4b FEHJwFw22la32YSWrPHS95+BA1C2F7iCvPZ8Inxlj+KUCMQv/nhqdR6XqkQ8w9kNyC5Y LObySJyCTXf4fHHAMZj4dL7PzIvRooDM31ZQ/0uDw+KR6GoveRV+rZAzFTKaxeq1aSpU 9Ays8o4PelwD7iLQX3TE2dU5OQg99HUtrPLmCLy/Ud7hCSYn2RVlRL9mRhyJJPrIc0eS ke6w== X-Gm-Message-State: ANoB5pkYVDJOsYFiaynkZkgM37y8uPBP0vI7vc366CEiSR8nAVWxSs9J W3xjpHfp9K+QetPUJBiT3Wj65x1ZjtcKR5z2M+I= X-Google-Smtp-Source: AA0mqf6bZq5lk2o5ROtjASap72pHyN/9rHcvIX+dtcfadEo7eZVnw77Tk3X65zw7lM6/NbOHmEqZUmyw/5I8+TA28cQ= X-Received: from albertccwang.c.googlers.com ([fda3:e722:ac3:cc00:3:22c1:c0a8:83d]) (user=albertccwang job=sendgmr) by 2002:a25:2d44:0:b0:6f0:6dfa:c762 with SMTP id s4-20020a252d44000000b006f06dfac762mr64433088ybe.109.1670940643201; Tue, 13 Dec 2022 06:10:43 -0800 (PST) Date: Tue, 13 Dec 2022 14:10:05 +0000 In-Reply-To: <20221213141005.3068792-1-albertccwang@google.com> Mime-Version: 1.0 References: <20221213141005.3068792-1-albertccwang@google.com> X-Mailer: git-send-email 2.39.0.rc1.256.g54fd8350bd-goog Message-ID: <20221213141005.3068792-4-albertccwang@google.com> Subject: [PATCH v3 3/3] usb: host: add the xhci offload hooks implementations From: Albert Wang To: mathias.nyman@intel.com, gregkh@linuxfoundation.org Cc: badhri@google.com, howardyen@google.com, pumahsu@google.com, raychi@google.com, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org, Albert Wang Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the offload hooks implementations which are used in the xHCI driver for vendor offload function, and some functions will call to co-processor driver for further offload operations. Signed-off-by: Albert Wang Signed-off-by: Howard Yen --- Changes in v3: - Add new implementation driver file in v3 --- drivers/usb/host/aoc-usb.c | 198 ++++++++++++++ drivers/usb/host/aoc-usb.h | 108 ++++++++ drivers/usb/host/xhci-offload-impl.c | 396 +++++++++++++++++++++++++++ 3 files changed, 702 insertions(+) create mode 100644 drivers/usb/host/aoc-usb.c create mode 100644 drivers/usb/host/aoc-usb.h create mode 100644 drivers/usb/host/xhci-offload-impl.c diff --git a/drivers/usb/host/aoc-usb.c b/drivers/usb/host/aoc-usb.c new file mode 100644 index 000000000000..b51f26755a63 --- /dev/null +++ b/drivers/usb/host/aoc-usb.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright 2022 Google LLC. All Rights Reserved. + * + * Interface to the AoC USB control service + */ + +#include +#include +#include +#include +#include + +#include "aoc-usb.h" + +#define AOC_USB_NAME "aoc_usb" + +extern struct blocking_notifier_head aoc_usb_notifier_list; + +int xhci_set_offload_state(bool enabled) +{ + return blocking_notifier_call_chain(&aoc_usb_notifier_list, SET_OFFLOAD_S= TATE, &enabled); +} + +int xhci_set_dcbaa_ptr(u64 aoc_dcbaa_ptr) +{ + return blocking_notifier_call_chain(&aoc_usb_notifier_list, SET_DCBAA_PTR= , &aoc_dcbaa_ptr); +} + +int xhci_setup_done(void) +{ + return blocking_notifier_call_chain(&aoc_usb_notifier_list, SETUP_DONE, N= ULL); +} + +int xhci_sync_conn_stat(unsigned int bus_id, unsigned int dev_num, unsigne= d int slot_id, + unsigned int conn_stat) +{ + struct conn_stat_args args; + + args.bus_id =3D bus_id; + args.dev_num =3D dev_num; + args.slot_id =3D slot_id; + args.conn_stat =3D conn_stat; + + return blocking_notifier_call_chain(&aoc_usb_notifier_list, SYNC_CONN_STA= T, &args); +} + +int usb_host_mode_state_notify(enum usb_conn_state usb_state) +{ + return xhci_sync_conn_stat(0, 0, 0, usb_state); +} + +int xhci_set_isoc_tr_info(u16 ep_id, u16 dir, struct xhci_ring *ep_ring) +{ + struct get_isoc_tr_info_args tr_info; + + tr_info.ep_id =3D ep_id; + tr_info.dir =3D dir; + tr_info.num_segs =3D ep_ring->num_segs; + tr_info.max_packet =3D ep_ring->bounce_buf_len; + tr_info.type =3D ep_ring->type; + tr_info.seg_ptr =3D ep_ring->first_seg->dma; + tr_info.cycle_state =3D ep_ring->cycle_state; + tr_info.num_trbs_free =3D ep_ring->num_trbs_free; + + return blocking_notifier_call_chain(&aoc_usb_notifier_list, SET_ISOC_TR_I= NFO, &tr_info); +} + +static int aoc_usb_match(struct device *dev, void *data) +{ + if (sysfs_streq(dev_driver_string(dev), "xhci-hcd-exynos")) + return 1; + + return 0; +} + +static bool aoc_usb_is_hcd_working(void) +{ + struct device_node *np; + struct platform_device *pdev; + struct device *udev; + int ret; + + np =3D of_find_node_by_name(NULL, "dwc3"); + if (!np || !of_device_is_available(np)) { + pr_err("Cannot find dwc3 device node\n"); + return false; + } + + pdev =3D of_find_device_by_node(np); + if (!pdev) + return false; + + udev =3D device_find_child(&pdev->dev, NULL, aoc_usb_match); + if (!udev) + return false; + + ret =3D usb_host_mode_state_notify(USB_CONNECTED); + if (ret) + dev_err(udev, "Notifying AoC for xhci driver status is failed.\n"); + + return true; +} + +static struct work_struct usb_host_mode_checking_ws; +static void usb_host_mode_checking_work(struct work_struct *ws) +{ + if (aoc_usb_is_hcd_working()) + pr_info("USB HCD is working, send notification to AoC\n"); +} + +/* + * This variable used to present if aoc_usb module was probed done. If off= load + * is enabled, the controller needs to wait for the aoc_usb probe done and= then + * continue the controller's probe. + */ +static bool aoc_usb_probe_done; +static int aoc_usb_probe(struct aoc_service_dev *adev) +{ + struct device *dev =3D &adev->dev; + struct aoc_usb_drvdata *drvdata; + + drvdata =3D kzalloc(sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->adev =3D adev; + + mutex_init(&drvdata->lock); + + drvdata->ws =3D wakeup_source_register(dev, dev_name(dev)); + if (!drvdata->ws) { + dev_err(&drvdata->adev->dev, "wakeup_source_register failed!\n"); + return -ENOMEM; + } + + drvdata->usb_conn_state =3D 0; + dev_set_drvdata(dev, drvdata); + + aoc_usb_probe_done =3D true; + + schedule_work(&usb_host_mode_checking_ws); + + return 0; +} + +static int aoc_usb_remove(struct aoc_service_dev *adev) +{ + struct aoc_usb_drvdata *drvdata =3D dev_get_drvdata(&adev->dev); + + wakeup_source_unregister(drvdata->ws); + mutex_destroy(&drvdata->lock); + + kfree(drvdata); + + aoc_usb_probe_done =3D false; + + return 0; +} + +bool is_aoc_usb_probe_done(void) +{ + return aoc_usb_probe_done; +} + +static const char *const aoc_usb_service_names[] =3D { + "usb_control", + NULL, +}; + +static struct aoc_driver aoc_usb_driver =3D { + .drv =3D { + .name =3D AOC_USB_NAME, + }, + .service_names =3D aoc_usb_service_names, + .probe =3D aoc_usb_probe, + .remove =3D aoc_usb_remove, +}; + +static int __init aoc_usb_init(void) +{ + xhci_offload_helper_init(); + + INIT_WORK(&usb_host_mode_checking_ws, usb_host_mode_checking_work); + + return driver_register(&aoc_usb_driver.drv); +} + +static void __exit aoc_usb_exit(void) +{ + driver_unregister(&aoc_usb_driver.drv); +} + +module_init(aoc_usb_init); +module_exit(aoc_usb_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Howard Yen (Google)"); +MODULE_DESCRIPTION("USB driver for AoC"); diff --git a/drivers/usb/host/aoc-usb.h b/drivers/usb/host/aoc-usb.h new file mode 100644 index 000000000000..a756abd00d01 --- /dev/null +++ b/drivers/usb/host/aoc-usb.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Google Corp. + * + * Author: + * Howard.Yen + */ + +#ifndef __LINUX_AOC_USB_H +#define __LINUX_AOC_USB_H + +#include "xhci.h" + +struct aoc_service_dev { + struct device dev; + wait_queue_head_t read_queue; + wait_queue_head_t write_queue; + + void *ipc_base; + void *prvdata; + uint64_t suspend_rx_count; + + uint8_t mbox_index; + uint8_t service_index; + + bool dead; + bool wake_capable; +}; + +struct aoc_driver { + struct device_driver drv; + + /* Array of service names to match against. Last entry must be NULL */ + const char * const *service_names; + int (*probe)(struct aoc_service_dev *dev); + int (*remove)(struct aoc_service_dev *dev); +}; + +enum usb_offload_msg { + SET_DCBAA_PTR, + GET_TR_DEQUEUE_PTR, + SETUP_DONE, + SET_ISOC_TR_INFO, + SYNC_CONN_STAT, + SET_OFFLOAD_STATE +}; + +enum usb_offload_op_mode { + USB_OFFLOAD_STOP, + USB_OFFLOAD_DRAM +}; + +enum usb_conn_state { + USB_DISCONNECTED, + USB_CONNECTED +}; + +enum usb_recover_state { + NONE, + RECOVER_HOST_OFF, + RECOVER_HOST_ON, + RECOVERED +}; + +struct aoc_usb_drvdata { + struct aoc_service_dev *adev; + + struct mutex lock; + struct wakeup_source *ws; + + struct notifier_block nb; + + unsigned int usb_conn_state; +}; + +struct conn_stat_args { + u16 bus_id; + u16 dev_num; + u16 slot_id; + u32 conn_stat; +}; + +struct get_isoc_tr_info_args { + u16 ep_id; + u16 dir; + u32 type; + u32 num_segs; + u32 seg_ptr; + u32 max_packet; + u32 deq_ptr; + u32 enq_ptr; + u32 cycle_state; + u32 num_trbs_free; +}; + +int xhci_set_offload_state(bool enabled); +int xhci_set_dcbaa_ptr(u64 aoc_dcbaa_ptr); +int xhci_setup_done(void); +int xhci_sync_conn_stat(unsigned int bus_id, unsigned int dev_num, unsigne= d int slot_id, + unsigned int conn_stat); +int usb_host_mode_state_notify(enum usb_conn_state usb_state); +int xhci_set_isoc_tr_info(u16 ep_id, u16 dir, struct xhci_ring *ep_ring); + +bool is_aoc_usb_probe_done(void); + +int xhci_offload_helper_init(void); + +#endif /* __LINUX_AOC_USB_H */ diff --git a/drivers/usb/host/xhci-offload-impl.c b/drivers/usb/host/xhci-o= ffload-impl.c new file mode 100644 index 000000000000..738595382f7e --- /dev/null +++ b/drivers/usb/host/xhci-offload-impl.c @@ -0,0 +1,396 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Google Corp. + * + * Author: + * Howard.Yen + */ + +#include +#include +#include +#include +#include + +#include "aoc-usb.h" +#include "xhci-plat.h" + +struct xhci_offload_data { + struct xhci_hcd *xhci; + + bool usb_accessory_enabled; + bool usb_audio_offload; + bool dt_direct_usb_access; + bool offload_state; + + enum usb_offload_op_mode op_mode; +}; + +static struct xhci_offload_data *offload_data; +struct xhci_offload_data *xhci_get_offload_data(void) +{ + return offload_data; +} + +/* + * Determine if an USB device is a compatible devices: + * True: Devices are audio class and they contain ISOC endpoint + * False: Devices are not audio class or they're audio class but no ISO= C endpoint or + * they have at least one interface is video class + */ +static bool is_compatible_with_usb_audio_offload(struct usb_device *udev) +{ + struct usb_endpoint_descriptor *epd; + struct usb_host_config *config; + struct usb_host_interface *alt; + struct usb_interface_cache *intfc; + int i, j, k; + bool is_audio =3D false; + + config =3D udev->config; + for (i =3D 0; i < config->desc.bNumInterfaces; i++) { + intfc =3D config->intf_cache[i]; + for (j =3D 0; j < intfc->num_altsetting; j++) { + alt =3D &intfc->altsetting[j]; + + if (alt->desc.bInterfaceClass =3D=3D USB_CLASS_VIDEO) { + is_audio =3D false; + goto out; + } + + if (alt->desc.bInterfaceClass =3D=3D USB_CLASS_AUDIO) { + for (k =3D 0; k < alt->desc.bNumEndpoints; k++) { + epd =3D &alt->endpoint[k].desc; + if (usb_endpoint_xfer_isoc(epd)) { + is_audio =3D true; + break; + } + } + } + } + } + +out: + return is_audio; +} + +/* + * check the usb device including the video class: + * True: Devices contain video class + * False: Device doesn't contain video class + */ +static bool is_usb_video_device(struct usb_device *udev) +{ + struct usb_host_config *config; + struct usb_host_interface *alt; + struct usb_interface_cache *intfc; + int i, j; + bool is_video =3D false; + + if (!udev || !udev->config) + return is_video; + + config =3D udev->config; + + for (i =3D 0; i < config->desc.bNumInterfaces; i++) { + intfc =3D config->intf_cache[i]; + for (j =3D 0; j < intfc->num_altsetting; j++) { + alt =3D &intfc->altsetting[j]; + + if (alt->desc.bInterfaceClass =3D=3D USB_CLASS_VIDEO) { + is_video =3D true; + goto out; + } + } + } + +out: + return is_video; +} + +static int xhci_udev_notify(struct notifier_block *self, unsigned long act= ion, + void *dev) +{ + struct usb_device *udev =3D dev; + struct xhci_offload_data *offload_data =3D xhci_get_offload_data(); + + switch (action) { + case USB_DEVICE_ADD: + if (is_compatible_with_usb_audio_offload(udev)) { + dev_dbg(&udev->dev, "Compatible with usb audio offload\n"); + if (offload_data->op_mode =3D=3D USB_OFFLOAD_DRAM) { + xhci_sync_conn_stat(udev->bus->busnum, udev->devnum, udev->slot_id, + USB_CONNECTED); + } + } + offload_data->usb_accessory_enabled =3D false; + break; + case USB_DEVICE_REMOVE: + if (is_compatible_with_usb_audio_offload(udev) && + (offload_data->op_mode =3D=3D USB_OFFLOAD_DRAM)) { + xhci_sync_conn_stat(udev->bus->busnum, udev->devnum, udev->slot_id, + USB_DISCONNECTED); + } + offload_data->usb_accessory_enabled =3D false; + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block xhci_udev_nb =3D { + .notifier_call =3D xhci_udev_notify, +}; + +static int usb_audio_offload_init(struct xhci_hcd *xhci) +{ + struct device *dev =3D xhci_to_hcd(xhci)->self.sysdev; + struct xhci_offload_data *offload_data =3D xhci_get_offload_data(); + int ret; + u32 out_val; + + if (!is_aoc_usb_probe_done()) { + dev_info(dev, "%s: deferring the probe\n", __func__); + return -EPROBE_DEFER; + } + + offload_data =3D kzalloc(sizeof(struct xhci_offload_data), GFP_KERNEL); + if (!offload_data) + return -ENOMEM; + + if (!of_property_read_u32(dev->of_node, "offload", &out_val)) + offload_data->usb_audio_offload =3D (out_val =3D=3D 1) ? true : false; + + ret =3D of_reserved_mem_device_init(dev); + if (ret) { + dev_err(dev, "Could not get reserved memory\n"); + kfree(offload_data); + return ret; + } + + offload_data->dt_direct_usb_access =3D + of_property_read_bool(dev->of_node, "direct-usb-access") ? true : false; + if (!offload_data->dt_direct_usb_access) + dev_warn(dev, "Direct USB access is not supported\n"); + + offload_data->offload_state =3D true; + + usb_register_notify(&xhci_udev_nb); + offload_data->op_mode =3D USB_OFFLOAD_DRAM; + offload_data->xhci =3D xhci; + + return 0; +} + +static void usb_audio_offload_cleanup(struct xhci_hcd *xhci) +{ + struct xhci_offload_data *offload_data =3D xhci_get_offload_data(); + + offload_data->usb_audio_offload =3D false; + offload_data->op_mode =3D USB_OFFLOAD_STOP; + offload_data->xhci =3D NULL; + + usb_unregister_notify(&xhci_udev_nb); + + /* Notification for xhci driver removing */ + usb_host_mode_state_notify(USB_DISCONNECTED); + + kfree(offload_data); +} + +static bool is_offload_enabled(struct xhci_hcd *xhci, + struct xhci_virt_device *vdev, unsigned int ep_index) +{ + struct usb_device *udev; + struct xhci_offload_data *offload_data =3D xhci_get_offload_data(); + bool global_enabled =3D offload_data->op_mode !=3D USB_OFFLOAD_STOP; + struct xhci_ring *ep_ring; + + if (vdev =3D=3D NULL || vdev->eps[ep_index].ring =3D=3D NULL) + return global_enabled; + + udev =3D vdev->udev; + + if (global_enabled) { + ep_ring =3D vdev->eps[ep_index].ring; + if (offload_data->op_mode =3D=3D USB_OFFLOAD_DRAM) { + if (is_usb_video_device(udev)) + return false; + else if (ep_ring->type =3D=3D TYPE_ISOC) + return offload_data->offload_state; + } + } + + return false; +} + +static bool is_usb_bulk_transfer_enabled(struct xhci_hcd *xhci, struct urb= *urb) +{ + struct xhci_offload_data *offload_data =3D xhci_get_offload_data(); + struct usb_endpoint_descriptor *desc =3D &urb->ep->desc; + int ep_type =3D usb_endpoint_type(desc); + struct usb_ctrlrequest *cmd; + bool skip_bulk =3D false; + + cmd =3D (struct usb_ctrlrequest *) urb->setup_packet; + + if (ep_type =3D=3D USB_ENDPOINT_XFER_CONTROL) { + if (!usb_endpoint_dir_in(desc) && cmd->bRequest =3D=3D 0x35) + offload_data->usb_accessory_enabled =3D true; + else + offload_data->usb_accessory_enabled =3D false; + } + + if (ep_type =3D=3D USB_ENDPOINT_XFER_BULK && !usb_endpoint_dir_in(desc)) + skip_bulk =3D offload_data->usb_accessory_enabled; + + return skip_bulk; +} + +static void alloc_dcbaa(struct xhci_hcd *xhci, gfp_t flags) +{ + dma_addr_t dma; + struct device *dev =3D xhci_to_hcd(xhci)->self.sysdev; + struct xhci_offload_data *offload_data =3D xhci_get_offload_data(); + + if (offload_data->op_mode =3D=3D USB_OFFLOAD_DRAM) { + xhci->dcbaa =3D dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), + &dma, flags); + if (!xhci->dcbaa) + return; + + xhci->dcbaa->dma =3D dma; + if (xhci_set_dcbaa_ptr(xhci->dcbaa->dma) !=3D 0) { + xhci_err(xhci, "Set DCBAA pointer failed\n"); + xhci->dcbaa =3D NULL; + return; + } + xhci_setup_done(); + + xhci_dbg(xhci, "Set dcbaa_ptr=3D%llx to AoC\n", xhci->dcbaa->dma); + } else { + xhci->dcbaa =3D dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), + &dma, flags); + if (!xhci->dcbaa) + return; + + xhci->dcbaa->dma =3D dma; + } +} + +static void free_dcbaa(struct xhci_hcd *xhci) +{ + struct device *dev =3D xhci_to_hcd(xhci)->self.sysdev; + + if (!xhci->dcbaa) + return; + + dma_free_coherent(dev, sizeof(*xhci->dcbaa), + xhci->dcbaa, xhci->dcbaa->dma); + + xhci->dcbaa =3D NULL; +} + +static struct xhci_ring *alloc_transfer_ring(struct xhci_hcd *xhci, + u32 endpoint_type, enum xhci_ring_type ring_type, + unsigned int max_packet, gfp_t mem_flags) +{ + struct xhci_ring *ep_ring; + u16 dir; + + ep_ring =3D xhci_ring_alloc(xhci, 1, 1, ring_type, max_packet, mem_flags); + dir =3D endpoint_type =3D=3D ISOC_IN_EP ? 0 : 1; + + xhci_set_isoc_tr_info(0, dir, ep_ring); + + return ep_ring; +} + +static void free_transfer_ring(struct xhci_hcd *xhci, struct xhci_virt_dev= ice *virt_dev, + unsigned int ep_index) +{ + struct xhci_ring *ring, *new_ring; + struct xhci_ep_ctx *ep_ctx; + struct xhci_input_control_ctx *ctrl_ctx; + u32 ep_type; + u32 ep_is_added, ep_is_dropped; + + ring =3D virt_dev->eps[ep_index].ring; + new_ring =3D virt_dev->eps[ep_index].new_ring; + ep_ctx =3D xhci_get_ep_ctx(xhci, virt_dev->out_ctx, ep_index); + ep_type =3D CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2)); + + ctrl_ctx =3D xhci_get_input_control_ctx(virt_dev->in_ctx); + if (!ctrl_ctx) { + xhci_warn(xhci, "%s: Could not get input context, bad type.\n", __func__= ); + return; + } + ep_is_added =3D EP_IS_ADDED(ctrl_ctx, ep_index); + ep_is_dropped =3D EP_IS_DROPPED(ctrl_ctx, ep_index); + + xhci_dbg(xhci, "%s: ep %u is added(0x%x), is dropped(0x%x)\n", __func__, = ep_index, + ep_is_added, ep_is_dropped); + + if (ring) { + xhci_dbg(xhci, "%s: ep_index=3D%u, ep_type=3D%u, ring type=3D%u, new_rin= g=3D%pK\n", + __func__, ep_index, ep_type, ring->type, new_ring); + + xhci_ring_free(xhci, virt_dev->eps[ep_index].ring); + + virt_dev->eps[ep_index].ring =3D NULL; + + if (ep_is_added =3D=3D 0 && ep_is_dropped =3D=3D 0) + return; + } + + if (new_ring) { + xhci_dbg(xhci, "%s: ep_index=3D%u, ep_type=3D%u, new_ring type=3D%u\n", = __func__, + ep_index, ep_type, new_ring->type); + + xhci_ring_free(xhci, virt_dev->eps[ep_index].new_ring); + + virt_dev->eps[ep_index].new_ring =3D NULL; + + return; + } +} + +static bool offload_skip_urb(struct xhci_hcd *xhci, struct urb *urb) +{ + struct xhci_virt_device *vdev =3D xhci->devs[urb->dev->slot_id]; + struct usb_endpoint_descriptor *desc =3D &urb->ep->desc; + int ep_type =3D usb_endpoint_type(desc); + unsigned int ep_index; + + if (ep_type =3D=3D USB_ENDPOINT_XFER_CONTROL) + ep_index =3D (unsigned int)(usb_endpoint_num(desc)*2); + else + ep_index =3D (unsigned int)(usb_endpoint_num(desc)*2) + + (usb_endpoint_dir_in(desc) ? 1 : 0) - 1; + + xhci_dbg(xhci, "%s: ep_index=3D%u, ep_type=3D%d\n", __func__, ep_index, e= p_type); + + if (is_offload_enabled(xhci, vdev, ep_index)) + return true; + + if (is_usb_bulk_transfer_enabled(xhci, urb)) + return true; + + return false; +} + +static struct xhci_offload_ops offload_ops =3D { + .offload_init =3D usb_audio_offload_init, + .offload_cleanup =3D usb_audio_offload_cleanup, + .is_offload_enabled =3D is_offload_enabled, + .alloc_dcbaa =3D alloc_dcbaa, + .free_dcbaa =3D free_dcbaa, + .alloc_transfer_ring =3D alloc_transfer_ring, + .free_transfer_ring =3D free_transfer_ring, + .usb_offload_skip_urb =3D offload_skip_urb, +}; + +int xhci_offload_helper_init(void) +{ + return xhci_plat_register_offload_ops(&offload_ops); +} --=20 2.39.0.rc1.256.g54fd8350bd-goog