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=-9.0 required=3.0 tests=DKIMWL_WL_MED,DKIM_SIGNED, DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT 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 53012C282C4 for ; Tue, 12 Feb 2019 15:14:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 03E19217D9 for ; Tue, 12 Feb 2019 15:14:40 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=baylibre-com.20150623.gappssmtp.com header.i=@baylibre-com.20150623.gappssmtp.com header.b="iQO8kbl1" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730589AbfBLPOi (ORCPT ); Tue, 12 Feb 2019 10:14:38 -0500 Received: from mail-wm1-f68.google.com ([209.85.128.68]:37633 "EHLO mail-wm1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730446AbfBLPOd (ORCPT ); Tue, 12 Feb 2019 10:14:33 -0500 Received: by mail-wm1-f68.google.com with SMTP id x10so3376466wmg.2 for ; Tue, 12 Feb 2019 07:14:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Gsq17jOveVaPpdOsp4iUxKzX6UR/llKkyBjljOGUIGI=; b=iQO8kbl1ees3MCrw+wTvgK6b9jiCQGIdWPH5R10FXFSyyc0JUWe0c8L1baaWsOO2w7 3td1WNXBuKXJbUosze4OsPuSLCFwK+vHGKm+biurRAudWDHoOhVDiLxNbqZkDsGo2jOt rUzug7jUMrcuomnQPQXt0BC5/38XOP3WayE6UmciufOOPLXdIgroZSpoUc04jcemCIUy uaV0q5vTGoDI6fZcgnPM4tuZLyWwgQnS5QhHUIAIeXsK8RHU5HDezLxXqHRkKa1ioYUH gE78W04/zPqxgP/obJKS9ieqCofGMwReyVo1D2oQB19W4kjpNu01+NNusKC74Up0hipz IDNA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Gsq17jOveVaPpdOsp4iUxKzX6UR/llKkyBjljOGUIGI=; b=dwhHWDR7sy8Jf0+JlHqM8JDfjekTkVW2m3RbVWF0LQCyrlLWYRpBXgX4HsBO1xkNT8 gBYp828mF9UKWSWkKFmAEHJsovq5tkfmxYa9/GQCmTECMxKi/9dQBB++5Rz1qtUhQmPV EiZuAcVAcBq1pRD+em3NAx7Ocm1+/Ub0qFnRXpzyHFPuwnCGB+6wz4t+sRVZgQkbUVMs KAVyQHGW3R5tJNQON6oVKsdfUz9N8H/YGsqNzN9UOTV8MyBS80RC462V07jXCIMuGuHD QqZbKH+pVjwBOqGsI7OFJeGJNr35gFKKYMNo8irvR7O966vvQwPhh8tdHt89f5vIAsPG u9dg== X-Gm-Message-State: AHQUAubwxmBFRis6gWs+iTaIxMzP52/Wj0N/1fCuiNQY77xqBtViF4eL qCQ/uyEeoiWn86vN/vt7My2bXQ== X-Google-Smtp-Source: AHgI3Ia0tlG9NoF2lsr6Zy/7oPVT7yf1S3TetRm1/tz233B6JwUmBSnPeF6lG9+13EF1t8wuWcK4KQ== X-Received: by 2002:a1c:400a:: with SMTP id n10mr3220840wma.56.1549984469970; Tue, 12 Feb 2019 07:14:29 -0800 (PST) Received: from bender.baylibre.local (lmontsouris-657-1-212-31.w90-63.abo.wanadoo.fr. [90.63.244.31]) by smtp.gmail.com with ESMTPSA id c1sm1589515wmb.14.2019.02.12.07.14.28 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 12 Feb 2019 07:14:28 -0800 (PST) From: Neil Armstrong To: gregkh@linuxfoundation.org, hminas@synopsys.com, balbi@kernel.org, kishon@ti.com Cc: Neil Armstrong , linux-amlogic@lists.infradead.org, linux-usb@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH 8/8] usb: dwc3: Add Amlogic G12A DWC3 glue Date: Tue, 12 Feb 2019 16:14:13 +0100 Message-Id: <20190212151413.24632-9-narmstrong@baylibre.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190212151413.24632-1-narmstrong@baylibre.com> References: <20190212151413.24632-1-narmstrong@baylibre.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Adds support for Amlogic G12A USB Control Glue HW. The Amlogic G12A SoC Family embeds 2 USB Controllers : - a DWC3 IP configured as Host for USB2 and USB3 - a DWC2 IP configured as Peripheral USB2 Only A glue connects these both controllers to 2 USB2 PHYs, and optionnally to an USB3+PCIE Combo PHY shared with the PCIE controller. The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including routing of the OTG PHY between the DWC3 and DWC2 controllers, and setups the on-chip OTG mode selection for this PHY. The PHYs are childen of the Glue node since the Glue controls the interface with the PHY, not the DWC3 controller. The drivers collects the mode of each PHY and determine which PHY is to be routed between the DWC2 and DWC3 controllers. This drivers supports the on-probe setup of the OTG mode, and manually via a debugfs interface. The IRQ mode change detect is yet to be added in a future patchset, mainly due to lack of hardware to validate on. Signed-off-by: Neil Armstrong --- drivers/usb/dwc3/Kconfig | 9 + drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-meson-g12a.c | 650 +++++++++++++++++++++++++++++ 3 files changed, 660 insertions(+) create mode 100644 drivers/usb/dwc3/dwc3-meson-g12a.c diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 1a0404fda596..4335e5e76bbb 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -93,6 +93,15 @@ config USB_DWC3_KEYSTONE Support of USB2/3 functionality in TI Keystone2 platforms. Say 'Y' or 'M' here if you have one such device +config USB_DWC3_MESON_G12A + tristate "Amlogic Meson G12A Platforms" + depends on OF && COMMON_CLK + depends on ARCH_MESON || COMPILE_TEST + default USB_DWC3 + help + Support USB2/3 functionality in Amlogic G12A platforms. + Say 'Y' or 'M' if you have one such device. + config USB_DWC3_OF_SIMPLE tristate "Generic OF Simple Glue Layer" depends on OF && COMMON_CLK diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 6e3ef6144e5d..ae86da0dc5bd 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o obj-$(CONFIG_USB_DWC3_HAPS) += dwc3-haps.o obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o +obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c new file mode 100644 index 000000000000..abeff2d56b1d --- /dev/null +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -0,0 +1,650 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB Glue for Amlogic G12A SoCs + * + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong + */ + +/* + * The USB is organized with a glue around the DWC3 Controller IP as : + * - Control registers for each USB2 Ports + * - Control registers for the USB PHY layer + * - SuperSpeed PHY can be enabled only if port is used + * + * TOFIX: + * - Add dynamic OTG switching with ID change interrupt + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* USB Glue Control Registers */ + +#define USB_R0 0x00 + #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17) + #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18) + #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19) + #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29) + #define USB_R0_U2D_ACT BIT(31) + +#define USB_R1 0x04 + #define USB_R1_U3H_BIGENDIAN_GS BIT(0) + #define USB_R1_U3H_PME_ENABLE BIT(1) + #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(4, 2) + #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(9, 7) + #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(13, 12) + #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16) + #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17) + #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18) + #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19) + #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25) + +#define USB_R2 0x08 + #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20) + #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26) + +#define USB_R3 0x0c + #define USB_R3_P30_SSC_ENABLE BIT(0) + #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1) + #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4) + #define USB_R3_P30_REF_SSP_EN BIT(13) + +#define USB_R4 0x10 + #define USB_R4_P21_PORT_RESET_0 BIT(0) + #define USB_R4_P21_SLEEP_M0 BIT(1) + #define USB_R4_MEM_PD_MASK GENMASK(3, 2) + #define USB_R4_P21_ONLY BIT(4) + +#define USB_R5 0x14 + #define USB_R5_ID_DIG_SYNC BIT(0) + #define USB_R5_ID_DIG_REG BIT(1) + #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2) + #define USB_R5_ID_DIG_EN_0 BIT(4) + #define USB_R5_ID_DIG_EN_1 BIT(5) + #define USB_R5_ID_DIG_CURR BIT(6) + #define USB_R5_ID_DIG_IRQ BIT(7) + #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) + #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) + +/* USB2 Ports Control Registers */ + +#define U2P_R0 0x0 + #define U2P_R0_HOST_DEVICE BIT(0) + #define U2P_R0_POWER_OK BIT(1) + #define U2P_R0_HAST_MODE BIT(2) + #define U2P_R0_POWER_ON_RESET BIT(3) + #define U2P_R0_ID_PULLUP BIT(4) + #define U2P_R0_DRV_VBUS BIT(5) + +#define U2P_R1 0x4 + #define U2P_R1_PHY_READY BIT(0) + #define U2P_R1_ID_DIG BIT(1) + #define U2P_R1_OTG_SESSION_VALID BIT(2) + #define U2P_R1_VBUS_VALID BIT(3) + +#define MAX_PHY 5 + +#define USB2_MAX_PHY 4 +#define USB3_PHY 4 + +struct dwc3_meson_g12a { + struct device *dev; + struct regmap *regmap; + struct clk *clk; + struct reset_control *reset; + struct phy *phys[5]; + enum usb_dr_mode phy_modes[5]; + enum phy_mode otg_phy_mode; + unsigned int usb2_ports; + unsigned int usb3_ports; + struct dentry *root; +}; + +#define U2P_REG_SIZE 0x20 +#define USB_REG_OFFSET 0x80 + +static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv, + int i, enum usb_dr_mode mode) +{ + switch (mode) { + case USB_DR_MODE_HOST: + case USB_DR_MODE_OTG: + case USB_DR_MODE_UNKNOWN: + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, + U2P_R0_HOST_DEVICE); + break; + + case USB_DR_MODE_PERIPHERAL: + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, 0); + break; + } +} + +static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv) +{ + enum usb_dr_mode id_mode; + u32 val; + int i; + + /* Read ID current level */ + regmap_read(priv->regmap, USB_R5, &val); + if (val & USB_R5_ID_DIG_CURR) + id_mode = USB_DR_MODE_PERIPHERAL; + else + id_mode = USB_DR_MODE_HOST; + + dev_info(priv->dev, "ID mode %s\n", + id_mode == USB_DR_MODE_HOST ? "host" : "peripheral"); + + for (i = 0 ; i < USB2_MAX_PHY ; ++i) { + if (!priv->phys[i]) + continue; + + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_POWER_ON_RESET, + U2P_R0_POWER_ON_RESET); + + if (priv->phy_modes[i] == USB_DR_MODE_PERIPHERAL || + (priv->phy_modes[i] == USB_DR_MODE_OTG && + id_mode == USB_DR_MODE_PERIPHERAL)) { + dwc3_meson_g12a_usb2_set_mode(priv, i, + USB_DR_MODE_PERIPHERAL); + + if (priv->phy_modes[i] == USB_DR_MODE_OTG) + priv->otg_phy_mode = PHY_MODE_USB_DEVICE; + } else { + dwc3_meson_g12a_usb2_set_mode(priv, i, + USB_DR_MODE_HOST); + + if (priv->phy_modes[i] == USB_DR_MODE_OTG) + priv->otg_phy_mode = PHY_MODE_USB_HOST; + } + + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_POWER_ON_RESET, 0); + } + + return 0; +} + +static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv) +{ + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R3, + USB_R3_P30_SSC_RANGE_MASK | + USB_R3_P30_REF_SSP_EN, + USB_R3_P30_SSC_ENABLE | + FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) | + USB_R3_P30_REF_SSP_EN); + udelay(2); + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R2, + USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, + FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15)); + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R2, + USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, + FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20)); + + udelay(2); + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R1, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT); + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R1, + USB_R1_P30_PCS_TX_SWING_FULL_MASK, + FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127)); +} + +static void dwc3_meson_g12a_usb_init_mode(struct dwc3_meson_g12a *priv, + bool is_peripheral) +{ + if (is_peripheral) { + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R0, + USB_R0_U2D_ACT, USB_R0_U2D_ACT); + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R0, + USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0); + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R4, + USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0); + } else { + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R0, + USB_R0_U2D_ACT, 0); + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R4, + USB_R4_P21_SLEEP_M0, 0); + } +} + +static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv) +{ + int ret; + + ret = dwc3_meson_g12a_usb2_init(priv); + if (ret) + return ret; + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R1, + USB_R1_U3H_FLADJ_30MHZ_REG_MASK, + FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20)); + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R5, + USB_R5_ID_DIG_EN_0, + USB_R5_ID_DIG_EN_0); + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R5, + USB_R5_ID_DIG_EN_1, + USB_R5_ID_DIG_EN_1); + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R5, + USB_R5_ID_DIG_TH_MASK, + FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff)); + + /* If we have an actual SuperSpeed port, initialize it */ + if (priv->usb3_ports) + dwc3_meson_g12a_usb3_init(priv); + + dwc3_meson_g12a_usb_init_mode(priv, + (priv->otg_phy_mode == PHY_MODE_USB_DEVICE)); + + return 0; +} + +static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = USB_REG_OFFSET + USB_R5, +}; + +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv) +{ + struct device_node *port, *phy_node; + struct of_phandle_args args; + enum usb_dr_mode mode; + const char *dr_mode; + struct phy *phy; + int ret, i; + + for (i = 0 ; i < MAX_PHY ; ++i) { + port = of_graph_get_port_by_id(priv->dev->of_node, i); + + /* Ignore port if not defined or disabled */ + if (!of_device_is_available(port)) { + of_node_put(port); + continue; + } + + /* Get associated PHY */ + phy = of_phy_get(port, NULL); + if (IS_ERR(phy)) { + of_node_put(port); + ret = PTR_ERR(phy); + goto err_phy_get; + } + + of_node_put(port); + + /* Get phy dr_mode */ + ret = of_parse_phandle_with_args(port, "phys", "#phy-cells", + 0, &args); + if (ret) { + of_node_put(port); + goto err_phy_get; + } + + phy_node = args.np; + + ret = of_property_read_string(phy_node, "dr_mode", &dr_mode); + if (ret) { + dr_mode = "unknown"; + mode = USB_DR_MODE_UNKNOWN; + } else { + if (!strcmp(dr_mode, "host")) + mode = USB_DR_MODE_HOST; + else if (!strcmp(dr_mode, "otg")) + mode = USB_DR_MODE_OTG; + else if (!strcmp(dr_mode, "peripheral")) + mode = USB_DR_MODE_PERIPHERAL; + else { + mode = USB_DR_MODE_UNKNOWN; + dr_mode = "unknown"; + } + } + + dev_info(priv->dev, "port%d: %s mode %s\n", + i, of_node_full_name(phy_node), dr_mode); + + of_node_put(phy_node); + + priv->phy_modes[i] = mode; + priv->phys[i] = phy; + + if (i == USB3_PHY) + priv->usb3_ports++; + else + priv->usb2_ports++; + } + + dev_info(priv->dev, "usb2 ports: %d\n", priv->usb2_ports); + dev_info(priv->dev, "usb3 ports: %d\n", priv->usb3_ports); + + return 0; + +err_phy_get: + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_put(priv->phys[i]); + + return ret; +} + +static int dwc3_meson_g12a_mode_force_get(void *data, u64 *val) +{ + struct dwc3_meson_g12a *priv = data; + + if (priv->otg_phy_mode == PHY_MODE_USB_HOST) + *val = 1; + else if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) + *val = 0; + else + return -EINVAL; + + return 0; +} + +static int dwc3_meson_g12a_mode_force_set(void *data, u64 val) +{ + struct dwc3_meson_g12a *priv = data; + int i; + + if ((val && priv->otg_phy_mode == PHY_MODE_USB_HOST) || + (!val && priv->otg_phy_mode == PHY_MODE_USB_DEVICE)) + return 0; + + for (i = 0 ; i < USB2_MAX_PHY ; ++i) { + if (!priv->phys[i]) + continue; + + if (priv->phy_modes[i] != USB_DR_MODE_OTG) + continue; + + if (val) { + dev_info(priv->dev, "switching to Host Mode\n"); + + dwc3_meson_g12a_usb2_set_mode(priv, i, + USB_DR_MODE_HOST); + + dwc3_meson_g12a_usb_init_mode(priv, false); + + priv->otg_phy_mode = PHY_MODE_USB_HOST; + } else { + dev_info(priv->dev, "switching to Device Mode\n"); + + dwc3_meson_g12a_usb2_set_mode(priv, i, + USB_DR_MODE_PERIPHERAL); + + dwc3_meson_g12a_usb_init_mode(priv, true); + + priv->otg_phy_mode = PHY_MODE_USB_DEVICE; + } + + return phy_set_mode(priv->phys[i], priv->otg_phy_mode); + } + + return -EINVAL; +} + +DEFINE_DEBUGFS_ATTRIBUTE(dwc3_meson_g12a_mode_force_fops, + dwc3_meson_g12a_mode_force_get, + dwc3_meson_g12a_mode_force_set, "%llu\n"); + +static int dwc3_meson_g12a_otg_id_get(void *data, u64 *val) +{ + struct dwc3_meson_g12a *priv = data; + u32 reg; + + regmap_read(priv->regmap, USB_R5, ®); + + *val = (reg & USB_R5_ID_DIG_CURR); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(dwc3_meson_g12a_otg_id_fops, + dwc3_meson_g12a_otg_id_get, NULL, "%llu\n"); + +/* We provide a DebugFS interface to get the ID value and force OTG switch */ +static int dwc3_meson_g12a_debugfs_init(struct dwc3_meson_g12a *priv) +{ + priv->root = debugfs_create_dir("dwc3-meson-g12a", NULL); + if (IS_ERR(priv->root)) + return PTR_ERR(priv->root); + + debugfs_create_file("mode_force", 0600, priv->root, priv, + &dwc3_meson_g12a_mode_force_fops); + + debugfs_create_file("otg_id", 0400, priv->root, priv, + &dwc3_meson_g12a_otg_id_fops); + + return 0; +} + +static int dwc3_meson_g12a_probe(struct platform_device *pdev) +{ + struct dwc3_meson_g12a *priv; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + void __iomem *base; + struct resource *res; + int ret, i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(dev, base, + &phy_meson_g12a_usb3_regmap_conf); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->clk = devm_clk_get(dev, "usb"); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + platform_set_drvdata(pdev, priv); + priv->dev = dev; + + priv->reset = devm_reset_control_get(dev, "usb"); + if (IS_ERR(priv->reset)) { + ret = PTR_ERR(priv->reset); + dev_err(dev, "failed to get device reset, err=%d\n", ret); + return ret; + } + + ret = reset_control_reset(priv->reset); + if (ret) + return ret; + + ret = dwc3_meson_g12a_get_phys(priv); + if (ret) + return ret; + + dwc3_meson_g12a_usb_init(priv); + + /* Init PHYs */ + for (i = 0 ; i < MAX_PHY ; ++i) { + if (priv->phys[i]) { + ret = phy_init(priv->phys[i]); + if (ret) + goto err_phys_put; + } + } + + /* Set OTG PHY mode */ + for (i = 0 ; i < MAX_PHY ; ++i) { + if (priv->phys[i] && priv->phy_modes[i] == USB_DR_MODE_OTG) { + ret = phy_set_mode(priv->phys[i], priv->otg_phy_mode); + if (ret) + goto err_phys_put; + } + } + + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) { + clk_disable_unprepare(priv->clk); + clk_put(priv->clk); + + goto err_phys_exit; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + if (dwc3_meson_g12a_debugfs_init(priv)) + dev_dbg(dev, "Failed to add DebugFS interface\n"); + + return 0; + +err_phys_exit: + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_exit(priv->phys[i]); + +err_phys_put: + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_put(priv->phys[i]); + + return ret; +} + +static int dwc3_meson_g12a_remove(struct platform_device *pdev) +{ + struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int i; + + debugfs_remove_recursive(priv->root); + + of_platform_depopulate(dev); + + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_exit(priv->phys[i]); + + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_put(priv->phys[i]); + + clk_disable_unprepare(priv->clk); + clk_put(priv->clk); + + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + pm_runtime_set_suspended(dev); + + return 0; +} + +static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + + clk_disable(priv->clk); + + return 0; +} + +static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + + return clk_enable(priv->clk); +} + +static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + int i; + + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_exit(priv->phys[i]); + + reset_control_assert(priv->reset); + + return 0; +} + +static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + int i, ret; + + reset_control_deassert(priv->reset); + + dwc3_meson_g12a_usb_init(priv); + + /* Init PHYs */ + for (i = 0 ; i < MAX_PHY ; ++i) { + if (priv->phys[i]) { + ret = phy_init(priv->phys[i]); + if (ret) + return ret; + } + } + + return 0; +} + +static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume) + SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend, + dwc3_meson_g12a_runtime_resume, NULL) +}; + +static const struct of_device_id dwc3_meson_g12a_match[] = { + { .compatible = "amlogic,meson-g12a-usb-ctrl" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); + +static struct platform_driver dwc3_meson_g12a_driver = { + .probe = dwc3_meson_g12a_probe, + .remove = dwc3_meson_g12a_remove, + .driver = { + .name = "dwc3-meson-g12a", + .of_match_table = dwc3_meson_g12a_match, + .pm = &dwc3_meson_g12a_dev_pm_ops, + }, +}; + +module_platform_driver(dwc3_meson_g12a_driver); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer"); +MODULE_AUTHOR("Neil Armstrong "); -- 2.20.1 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: [8/8] usb: dwc3: Add Amlogic G12A DWC3 glue From: Neil Armstrong Message-Id: <20190212151413.24632-9-narmstrong@baylibre.com> Date: Tue, 12 Feb 2019 16:14:13 +0100 To: gregkh@linuxfoundation.org, hminas@synopsys.com, balbi@kernel.org, kishon@ti.com Cc: Neil Armstrong , linux-amlogic@lists.infradead.org, linux-usb@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org List-ID: QWRkcyBzdXBwb3J0IGZvciBBbWxvZ2ljIEcxMkEgVVNCIENvbnRyb2wgR2x1ZSBIVy4KClRoZSBB bWxvZ2ljIEcxMkEgU29DIEZhbWlseSBlbWJlZHMgMiBVU0IgQ29udHJvbGxlcnMgOgotIGEgRFdD MyBJUCBjb25maWd1cmVkIGFzIEhvc3QgZm9yIFVTQjIgYW5kIFVTQjMKLSBhIERXQzIgSVAgY29u ZmlndXJlZCBhcyBQZXJpcGhlcmFsIFVTQjIgT25seQoKQSBnbHVlIGNvbm5lY3RzIHRoZXNlIGJv dGggY29udHJvbGxlcnMgdG8gMiBVU0IyIFBIWXMsIGFuZCBvcHRpb25uYWxseQp0byBhbiBVU0Iz K1BDSUUgQ29tYm8gUEhZIHNoYXJlZCB3aXRoIHRoZSBQQ0lFIGNvbnRyb2xsZXIuCgpUaGUgR2x1 ZSBjb25maWd1cmVzIHRoZSBVVE1JIDhiaXQgaW50ZXJmYWNlcyBmb3IgdGhlIFVTQjIgUEhZcywg aW5jbHVkaW5nCnJvdXRpbmcgb2YgdGhlIE9URyBQSFkgYmV0d2VlbiB0aGUgRFdDMyBhbmQgRFdD MiBjb250cm9sbGVycywgYW5kCnNldHVwcyB0aGUgb24tY2hpcCBPVEcgbW9kZSBzZWxlY3Rpb24g Zm9yIHRoaXMgUEhZLgoKVGhlIFBIWXMgYXJlIGNoaWxkZW4gb2YgdGhlIEdsdWUgbm9kZSBzaW5j ZSB0aGUgR2x1ZSBjb250cm9scyB0aGUgaW50ZXJmYWNlCndpdGggdGhlIFBIWSwgbm90IHRoZSBE V0MzIGNvbnRyb2xsZXIuClRoZSBkcml2ZXJzIGNvbGxlY3RzIHRoZSBtb2RlIG9mIGVhY2ggUEhZ IGFuZCBkZXRlcm1pbmUgd2hpY2ggUEhZCmlzIHRvIGJlIHJvdXRlZCBiZXR3ZWVuIHRoZSBEV0My IGFuZCBEV0MzIGNvbnRyb2xsZXJzLgoKVGhpcyBkcml2ZXJzIHN1cHBvcnRzIHRoZSBvbi1wcm9i ZSBzZXR1cCBvZiB0aGUgT1RHIG1vZGUsIGFuZCBtYW51YWxseQp2aWEgYSBkZWJ1Z2ZzIGludGVy ZmFjZS4gVGhlIElSUSBtb2RlIGNoYW5nZSBkZXRlY3QgaXMgeWV0IHRvIGJlIGFkZGVkCmluIGEg ZnV0dXJlIHBhdGNoc2V0LCBtYWlubHkgZHVlIHRvIGxhY2sgb2YgaGFyZHdhcmUgdG8gdmFsaWRh dGUgb24uCgpTaWduZWQtb2ZmLWJ5OiBOZWlsIEFybXN0cm9uZyA8bmFybXN0cm9uZ0BiYXlsaWJy ZS5jb20+Ci0tLQogZHJpdmVycy91c2IvZHdjMy9LY29uZmlnICAgICAgICAgICB8ICAgOSArCiBk cml2ZXJzL3VzYi9kd2MzL01ha2VmaWxlICAgICAgICAgIHwgICAxICsKIGRyaXZlcnMvdXNiL2R3 YzMvZHdjMy1tZXNvbi1nMTJhLmMgfCA2NTAgKysrKysrKysrKysrKysrKysrKysrKysrKysrKysK IDMgZmlsZXMgY2hhbmdlZCwgNjYwIGluc2VydGlvbnMoKykKIGNyZWF0ZSBtb2RlIDEwMDY0NCBk cml2ZXJzL3VzYi9kd2MzL2R3YzMtbWVzb24tZzEyYS5jCgpkaWZmIC0tZ2l0IGEvZHJpdmVycy91 c2IvZHdjMy9LY29uZmlnIGIvZHJpdmVycy91c2IvZHdjMy9LY29uZmlnCmluZGV4IDFhMDQwNGZk YTU5Ni4uNDMzNWU1ZTc2YmJiIDEwMDY0NAotLS0gYS9kcml2ZXJzL3VzYi9kd2MzL0tjb25maWcK KysrIGIvZHJpdmVycy91c2IvZHdjMy9LY29uZmlnCkBAIC05Myw2ICs5MywxNSBAQCBjb25maWcg VVNCX0RXQzNfS0VZU1RPTkUKIAkgIFN1cHBvcnQgb2YgVVNCMi8zIGZ1bmN0aW9uYWxpdHkgaW4g VEkgS2V5c3RvbmUyIHBsYXRmb3Jtcy4KIAkgIFNheSAnWScgb3IgJ00nIGhlcmUgaWYgeW91IGhh dmUgb25lIHN1Y2ggZGV2aWNlCiAKK2NvbmZpZyBVU0JfRFdDM19NRVNPTl9HMTJBCisgICAgICAg dHJpc3RhdGUgIkFtbG9naWMgTWVzb24gRzEyQSBQbGF0Zm9ybXMiCisgICAgICAgZGVwZW5kcyBv biBPRiAmJiBDT01NT05fQ0xLCisgICAgICAgZGVwZW5kcyBvbiBBUkNIX01FU09OIHx8IENPTVBJ TEVfVEVTVAorICAgICAgIGRlZmF1bHQgVVNCX0RXQzMKKyAgICAgICBoZWxwCisgICAgICAgICBT dXBwb3J0IFVTQjIvMyBmdW5jdGlvbmFsaXR5IGluIEFtbG9naWMgRzEyQSBwbGF0Zm9ybXMuCisJ IFNheSAnWScgb3IgJ00nIGlmIHlvdSBoYXZlIG9uZSBzdWNoIGRldmljZS4KKwogY29uZmlnIFVT Ql9EV0MzX09GX1NJTVBMRQogICAgICAgIHRyaXN0YXRlICJHZW5lcmljIE9GIFNpbXBsZSBHbHVl IExheWVyIgogICAgICAgIGRlcGVuZHMgb24gT0YgJiYgQ09NTU9OX0NMSwpkaWZmIC0tZ2l0IGEv ZHJpdmVycy91c2IvZHdjMy9NYWtlZmlsZSBiL2RyaXZlcnMvdXNiL2R3YzMvTWFrZWZpbGUKaW5k ZXggNmUzZWY2MTQ0ZTVkLi5hZTg2ZGEwZGM1YmQgMTAwNjQ0Ci0tLSBhL2RyaXZlcnMvdXNiL2R3 YzMvTWFrZWZpbGUKKysrIGIvZHJpdmVycy91c2IvZHdjMy9NYWtlZmlsZQpAQCAtNDcsNiArNDcs NyBAQCBvYmotJChDT05GSUdfVVNCX0RXQzNfRVhZTk9TKQkJKz0gZHdjMy1leHlub3Mubwogb2Jq LSQoQ09ORklHX1VTQl9EV0MzX1BDSSkJCSs9IGR3YzMtcGNpLm8KIG9iai0kKENPTkZJR19VU0Jf RFdDM19IQVBTKQkJKz0gZHdjMy1oYXBzLm8KIG9iai0kKENPTkZJR19VU0JfRFdDM19LRVlTVE9O RSkJCSs9IGR3YzMta2V5c3RvbmUubworb2JqLSQoQ09ORklHX1VTQl9EV0MzX01FU09OX0cxMkEp CSs9IGR3YzMtbWVzb24tZzEyYS5vCiBvYmotJChDT05GSUdfVVNCX0RXQzNfT0ZfU0lNUExFKQkr PSBkd2MzLW9mLXNpbXBsZS5vCiBvYmotJChDT05GSUdfVVNCX0RXQzNfU1QpCQkrPSBkd2MzLXN0 Lm8KIG9iai0kKENPTkZJR19VU0JfRFdDM19RQ09NKQkJKz0gZHdjMy1xY29tLm8KZGlmZiAtLWdp dCBhL2RyaXZlcnMvdXNiL2R3YzMvZHdjMy1tZXNvbi1nMTJhLmMgYi9kcml2ZXJzL3VzYi9kd2Mz L2R3YzMtbWVzb24tZzEyYS5jCm5ldyBmaWxlIG1vZGUgMTAwNjQ0CmluZGV4IDAwMDAwMDAwMDAw MC4uYWJlZmYyZDU2YjFkCi0tLSAvZGV2L251bGwKKysrIGIvZHJpdmVycy91c2IvZHdjMy9kd2Mz LW1lc29uLWcxMmEuYwpAQCAtMCwwICsxLDY1MCBAQAorLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZp ZXI6IEdQTC0yLjAKKy8qCisgKiBVU0IgR2x1ZSBmb3IgQW1sb2dpYyBHMTJBIFNvQ3MKKyAqCisg KiBDb3B5cmlnaHQgKGMpIDIwMTkgQmF5TGlicmUsIFNBUworICogQXV0aG9yOiBOZWlsIEFybXN0 cm9uZyA8bmFybXN0cm9uZ0BiYXlsaWJyZS5jb20+CisgKi8KKworLyoKKyAqIFRoZSBVU0IgaXMg b3JnYW5pemVkIHdpdGggYSBnbHVlIGFyb3VuZCB0aGUgRFdDMyBDb250cm9sbGVyIElQIGFzIDoK KyAqIC0gQ29udHJvbCByZWdpc3RlcnMgZm9yIGVhY2ggVVNCMiBQb3J0cworICogLSBDb250cm9s IHJlZ2lzdGVycyBmb3IgdGhlIFVTQiBQSFkgbGF5ZXIKKyAqIC0gU3VwZXJTcGVlZCBQSFkgY2Fu IGJlIGVuYWJsZWQgb25seSBpZiBwb3J0IGlzIHVzZWQKKyAqCisgKiBUT0ZJWDoKKyAqIC0gQWRk IGR5bmFtaWMgT1RHIHN3aXRjaGluZyB3aXRoIElEIGNoYW5nZSBpbnRlcnJ1cHQKKyAqLworCisj aW5jbHVkZSA8bGludXgvbW9kdWxlLmg+CisjaW5jbHVkZSA8bGludXgva2VybmVsLmg+CisjaW5j bHVkZSA8bGludXgvc2xhYi5oPgorI2luY2x1ZGUgPGxpbnV4L3BsYXRmb3JtX2RldmljZS5oPgor I2luY2x1ZGUgPGxpbnV4L2RtYS1tYXBwaW5nLmg+CisjaW5jbHVkZSA8bGludXgvY2xrLmg+Cisj aW5jbHVkZSA8bGludXgvb2YuaD4KKyNpbmNsdWRlIDxsaW51eC9vZl9wbGF0Zm9ybS5oPgorI2lu Y2x1ZGUgPGxpbnV4L3BtX3J1bnRpbWUuaD4KKyNpbmNsdWRlIDxsaW51eC9yZWdtYXAuaD4KKyNp bmNsdWRlIDxsaW51eC9iaXRmaWVsZC5oPgorI2luY2x1ZGUgPGxpbnV4L2JpdG9wcy5oPgorI2lu Y2x1ZGUgPGxpbnV4L3Jlc2V0Lmg+CisjaW5jbHVkZSA8bGludXgvb2ZfZ3JhcGguaD4KKyNpbmNs dWRlIDxsaW51eC9waHkvcGh5Lmg+CisjaW5jbHVkZSA8bGludXgvdXNiL290Zy5oPgorI2luY2x1 ZGUgPGxpbnV4L2RlYnVnZnMuaD4KKworLyogVVNCIEdsdWUgQ29udHJvbCBSZWdpc3RlcnMgKi8K KworI2RlZmluZSBVU0JfUjAJCQkJCQkJMHgwMAorCSNkZWZpbmUgVVNCX1IwX1AzMF9MQU5FMF9U WDJSWF9MT09QQkFDSwkJCUJJVCgxNykKKwkjZGVmaW5lIFVTQl9SMF9QMzBfTEFORTBfRVhUX1BD TEtfUkVRCQkJQklUKDE4KQorCSNkZWZpbmUgVVNCX1IwX1AzMF9QQ1NfUlhfTE9TX01BU0tfVkFM X01BU0sJCUdFTk1BU0soMjgsIDE5KQorCSNkZWZpbmUgVVNCX1IwX1UyRF9TU19TQ0FMRURPV05f TU9ERV9NQVNLCQlHRU5NQVNLKDMwLCAyOSkKKwkjZGVmaW5lIFVTQl9SMF9VMkRfQUNUCQkJCQlC SVQoMzEpCisKKyNkZWZpbmUgVVNCX1IxCQkJCQkJCTB4MDQKKwkjZGVmaW5lIFVTQl9SMV9VM0hf QklHRU5ESUFOX0dTCQkJCUJJVCgwKQorCSNkZWZpbmUgVVNCX1IxX1UzSF9QTUVfRU5BQkxFCQkJ CUJJVCgxKQorCSNkZWZpbmUgVVNCX1IxX1UzSF9IVUJfUE9SVF9PVkVSQ1VSUkVOVF9NQVNLCQlH RU5NQVNLKDQsIDIpCisJI2RlZmluZSBVU0JfUjFfVTNIX0hVQl9QT1JUX1BFUk1fQVRUQUNIX01B U0sJCUdFTk1BU0soOSwgNykKKwkjZGVmaW5lIFVTQl9SMV9VM0hfSE9TVF9VMl9QT1JUX0RJU0FC TEVfTUFTSwkJR0VOTUFTSygxMywgMTIpCisJI2RlZmluZSBVU0JfUjFfVTNIX0hPU1RfVTNfUE9S VF9ESVNBQkxFCQkJQklUKDE2KQorCSNkZWZpbmUgVVNCX1IxX1UzSF9IT1NUX1BPUlRfUE9XRVJf Q09OVFJPTF9QUkVTRU5UCUJJVCgxNykKKwkjZGVmaW5lIFVTQl9SMV9VM0hfSE9TVF9NU0lfRU5B QkxFCQkJQklUKDE4KQorCSNkZWZpbmUgVVNCX1IxX1UzSF9GTEFESl8zME1IWl9SRUdfTUFTSwkJ CUdFTk1BU0soMjQsIDE5KQorCSNkZWZpbmUgVVNCX1IxX1AzMF9QQ1NfVFhfU1dJTkdfRlVMTF9N QVNLCQlHRU5NQVNLKDMxLCAyNSkKKworI2RlZmluZSBVU0JfUjIJCQkJCQkJMHgwOAorCSNkZWZp bmUgVVNCX1IyX1AzMF9QQ1NfVFhfREVFTVBIXzNQNURCX01BU0sJCUdFTk1BU0soMjUsIDIwKQor CSNkZWZpbmUgVVNCX1IyX1AzMF9QQ1NfVFhfREVFTVBIXzZEQl9NQVNLCQlHRU5NQVNLKDMxLCAy NikKKworI2RlZmluZSBVU0JfUjMJCQkJCQkJMHgwYworCSNkZWZpbmUgVVNCX1IzX1AzMF9TU0Nf RU5BQkxFCQkJCUJJVCgwKQorCSNkZWZpbmUgVVNCX1IzX1AzMF9TU0NfUkFOR0VfTUFTSwkJCUdF Tk1BU0soMywgMSkKKwkjZGVmaW5lIFVTQl9SM19QMzBfU1NDX1JFRl9DTEtfU0VMX01BU0sJCQlH RU5NQVNLKDEyLCA0KQorCSNkZWZpbmUgVVNCX1IzX1AzMF9SRUZfU1NQX0VOCQkJCUJJVCgxMykK KworI2RlZmluZSBVU0JfUjQJCQkJCQkJMHgxMAorCSNkZWZpbmUgVVNCX1I0X1AyMV9QT1JUX1JF U0VUXzAJCQkJQklUKDApCisJI2RlZmluZSBVU0JfUjRfUDIxX1NMRUVQX00wCQkJCUJJVCgxKQor CSNkZWZpbmUgVVNCX1I0X01FTV9QRF9NQVNLCQkJCUdFTk1BU0soMywgMikKKwkjZGVmaW5lIFVT Ql9SNF9QMjFfT05MWQkJCQkJQklUKDQpCisKKyNkZWZpbmUgVVNCX1I1CQkJCQkJCTB4MTQKKwkj ZGVmaW5lIFVTQl9SNV9JRF9ESUdfU1lOQwkJCQlCSVQoMCkKKwkjZGVmaW5lIFVTQl9SNV9JRF9E SUdfUkVHCQkJCUJJVCgxKQorCSNkZWZpbmUgVVNCX1I1X0lEX0RJR19DRkdfTUFTSwkJCQlHRU5N QVNLKDMsIDIpCisJI2RlZmluZSBVU0JfUjVfSURfRElHX0VOXzAJCQkJQklUKDQpCisJI2RlZmlu ZSBVU0JfUjVfSURfRElHX0VOXzEJCQkJQklUKDUpCisJI2RlZmluZSBVU0JfUjVfSURfRElHX0NV UlIJCQkJQklUKDYpCisJI2RlZmluZSBVU0JfUjVfSURfRElHX0lSUQkJCQlCSVQoNykKKwkjZGVm aW5lIFVTQl9SNV9JRF9ESUdfVEhfTUFTSwkJCQlHRU5NQVNLKDE1LCA4KQorCSNkZWZpbmUgVVNC X1I1X0lEX0RJR19DTlRfTUFTSwkJCQlHRU5NQVNLKDIzLCAxNikKKworLyogVVNCMiBQb3J0cyBD b250cm9sIFJlZ2lzdGVycyAqLworCisjZGVmaW5lIFUyUF9SMAkJCQkJCQkweDAKKwkjZGVmaW5l IFUyUF9SMF9IT1NUX0RFVklDRQkJCQlCSVQoMCkKKwkjZGVmaW5lIFUyUF9SMF9QT1dFUl9PSwkJ CQkJQklUKDEpCisJI2RlZmluZSBVMlBfUjBfSEFTVF9NT0RFCQkJCUJJVCgyKQorCSNkZWZpbmUg VTJQX1IwX1BPV0VSX09OX1JFU0VUCQkJCUJJVCgzKQorCSNkZWZpbmUgVTJQX1IwX0lEX1BVTExV UAkJCQlCSVQoNCkKKwkjZGVmaW5lIFUyUF9SMF9EUlZfVkJVUwkJCQkJQklUKDUpCisKKyNkZWZp bmUgVTJQX1IxCQkJCQkJCTB4NAorCSNkZWZpbmUgVTJQX1IxX1BIWV9SRUFEWQkJCQlCSVQoMCkK KwkjZGVmaW5lIFUyUF9SMV9JRF9ESUcJCQkJCUJJVCgxKQorCSNkZWZpbmUgVTJQX1IxX09UR19T RVNTSU9OX1ZBTElECQkJQklUKDIpCisJI2RlZmluZSBVMlBfUjFfVkJVU19WQUxJRAkJCQlCSVQo MykKKworI2RlZmluZSBNQVhfUEhZCQkJCQkJCTUKKworI2RlZmluZSBVU0IyX01BWF9QSFkJCQkJ CQk0CisjZGVmaW5lIFVTQjNfUEhZCQkJCQkJNAorCitzdHJ1Y3QgZHdjM19tZXNvbl9nMTJhIHsK KwlzdHJ1Y3QgZGV2aWNlCQkqZGV2OworCXN0cnVjdCByZWdtYXAJCSpyZWdtYXA7CisJc3RydWN0 IGNsawkJKmNsazsKKwlzdHJ1Y3QgcmVzZXRfY29udHJvbAkqcmVzZXQ7CisJc3RydWN0IHBoeQkJ KnBoeXNbNV07CisJZW51bSB1c2JfZHJfbW9kZQlwaHlfbW9kZXNbNV07CisJZW51bSBwaHlfbW9k ZQkJb3RnX3BoeV9tb2RlOworCXVuc2lnbmVkIGludAkJdXNiMl9wb3J0czsKKwl1bnNpZ25lZCBp bnQJCXVzYjNfcG9ydHM7CisJc3RydWN0IGRlbnRyeQkJKnJvb3Q7Cit9OworCisjZGVmaW5lIFUy UF9SRUdfU0laRQkJCQkJCTB4MjAKKyNkZWZpbmUgVVNCX1JFR19PRkZTRVQJCQkJCQkweDgwCisK K3N0YXRpYyB2b2lkIGR3YzNfbWVzb25fZzEyYV91c2IyX3NldF9tb2RlKHN0cnVjdCBkd2MzX21l c29uX2cxMmEgKnByaXYsCisJCQkJCSAgaW50IGksIGVudW0gdXNiX2RyX21vZGUgbW9kZSkKK3sK Kwlzd2l0Y2ggKG1vZGUpIHsKKwljYXNlIFVTQl9EUl9NT0RFX0hPU1Q6CisJY2FzZSBVU0JfRFJf TU9ERV9PVEc6CisJY2FzZSBVU0JfRFJfTU9ERV9VTktOT1dOOgorCQlyZWdtYXBfdXBkYXRlX2Jp dHMocHJpdi0+cmVnbWFwLCBVMlBfUjAgKyAoVTJQX1JFR19TSVpFICogaSksCisJCQkJVTJQX1Iw X0hPU1RfREVWSUNFLAorCQkJCVUyUF9SMF9IT1NUX0RFVklDRSk7CisJCWJyZWFrOworCisJY2Fz ZSBVU0JfRFJfTU9ERV9QRVJJUEhFUkFMOgorCQlyZWdtYXBfdXBkYXRlX2JpdHMocHJpdi0+cmVn bWFwLCBVMlBfUjAgKyAoVTJQX1JFR19TSVpFICogaSksCisJCQkJVTJQX1IwX0hPU1RfREVWSUNF LCAwKTsKKwkJYnJlYWs7CisJfQorfQorCitzdGF0aWMgaW50IGR3YzNfbWVzb25fZzEyYV91c2Iy X2luaXQoc3RydWN0IGR3YzNfbWVzb25fZzEyYSAqcHJpdikKK3sKKwllbnVtIHVzYl9kcl9tb2Rl IGlkX21vZGU7CisJdTMyIHZhbDsKKwlpbnQgaTsKKworCS8qIFJlYWQgSUQgY3VycmVudCBsZXZl bCAqLworCXJlZ21hcF9yZWFkKHByaXYtPnJlZ21hcCwgVVNCX1I1LCAmdmFsKTsKKwlpZiAodmFs ICYgVVNCX1I1X0lEX0RJR19DVVJSKQorCQlpZF9tb2RlID0gVVNCX0RSX01PREVfUEVSSVBIRVJB TDsKKwllbHNlCisJCWlkX21vZGUgPSBVU0JfRFJfTU9ERV9IT1NUOworCisJZGV2X2luZm8ocHJp di0+ZGV2LCAiSUQgbW9kZSAlc1xuIiwKKwkJIGlkX21vZGUgPT0gIFVTQl9EUl9NT0RFX0hPU1Qg PyAiaG9zdCIgOiAicGVyaXBoZXJhbCIpOworCisJZm9yIChpID0gMCA7IGkgPCBVU0IyX01BWF9Q SFkgOyArK2kpIHsKKwkJaWYgKCFwcml2LT5waHlzW2ldKQorCQkJY29udGludWU7CisKKwkJcmVn bWFwX3VwZGF0ZV9iaXRzKHByaXYtPnJlZ21hcCwgVTJQX1IwICsgKFUyUF9SRUdfU0laRSAqIGkp LAorCQkJCSAgIFUyUF9SMF9QT1dFUl9PTl9SRVNFVCwKKwkJCQkgICBVMlBfUjBfUE9XRVJfT05f UkVTRVQpOworCisJCWlmIChwcml2LT5waHlfbW9kZXNbaV0gPT0gVVNCX0RSX01PREVfUEVSSVBI RVJBTCB8fAorCQkgICAgKHByaXYtPnBoeV9tb2Rlc1tpXSA9PSBVU0JfRFJfTU9ERV9PVEcgJiYK KwkJICAgICBpZF9tb2RlID09IFVTQl9EUl9NT0RFX1BFUklQSEVSQUwpKSB7CisJCQlkd2MzX21l c29uX2cxMmFfdXNiMl9zZXRfbW9kZShwcml2LCBpLAorCQkJCQkJICAgICAgVVNCX0RSX01PREVf UEVSSVBIRVJBTCk7CisKKwkJCWlmIChwcml2LT5waHlfbW9kZXNbaV0gPT0gVVNCX0RSX01PREVf T1RHKQorCQkJCXByaXYtPm90Z19waHlfbW9kZSA9IFBIWV9NT0RFX1VTQl9ERVZJQ0U7CisJCX0g ZWxzZSB7CisJCQlkd2MzX21lc29uX2cxMmFfdXNiMl9zZXRfbW9kZShwcml2LCBpLAorCQkJCQkJ ICAgICAgVVNCX0RSX01PREVfSE9TVCk7CisKKwkJCWlmIChwcml2LT5waHlfbW9kZXNbaV0gPT0g VVNCX0RSX01PREVfT1RHKQorCQkJCXByaXYtPm90Z19waHlfbW9kZSA9IFBIWV9NT0RFX1VTQl9I T1NUOworCQl9CisKKwkJcmVnbWFwX3VwZGF0ZV9iaXRzKHByaXYtPnJlZ21hcCwgVTJQX1IwICsg KFUyUF9SRUdfU0laRSAqIGkpLAorCQkJCSAgIFUyUF9SMF9QT1dFUl9PTl9SRVNFVCwgMCk7CisJ fQorCisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyB2b2lkIGR3YzNfbWVzb25fZzEyYV91c2IzX2lu aXQoc3RydWN0IGR3YzNfbWVzb25fZzEyYSAqcHJpdikKK3sKKwlyZWdtYXBfdXBkYXRlX2JpdHMo cHJpdi0+cmVnbWFwLCBVU0JfUkVHX09GRlNFVCArIFVTQl9SMywKKwkJCVVTQl9SM19QMzBfU1ND X1JBTkdFX01BU0sgfAorCQkJVVNCX1IzX1AzMF9SRUZfU1NQX0VOLAorCQkJVVNCX1IzX1AzMF9T U0NfRU5BQkxFIHwKKwkJCUZJRUxEX1BSRVAoVVNCX1IzX1AzMF9TU0NfUkFOR0VfTUFTSywgMikg fAorCQkJVVNCX1IzX1AzMF9SRUZfU1NQX0VOKTsKKwl1ZGVsYXkoMik7CisKKwlyZWdtYXBfdXBk YXRlX2JpdHMocHJpdi0+cmVnbWFwLCBVU0JfUkVHX09GRlNFVCArIFVTQl9SMiwKKwkJCVVTQl9S Ml9QMzBfUENTX1RYX0RFRU1QSF8zUDVEQl9NQVNLLAorCQkJRklFTERfUFJFUChVU0JfUjJfUDMw X1BDU19UWF9ERUVNUEhfM1A1REJfTUFTSywgMHgxNSkpOworCisJcmVnbWFwX3VwZGF0ZV9iaXRz KHByaXYtPnJlZ21hcCwgVVNCX1JFR19PRkZTRVQgKyBVU0JfUjIsCisJCQlVU0JfUjJfUDMwX1BD U19UWF9ERUVNUEhfNkRCX01BU0ssCisJCQlGSUVMRF9QUkVQKFVTQl9SMl9QMzBfUENTX1RYX0RF RU1QSF82REJfTUFTSywgMHgyMCkpOworCisJdWRlbGF5KDIpOworCisJcmVnbWFwX3VwZGF0ZV9i aXRzKHByaXYtPnJlZ21hcCwgVVNCX1JFR19PRkZTRVQgKyBVU0JfUjEsCisJCQlVU0JfUjFfVTNI X0hPU1RfUE9SVF9QT1dFUl9DT05UUk9MX1BSRVNFTlQsCisJCQlVU0JfUjFfVTNIX0hPU1RfUE9S VF9QT1dFUl9DT05UUk9MX1BSRVNFTlQpOworCisJcmVnbWFwX3VwZGF0ZV9iaXRzKHByaXYtPnJl Z21hcCwgVVNCX1JFR19PRkZTRVQgKyBVU0JfUjEsCisJCQlVU0JfUjFfUDMwX1BDU19UWF9TV0lO R19GVUxMX01BU0ssCisJCQlGSUVMRF9QUkVQKFVTQl9SMV9QMzBfUENTX1RYX1NXSU5HX0ZVTExf TUFTSywgMTI3KSk7Cit9CisKK3N0YXRpYyB2b2lkIGR3YzNfbWVzb25fZzEyYV91c2JfaW5pdF9t b2RlKHN0cnVjdCBkd2MzX21lc29uX2cxMmEgKnByaXYsCisJCQkJCSAgYm9vbCBpc19wZXJpcGhl cmFsKQoreworCWlmIChpc19wZXJpcGhlcmFsKSB7CisJCXJlZ21hcF91cGRhdGVfYml0cyhwcml2 LT5yZWdtYXAsIFVTQl9SRUdfT0ZGU0VUICsgVVNCX1IwLAorCQkJCVVTQl9SMF9VMkRfQUNULCBV U0JfUjBfVTJEX0FDVCk7CisJCXJlZ21hcF91cGRhdGVfYml0cyhwcml2LT5yZWdtYXAsIFVTQl9S RUdfT0ZGU0VUICsgVVNCX1IwLAorCQkJCVVTQl9SMF9VMkRfU1NfU0NBTEVET1dOX01PREVfTUFT SywgMCk7CisJCXJlZ21hcF91cGRhdGVfYml0cyhwcml2LT5yZWdtYXAsIFVTQl9SRUdfT0ZGU0VU ICsgVVNCX1I0LAorCQkJCVVTQl9SNF9QMjFfU0xFRVBfTTAsIFVTQl9SNF9QMjFfU0xFRVBfTTAp OworCX0gZWxzZSB7CisJCXJlZ21hcF91cGRhdGVfYml0cyhwcml2LT5yZWdtYXAsIFVTQl9SRUdf T0ZGU0VUICsgVVNCX1IwLAorCQkJCVVTQl9SMF9VMkRfQUNULCAwKTsKKwkJcmVnbWFwX3VwZGF0 ZV9iaXRzKHByaXYtPnJlZ21hcCwgVVNCX1JFR19PRkZTRVQgKyBVU0JfUjQsCisJCQkJVVNCX1I0 X1AyMV9TTEVFUF9NMCwgMCk7CisJfQorfQorCitzdGF0aWMgaW50IGR3YzNfbWVzb25fZzEyYV91 c2JfaW5pdChzdHJ1Y3QgZHdjM19tZXNvbl9nMTJhICpwcml2KQoreworCWludCByZXQ7CisKKwly ZXQgPSBkd2MzX21lc29uX2cxMmFfdXNiMl9pbml0KHByaXYpOworCWlmIChyZXQpCisJCXJldHVy biByZXQ7CisKKwlyZWdtYXBfdXBkYXRlX2JpdHMocHJpdi0+cmVnbWFwLCBVU0JfUkVHX09GRlNF VCArIFVTQl9SMSwKKwkJCVVTQl9SMV9VM0hfRkxBREpfMzBNSFpfUkVHX01BU0ssCisJCQlGSUVM RF9QUkVQKFVTQl9SMV9VM0hfRkxBREpfMzBNSFpfUkVHX01BU0ssIDB4MjApKTsKKworCXJlZ21h cF91cGRhdGVfYml0cyhwcml2LT5yZWdtYXAsIFVTQl9SRUdfT0ZGU0VUICsgVVNCX1I1LAorCQkJ VVNCX1I1X0lEX0RJR19FTl8wLAorCQkJVVNCX1I1X0lEX0RJR19FTl8wKTsKKwlyZWdtYXBfdXBk YXRlX2JpdHMocHJpdi0+cmVnbWFwLCBVU0JfUkVHX09GRlNFVCArIFVTQl9SNSwKKwkJCVVTQl9S NV9JRF9ESUdfRU5fMSwKKwkJCVVTQl9SNV9JRF9ESUdfRU5fMSk7CisJcmVnbWFwX3VwZGF0ZV9i aXRzKHByaXYtPnJlZ21hcCwgVVNCX1JFR19PRkZTRVQgKyBVU0JfUjUsCisJCQlVU0JfUjVfSURf RElHX1RIX01BU0ssCisJCQlGSUVMRF9QUkVQKFVTQl9SNV9JRF9ESUdfVEhfTUFTSywgMHhmZikp OworCisJLyogSWYgd2UgaGF2ZSBhbiBhY3R1YWwgU3VwZXJTcGVlZCBwb3J0LCBpbml0aWFsaXpl IGl0ICovCisJaWYgKHByaXYtPnVzYjNfcG9ydHMpCisJCWR3YzNfbWVzb25fZzEyYV91c2IzX2lu aXQocHJpdik7CisKKwlkd2MzX21lc29uX2cxMmFfdXNiX2luaXRfbW9kZShwcml2LAorCQkJCShw cml2LT5vdGdfcGh5X21vZGUgPT0gUEhZX01PREVfVVNCX0RFVklDRSkpOworCisJcmV0dXJuIDA7 Cit9CisKK3N0YXRpYyBjb25zdCBzdHJ1Y3QgcmVnbWFwX2NvbmZpZyBwaHlfbWVzb25fZzEyYV91 c2IzX3JlZ21hcF9jb25mID0geworCS5yZWdfYml0cyA9IDgsCisJLnZhbF9iaXRzID0gMzIsCisJ LnJlZ19zdHJpZGUgPSA0LAorCS5tYXhfcmVnaXN0ZXIgPSBVU0JfUkVHX09GRlNFVCArIFVTQl9S NSwKK307CisKK3N0YXRpYyBpbnQgZHdjM19tZXNvbl9nMTJhX2dldF9waHlzKHN0cnVjdCBkd2Mz X21lc29uX2cxMmEgKnByaXYpCit7CisJc3RydWN0IGRldmljZV9ub2RlICpwb3J0LCAqcGh5X25v ZGU7CisJc3RydWN0IG9mX3BoYW5kbGVfYXJncyBhcmdzOworCWVudW0gdXNiX2RyX21vZGUgbW9k ZTsKKwljb25zdCBjaGFyICpkcl9tb2RlOworCXN0cnVjdCBwaHkgKnBoeTsKKwlpbnQgcmV0LCBp OworCisJZm9yIChpID0gMCA7IGkgPCBNQVhfUEhZIDsgKytpKSB7CisJCXBvcnQgPSBvZl9ncmFw aF9nZXRfcG9ydF9ieV9pZChwcml2LT5kZXYtPm9mX25vZGUsIGkpOworCisJCS8qIElnbm9yZSBw b3J0IGlmIG5vdCBkZWZpbmVkIG9yIGRpc2FibGVkICovCisJCWlmICghb2ZfZGV2aWNlX2lzX2F2 YWlsYWJsZShwb3J0KSkgeworCQkJb2Zfbm9kZV9wdXQocG9ydCk7CisJCQljb250aW51ZTsKKwkJ fQorCisJCS8qIEdldCBhc3NvY2lhdGVkIFBIWSAqLworCQlwaHkgPSBvZl9waHlfZ2V0KHBvcnQs IE5VTEwpOworCQlpZiAoSVNfRVJSKHBoeSkpIHsKKwkJCW9mX25vZGVfcHV0KHBvcnQpOworCQkJ cmV0ID0gUFRSX0VSUihwaHkpOworCQkJZ290byBlcnJfcGh5X2dldDsKKwkJfQorCisJCW9mX25v ZGVfcHV0KHBvcnQpOworCisJCS8qIEdldCBwaHkgZHJfbW9kZSAqLworCQlyZXQgPSBvZl9wYXJz ZV9waGFuZGxlX3dpdGhfYXJncyhwb3J0LCAicGh5cyIsICIjcGh5LWNlbGxzIiwKKwkJCQkJCSAw LCAmYXJncyk7CisJCWlmIChyZXQpIHsKKwkJCW9mX25vZGVfcHV0KHBvcnQpOworCQkJZ290byBl cnJfcGh5X2dldDsKKwkJfQorCisJCXBoeV9ub2RlID0gYXJncy5ucDsKKworCQlyZXQgPSBvZl9w cm9wZXJ0eV9yZWFkX3N0cmluZyhwaHlfbm9kZSwgImRyX21vZGUiLCAmZHJfbW9kZSk7CisJCWlm IChyZXQpIHsKKwkJCWRyX21vZGUgPSAidW5rbm93biI7CisJCQltb2RlID0gVVNCX0RSX01PREVf VU5LTk9XTjsKKwkJfSBlbHNlIHsKKwkJCWlmICghc3RyY21wKGRyX21vZGUsICJob3N0IikpCisJ CQkJbW9kZSA9IFVTQl9EUl9NT0RFX0hPU1Q7CisJCQllbHNlIGlmICghc3RyY21wKGRyX21vZGUs ICJvdGciKSkKKwkJCQltb2RlID0gVVNCX0RSX01PREVfT1RHOworCQkJZWxzZSBpZiAoIXN0cmNt cChkcl9tb2RlLCAicGVyaXBoZXJhbCIpKQorCQkJCW1vZGUgPSBVU0JfRFJfTU9ERV9QRVJJUEhF UkFMOworCQkJZWxzZSB7CisJCQkJbW9kZSA9IFVTQl9EUl9NT0RFX1VOS05PV047CisJCQkJZHJf bW9kZSA9ICJ1bmtub3duIjsKKwkJCX0KKwkJfQorCisJCWRldl9pbmZvKHByaXYtPmRldiwgInBv cnQlZDogJXMgbW9kZSAlc1xuIiwKKwkJCSBpLCBvZl9ub2RlX2Z1bGxfbmFtZShwaHlfbm9kZSks IGRyX21vZGUpOworCisJCW9mX25vZGVfcHV0KHBoeV9ub2RlKTsKKworCQlwcml2LT5waHlfbW9k ZXNbaV0gPSBtb2RlOworCQlwcml2LT5waHlzW2ldID0gcGh5OworCisJCWlmIChpID09IFVTQjNf UEhZKQorCQkJcHJpdi0+dXNiM19wb3J0cysrOworCQllbHNlCisJCQlwcml2LT51c2IyX3BvcnRz Kys7CisJfQorCisJZGV2X2luZm8ocHJpdi0+ZGV2LCAidXNiMiBwb3J0czogJWRcbiIsIHByaXYt PnVzYjJfcG9ydHMpOworCWRldl9pbmZvKHByaXYtPmRldiwgInVzYjMgcG9ydHM6ICVkXG4iLCBw cml2LT51c2IzX3BvcnRzKTsKKworCXJldHVybiAwOworCitlcnJfcGh5X2dldDoKKwlmb3IgKGkg PSAwIDsgaSA8IE1BWF9QSFkgOyArK2kpCisJCWlmIChwcml2LT5waHlzW2ldKQorCQkJcGh5X3B1 dChwcml2LT5waHlzW2ldKTsKKworCXJldHVybiByZXQ7Cit9CisKK3N0YXRpYyBpbnQgZHdjM19t ZXNvbl9nMTJhX21vZGVfZm9yY2VfZ2V0KHZvaWQgKmRhdGEsIHU2NCAqdmFsKQoreworCXN0cnVj dCBkd2MzX21lc29uX2cxMmEgKnByaXYgPSBkYXRhOworCisJaWYgKHByaXYtPm90Z19waHlfbW9k ZSA9PSBQSFlfTU9ERV9VU0JfSE9TVCkKKwkJKnZhbCA9IDE7CisJZWxzZSBpZiAocHJpdi0+b3Rn X3BoeV9tb2RlID09IFBIWV9NT0RFX1VTQl9ERVZJQ0UpCisJCSp2YWwgPSAwOworCWVsc2UKKwkJ cmV0dXJuIC1FSU5WQUw7CisKKwlyZXR1cm4gMDsKK30KKworc3RhdGljIGludCBkd2MzX21lc29u X2cxMmFfbW9kZV9mb3JjZV9zZXQodm9pZCAqZGF0YSwgdTY0IHZhbCkKK3sKKwlzdHJ1Y3QgZHdj M19tZXNvbl9nMTJhICpwcml2ID0gZGF0YTsKKwlpbnQgaTsKKworCWlmICgodmFsICYmIHByaXYt Pm90Z19waHlfbW9kZSA9PSBQSFlfTU9ERV9VU0JfSE9TVCkgfHwKKwkgICAgKCF2YWwgJiYgcHJp di0+b3RnX3BoeV9tb2RlID09IFBIWV9NT0RFX1VTQl9ERVZJQ0UpKQorCQlyZXR1cm4gMDsKKwor CWZvciAoaSA9IDAgOyBpIDwgVVNCMl9NQVhfUEhZIDsgKytpKSB7CisJCWlmICghcHJpdi0+cGh5 c1tpXSkKKwkJCWNvbnRpbnVlOworCisJCWlmIChwcml2LT5waHlfbW9kZXNbaV0gIT0gVVNCX0RS X01PREVfT1RHKQorCQkJY29udGludWU7CisKKwkJaWYgKHZhbCkgeworCQkJZGV2X2luZm8ocHJp di0+ZGV2LCAic3dpdGNoaW5nIHRvIEhvc3QgTW9kZVxuIik7CisKKwkJCWR3YzNfbWVzb25fZzEy YV91c2IyX3NldF9tb2RlKHByaXYsIGksCisJCQkJCQkgICAgICBVU0JfRFJfTU9ERV9IT1NUKTsK KworCQkJZHdjM19tZXNvbl9nMTJhX3VzYl9pbml0X21vZGUocHJpdiwgZmFsc2UpOworCisJCQlw cml2LT5vdGdfcGh5X21vZGUgPSBQSFlfTU9ERV9VU0JfSE9TVDsKKwkJfSBlbHNlIHsKKwkJCWRl dl9pbmZvKHByaXYtPmRldiwgInN3aXRjaGluZyB0byBEZXZpY2UgTW9kZVxuIik7CisKKwkJCWR3 YzNfbWVzb25fZzEyYV91c2IyX3NldF9tb2RlKHByaXYsIGksCisJCQkJCQkgICAgICBVU0JfRFJf TU9ERV9QRVJJUEhFUkFMKTsKKworCQkJZHdjM19tZXNvbl9nMTJhX3VzYl9pbml0X21vZGUocHJp diwgdHJ1ZSk7CisKKwkJCXByaXYtPm90Z19waHlfbW9kZSA9IFBIWV9NT0RFX1VTQl9ERVZJQ0U7 CisJCX0KKworCQlyZXR1cm4gcGh5X3NldF9tb2RlKHByaXYtPnBoeXNbaV0sIHByaXYtPm90Z19w aHlfbW9kZSk7CisJfQorCisJcmV0dXJuIC1FSU5WQUw7Cit9CisKK0RFRklORV9ERUJVR0ZTX0FU VFJJQlVURShkd2MzX21lc29uX2cxMmFfbW9kZV9mb3JjZV9mb3BzLAorCQkJIGR3YzNfbWVzb25f ZzEyYV9tb2RlX2ZvcmNlX2dldCwKKwkJCSBkd2MzX21lc29uX2cxMmFfbW9kZV9mb3JjZV9zZXQs ICIlbGx1XG4iKTsKKworc3RhdGljIGludCBkd2MzX21lc29uX2cxMmFfb3RnX2lkX2dldCh2b2lk ICpkYXRhLCB1NjQgKnZhbCkKK3sKKwlzdHJ1Y3QgZHdjM19tZXNvbl9nMTJhICpwcml2ID0gZGF0 YTsKKwl1MzIgcmVnOworCisJcmVnbWFwX3JlYWQocHJpdi0+cmVnbWFwLCBVU0JfUjUsICZyZWcp OworCisJKnZhbCA9IChyZWcgJiBVU0JfUjVfSURfRElHX0NVUlIpOworCisJcmV0dXJuIDA7Cit9 CisKK0RFRklORV9ERUJVR0ZTX0FUVFJJQlVURShkd2MzX21lc29uX2cxMmFfb3RnX2lkX2ZvcHMs CisJCQkgZHdjM19tZXNvbl9nMTJhX290Z19pZF9nZXQsIE5VTEwsICIlbGx1XG4iKTsKKworLyog V2UgcHJvdmlkZSBhIERlYnVnRlMgaW50ZXJmYWNlIHRvIGdldCB0aGUgSUQgdmFsdWUgYW5kIGZv cmNlIE9URyBzd2l0Y2ggKi8KK3N0YXRpYyBpbnQgZHdjM19tZXNvbl9nMTJhX2RlYnVnZnNfaW5p dChzdHJ1Y3QgZHdjM19tZXNvbl9nMTJhICpwcml2KQoreworCXByaXYtPnJvb3QgPSBkZWJ1Z2Zz X2NyZWF0ZV9kaXIoImR3YzMtbWVzb24tZzEyYSIsIE5VTEwpOworCWlmIChJU19FUlIocHJpdi0+ cm9vdCkpCisJCXJldHVybiBQVFJfRVJSKHByaXYtPnJvb3QpOworCisJZGVidWdmc19jcmVhdGVf ZmlsZSgibW9kZV9mb3JjZSIsIDA2MDAsIHByaXYtPnJvb3QsIHByaXYsCisJCQkgICAgJmR3YzNf bWVzb25fZzEyYV9tb2RlX2ZvcmNlX2ZvcHMpOworCisJZGVidWdmc19jcmVhdGVfZmlsZSgib3Rn X2lkIiwgMDQwMCwgcHJpdi0+cm9vdCwgcHJpdiwKKwkJCSAgICAmZHdjM19tZXNvbl9nMTJhX290 Z19pZF9mb3BzKTsKKworCXJldHVybiAwOworfQorCitzdGF0aWMgaW50IGR3YzNfbWVzb25fZzEy YV9wcm9iZShzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2KQoreworCXN0cnVjdCBkd2MzX21l c29uX2cxMmEJKnByaXY7CisJc3RydWN0IGRldmljZQkJKmRldiA9ICZwZGV2LT5kZXY7CisJc3Ry dWN0IGRldmljZV9ub2RlCSpucCA9IGRldi0+b2Zfbm9kZTsKKwl2b2lkIF9faW9tZW0gKmJhc2U7 CisJc3RydWN0IHJlc291cmNlICpyZXM7CisJaW50IHJldCwgaTsKKworCXByaXYgPSBkZXZtX2t6 YWxsb2MoZGV2LCBzaXplb2YoKnByaXYpLCBHRlBfS0VSTkVMKTsKKwlpZiAoIXByaXYpCisJCXJl dHVybiAtRU5PTUVNOworCisJcmVzID0gcGxhdGZvcm1fZ2V0X3Jlc291cmNlKHBkZXYsIElPUkVT T1VSQ0VfTUVNLCAwKTsKKwliYXNlID0gZGV2bV9pb3JlbWFwX3Jlc291cmNlKGRldiwgcmVzKTsK KwlpZiAoSVNfRVJSKGJhc2UpKQorCQlyZXR1cm4gUFRSX0VSUihiYXNlKTsKKworCXByaXYtPnJl Z21hcCA9IGRldm1fcmVnbWFwX2luaXRfbW1pbyhkZXYsIGJhc2UsCisJCQkJCSAgICAgJnBoeV9t ZXNvbl9nMTJhX3VzYjNfcmVnbWFwX2NvbmYpOworCWlmIChJU19FUlIocHJpdi0+cmVnbWFwKSkK KwkJcmV0dXJuIFBUUl9FUlIocHJpdi0+cmVnbWFwKTsKKworCXByaXYtPmNsayA9IGRldm1fY2xr X2dldChkZXYsICJ1c2IiKTsKKwlpZiAoSVNfRVJSKHByaXYtPmNsaykpCisJCXJldHVybiBQVFJf RVJSKHByaXYtPmNsayk7CisKKwlyZXQgPSBjbGtfcHJlcGFyZV9lbmFibGUocHJpdi0+Y2xrKTsK KwlpZiAocmV0KQorCQlyZXR1cm4gcmV0OworCisJcGxhdGZvcm1fc2V0X2RydmRhdGEocGRldiwg cHJpdik7CisJcHJpdi0+ZGV2ID0gZGV2OworCisJcHJpdi0+cmVzZXQgPSBkZXZtX3Jlc2V0X2Nv bnRyb2xfZ2V0KGRldiwgInVzYiIpOworCWlmIChJU19FUlIocHJpdi0+cmVzZXQpKSB7CisJCXJl dCA9IFBUUl9FUlIocHJpdi0+cmVzZXQpOworCQlkZXZfZXJyKGRldiwgImZhaWxlZCB0byBnZXQg ZGV2aWNlIHJlc2V0LCBlcnI9JWRcbiIsIHJldCk7CisJCXJldHVybiByZXQ7CisJfQorCisJcmV0 ID0gcmVzZXRfY29udHJvbF9yZXNldChwcml2LT5yZXNldCk7CisJaWYgKHJldCkKKwkJcmV0dXJu IHJldDsKKworCXJldCA9IGR3YzNfbWVzb25fZzEyYV9nZXRfcGh5cyhwcml2KTsKKwlpZiAocmV0 KQorCQlyZXR1cm4gcmV0OworCisJZHdjM19tZXNvbl9nMTJhX3VzYl9pbml0KHByaXYpOworCisJ LyogSW5pdCBQSFlzICovCisJZm9yIChpID0gMCA7IGkgPCBNQVhfUEhZIDsgKytpKSB7CisJCWlm IChwcml2LT5waHlzW2ldKSB7CisJCQlyZXQgPSBwaHlfaW5pdChwcml2LT5waHlzW2ldKTsKKwkJ CWlmIChyZXQpCisJCQkJZ290byBlcnJfcGh5c19wdXQ7CisJCX0KKwl9CisKKwkvKiBTZXQgT1RH IFBIWSBtb2RlICovCisJZm9yIChpID0gMCA7IGkgPCBNQVhfUEhZIDsgKytpKSB7CisJCWlmIChw cml2LT5waHlzW2ldICYmIHByaXYtPnBoeV9tb2Rlc1tpXSA9PSBVU0JfRFJfTU9ERV9PVEcpIHsK KwkJCXJldCA9IHBoeV9zZXRfbW9kZShwcml2LT5waHlzW2ldLCBwcml2LT5vdGdfcGh5X21vZGUp OworCQkJaWYgKHJldCkKKwkJCQlnb3RvIGVycl9waHlzX3B1dDsKKwkJfQorCX0KKworCXJldCA9 IG9mX3BsYXRmb3JtX3BvcHVsYXRlKG5wLCBOVUxMLCBOVUxMLCBkZXYpOworCWlmIChyZXQpIHsK KwkJY2xrX2Rpc2FibGVfdW5wcmVwYXJlKHByaXYtPmNsayk7CisJCWNsa19wdXQocHJpdi0+Y2xr KTsKKworCQlnb3RvIGVycl9waHlzX2V4aXQ7CisJfQorCisJcG1fcnVudGltZV9zZXRfYWN0aXZl KGRldik7CisJcG1fcnVudGltZV9lbmFibGUoZGV2KTsKKwlwbV9ydW50aW1lX2dldF9zeW5jKGRl dik7CisKKwlpZiAoZHdjM19tZXNvbl9nMTJhX2RlYnVnZnNfaW5pdChwcml2KSkKKwkJZGV2X2Ri ZyhkZXYsICJGYWlsZWQgdG8gYWRkIERlYnVnRlMgaW50ZXJmYWNlXG4iKTsKKworCXJldHVybiAw OworCitlcnJfcGh5c19leGl0OgorCWZvciAoaSA9IDAgOyBpIDwgTUFYX1BIWSA7ICsraSkKKwkJ aWYgKHByaXYtPnBoeXNbaV0pCisJCQlwaHlfZXhpdChwcml2LT5waHlzW2ldKTsKKworZXJyX3Bo eXNfcHV0OgorCWZvciAoaSA9IDAgOyBpIDwgTUFYX1BIWSA7ICsraSkKKwkJaWYgKHByaXYtPnBo eXNbaV0pCisJCQlwaHlfcHV0KHByaXYtPnBoeXNbaV0pOworCisJcmV0dXJuIHJldDsKK30KKwor c3RhdGljIGludCBkd2MzX21lc29uX2cxMmFfcmVtb3ZlKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2Ug KnBkZXYpCit7CisJc3RydWN0IGR3YzNfbWVzb25fZzEyYSAqcHJpdiA9IHBsYXRmb3JtX2dldF9k cnZkYXRhKHBkZXYpOworCXN0cnVjdCBkZXZpY2UgKmRldiA9ICZwZGV2LT5kZXY7CisJaW50IGk7 CisKKwlkZWJ1Z2ZzX3JlbW92ZV9yZWN1cnNpdmUocHJpdi0+cm9vdCk7CisKKwlvZl9wbGF0Zm9y bV9kZXBvcHVsYXRlKGRldik7CisKKwlmb3IgKGkgPSAwIDsgaSA8IE1BWF9QSFkgOyArK2kpCisJ CWlmIChwcml2LT5waHlzW2ldKQorCQkJcGh5X2V4aXQocHJpdi0+cGh5c1tpXSk7CisKKwlmb3Ig KGkgPSAwIDsgaSA8IE1BWF9QSFkgOyArK2kpCisJCWlmIChwcml2LT5waHlzW2ldKQorCQkJcGh5 X3B1dChwcml2LT5waHlzW2ldKTsKKworCWNsa19kaXNhYmxlX3VucHJlcGFyZShwcml2LT5jbGsp OworCWNsa19wdXQocHJpdi0+Y2xrKTsKKworCXBtX3J1bnRpbWVfZGlzYWJsZShkZXYpOworCXBt X3J1bnRpbWVfcHV0X25vaWRsZShkZXYpOworCXBtX3J1bnRpbWVfc2V0X3N1c3BlbmRlZChkZXYp OworCisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyBpbnQgX19tYXliZV91bnVzZWQgZHdjM19tZXNv bl9nMTJhX3J1bnRpbWVfc3VzcGVuZChzdHJ1Y3QgZGV2aWNlICpkZXYpCit7CisJc3RydWN0IGR3 YzNfbWVzb25fZzEyYQkqcHJpdiA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOworCisJY2xrX2Rpc2Fi bGUocHJpdi0+Y2xrKTsKKworCXJldHVybiAwOworfQorCitzdGF0aWMgaW50IF9fbWF5YmVfdW51 c2VkIGR3YzNfbWVzb25fZzEyYV9ydW50aW1lX3Jlc3VtZShzdHJ1Y3QgZGV2aWNlICpkZXYpCit7 CisJc3RydWN0IGR3YzNfbWVzb25fZzEyYQkqcHJpdiA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOwor CisJcmV0dXJuIGNsa19lbmFibGUocHJpdi0+Y2xrKTsKK30KKworc3RhdGljIGludCBfX21heWJl X3VudXNlZCBkd2MzX21lc29uX2cxMmFfc3VzcGVuZChzdHJ1Y3QgZGV2aWNlICpkZXYpCit7CisJ c3RydWN0IGR3YzNfbWVzb25fZzEyYSAqcHJpdiA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOworCWlu dCBpOworCisJZm9yIChpID0gMCA7IGkgPCBNQVhfUEhZIDsgKytpKQorCQlpZiAocHJpdi0+cGh5 c1tpXSkKKwkJCXBoeV9leGl0KHByaXYtPnBoeXNbaV0pOworCisJcmVzZXRfY29udHJvbF9hc3Nl cnQocHJpdi0+cmVzZXQpOworCisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyBpbnQgX19tYXliZV91 bnVzZWQgZHdjM19tZXNvbl9nMTJhX3Jlc3VtZShzdHJ1Y3QgZGV2aWNlICpkZXYpCit7CisJc3Ry dWN0IGR3YzNfbWVzb25fZzEyYSAqcHJpdiA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOworCWludCBp LCByZXQ7CisKKwlyZXNldF9jb250cm9sX2RlYXNzZXJ0KHByaXYtPnJlc2V0KTsKKworCWR3YzNf bWVzb25fZzEyYV91c2JfaW5pdChwcml2KTsKKworCS8qIEluaXQgUEhZcyAqLworCWZvciAoaSA9 IDAgOyBpIDwgTUFYX1BIWSA7ICsraSkgeworCQlpZiAocHJpdi0+cGh5c1tpXSkgeworCQkJcmV0 ID0gcGh5X2luaXQocHJpdi0+cGh5c1tpXSk7CisJCQlpZiAocmV0KQorCQkJCXJldHVybiByZXQ7 CisJCX0KKwl9CisKKwlyZXR1cm4gMDsKK30KKworc3RhdGljIGNvbnN0IHN0cnVjdCBkZXZfcG1f b3BzIGR3YzNfbWVzb25fZzEyYV9kZXZfcG1fb3BzID0geworCVNFVF9TWVNURU1fU0xFRVBfUE1f T1BTKGR3YzNfbWVzb25fZzEyYV9zdXNwZW5kLCBkd2MzX21lc29uX2cxMmFfcmVzdW1lKQorCVNF VF9SVU5USU1FX1BNX09QUyhkd2MzX21lc29uX2cxMmFfcnVudGltZV9zdXNwZW5kLAorCQkJZHdj M19tZXNvbl9nMTJhX3J1bnRpbWVfcmVzdW1lLCBOVUxMKQorfTsKKworc3RhdGljIGNvbnN0IHN0 cnVjdCBvZl9kZXZpY2VfaWQgZHdjM19tZXNvbl9nMTJhX21hdGNoW10gPSB7CisJeyAuY29tcGF0 aWJsZSA9ICJhbWxvZ2ljLG1lc29uLWcxMmEtdXNiLWN0cmwiIH0sCisJeyAvKiBTZW50aW5lbCAq LyB9Cit9OworTU9EVUxFX0RFVklDRV9UQUJMRShvZiwgZHdjM19tZXNvbl9nMTJhX21hdGNoKTsK Kworc3RhdGljIHN0cnVjdCBwbGF0Zm9ybV9kcml2ZXIgZHdjM19tZXNvbl9nMTJhX2RyaXZlciA9 IHsKKwkucHJvYmUJCT0gZHdjM19tZXNvbl9nMTJhX3Byb2JlLAorCS5yZW1vdmUJCT0gZHdjM19t ZXNvbl9nMTJhX3JlbW92ZSwKKwkuZHJpdmVyCQk9IHsKKwkJLm5hbWUJPSAiZHdjMy1tZXNvbi1n MTJhIiwKKwkJLm9mX21hdGNoX3RhYmxlID0gZHdjM19tZXNvbl9nMTJhX21hdGNoLAorCQkucG0J PSAmZHdjM19tZXNvbl9nMTJhX2Rldl9wbV9vcHMsCisJfSwKK307CisKK21vZHVsZV9wbGF0Zm9y bV9kcml2ZXIoZHdjM19tZXNvbl9nMTJhX2RyaXZlcik7CitNT0RVTEVfTElDRU5TRSgiR1BMIHYy Iik7CitNT0RVTEVfREVTQ1JJUFRJT04oIkFtbG9naWMgTWVzb24gRzEyQSBVU0IgR2x1ZSBMYXll ciIpOworTU9EVUxFX0FVVEhPUigiTmVpbCBBcm1zdHJvbmcgPG5hcm1zdHJvbmdAYmF5bGlicmUu Y29tPiIpOwo= 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=-9.0 required=3.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable 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 29231C282CA for ; Tue, 12 Feb 2019 15:16:29 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id EC72A2075C for ; Tue, 12 Feb 2019 15:16:28 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="msCt+Kp0"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=baylibre-com.20150623.gappssmtp.com header.i=@baylibre-com.20150623.gappssmtp.com header.b="iQO8kbl1" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org EC72A2075C Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+infradead-linux-arm-kernel=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=p8B+5XEgsMlJyX5MYuPDG+XrW7tYI9B08+YqMPd9x1w=; b=msCt+Kp0QpKUME cGFmxtb3DhIJ2mXA7OXu+HQT6kYoqYnLRLyOuXe9Ljq5yUTiqOt6F5gR7uOn4WgQASxDuY8b6V/pJ lyfG5GfzfiZyltRox6zlr1ylIXrItkjz1bFo93aS0j8aFXZzVTWRKtIh2nPlXyPiuJMl6UYIjmAN9 1tfWFm8UqT1NkDsU1dwWThN3Oa5woJWESpiYp+MXm0wYfuDv/useGOx9hYLjUwfVLPjCNHsUNNhOu GStRkdGAvUYnVCJh6PPh4joT8Z0cJzP6j8qh9wJvV4+EoM9oZR1GZOq7kiaP2wp7Rudu71km6TNkW qWySESBf+cgamXy/1mDA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gtZnO-0001E3-Nh; Tue, 12 Feb 2019 15:16:26 +0000 Received: from mail-wm1-x341.google.com ([2a00:1450:4864:20::341]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gtZlY-0006dj-82 for linux-arm-kernel@lists.infradead.org; Tue, 12 Feb 2019 15:14:45 +0000 Received: by mail-wm1-x341.google.com with SMTP id a62so3358166wmh.4 for ; Tue, 12 Feb 2019 07:14:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Gsq17jOveVaPpdOsp4iUxKzX6UR/llKkyBjljOGUIGI=; b=iQO8kbl1ees3MCrw+wTvgK6b9jiCQGIdWPH5R10FXFSyyc0JUWe0c8L1baaWsOO2w7 3td1WNXBuKXJbUosze4OsPuSLCFwK+vHGKm+biurRAudWDHoOhVDiLxNbqZkDsGo2jOt rUzug7jUMrcuomnQPQXt0BC5/38XOP3WayE6UmciufOOPLXdIgroZSpoUc04jcemCIUy uaV0q5vTGoDI6fZcgnPM4tuZLyWwgQnS5QhHUIAIeXsK8RHU5HDezLxXqHRkKa1ioYUH gE78W04/zPqxgP/obJKS9ieqCofGMwReyVo1D2oQB19W4kjpNu01+NNusKC74Up0hipz IDNA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Gsq17jOveVaPpdOsp4iUxKzX6UR/llKkyBjljOGUIGI=; b=TBrFbP8fSIg34flX5mRg4ZEHVKd6MaDeeBbhKViHwUnw7Wr1ibVtgbfPOEP75h9qmf Srawdsofjd3doOmn5bRBOUGQRwl6gfVwgo364d8tBm9m65gkvJUD4D1bEG1Tz9cMVgMH EwsE2egR1QRmWYruaWNulg5L/tBP1mnTL7T0WlXVD+RABeOAENedbVRvZ9P2ubQsKbk8 6Xeme0i9ZAov8zpabnskJP1b1q0gXD0lMpCbroOXeF+pWO+TiWFvpr+ObzxLG9LMiU9l k9glZOhvZos5oz8efelDeWfe0hrcLBjoAOjDWFB35+bX1sgaVbHYk8FtYVrE+3q6rknK 8IUw== X-Gm-Message-State: AHQUAuZaJIZrd30Ce2BM/8PQvjlb6Kp2oSuc4Dl0iapDrh8ej4tvj+EB +mqDcEZ3NsP0PFf49LZWon8E2Q== X-Google-Smtp-Source: AHgI3Ia0tlG9NoF2lsr6Zy/7oPVT7yf1S3TetRm1/tz233B6JwUmBSnPeF6lG9+13EF1t8wuWcK4KQ== X-Received: by 2002:a1c:400a:: with SMTP id n10mr3220840wma.56.1549984469970; Tue, 12 Feb 2019 07:14:29 -0800 (PST) Received: from bender.baylibre.local (lmontsouris-657-1-212-31.w90-63.abo.wanadoo.fr. [90.63.244.31]) by smtp.gmail.com with ESMTPSA id c1sm1589515wmb.14.2019.02.12.07.14.28 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 12 Feb 2019 07:14:28 -0800 (PST) From: Neil Armstrong To: gregkh@linuxfoundation.org, hminas@synopsys.com, balbi@kernel.org, kishon@ti.com Subject: [PATCH 8/8] usb: dwc3: Add Amlogic G12A DWC3 glue Date: Tue, 12 Feb 2019 16:14:13 +0100 Message-Id: <20190212151413.24632-9-narmstrong@baylibre.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190212151413.24632-1-narmstrong@baylibre.com> References: <20190212151413.24632-1-narmstrong@baylibre.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190212_071432_758502_AC2B595E X-CRM114-Status: GOOD ( 20.08 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-amlogic@lists.infradead.org, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Neil Armstrong Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+infradead-linux-arm-kernel=archiver.kernel.org@lists.infradead.org Adds support for Amlogic G12A USB Control Glue HW. The Amlogic G12A SoC Family embeds 2 USB Controllers : - a DWC3 IP configured as Host for USB2 and USB3 - a DWC2 IP configured as Peripheral USB2 Only A glue connects these both controllers to 2 USB2 PHYs, and optionnally to an USB3+PCIE Combo PHY shared with the PCIE controller. The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including routing of the OTG PHY between the DWC3 and DWC2 controllers, and setups the on-chip OTG mode selection for this PHY. The PHYs are childen of the Glue node since the Glue controls the interface with the PHY, not the DWC3 controller. The drivers collects the mode of each PHY and determine which PHY is to be routed between the DWC2 and DWC3 controllers. This drivers supports the on-probe setup of the OTG mode, and manually via a debugfs interface. The IRQ mode change detect is yet to be added in a future patchset, mainly due to lack of hardware to validate on. Signed-off-by: Neil Armstrong --- drivers/usb/dwc3/Kconfig | 9 + drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-meson-g12a.c | 650 +++++++++++++++++++++++++++++ 3 files changed, 660 insertions(+) create mode 100644 drivers/usb/dwc3/dwc3-meson-g12a.c diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 1a0404fda596..4335e5e76bbb 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -93,6 +93,15 @@ config USB_DWC3_KEYSTONE Support of USB2/3 functionality in TI Keystone2 platforms. Say 'Y' or 'M' here if you have one such device +config USB_DWC3_MESON_G12A + tristate "Amlogic Meson G12A Platforms" + depends on OF && COMMON_CLK + depends on ARCH_MESON || COMPILE_TEST + default USB_DWC3 + help + Support USB2/3 functionality in Amlogic G12A platforms. + Say 'Y' or 'M' if you have one such device. + config USB_DWC3_OF_SIMPLE tristate "Generic OF Simple Glue Layer" depends on OF && COMMON_CLK diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 6e3ef6144e5d..ae86da0dc5bd 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o obj-$(CONFIG_USB_DWC3_HAPS) += dwc3-haps.o obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o +obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c new file mode 100644 index 000000000000..abeff2d56b1d --- /dev/null +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -0,0 +1,650 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB Glue for Amlogic G12A SoCs + * + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong + */ + +/* + * The USB is organized with a glue around the DWC3 Controller IP as : + * - Control registers for each USB2 Ports + * - Control registers for the USB PHY layer + * - SuperSpeed PHY can be enabled only if port is used + * + * TOFIX: + * - Add dynamic OTG switching with ID change interrupt + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* USB Glue Control Registers */ + +#define USB_R0 0x00 + #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17) + #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18) + #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19) + #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29) + #define USB_R0_U2D_ACT BIT(31) + +#define USB_R1 0x04 + #define USB_R1_U3H_BIGENDIAN_GS BIT(0) + #define USB_R1_U3H_PME_ENABLE BIT(1) + #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(4, 2) + #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(9, 7) + #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(13, 12) + #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16) + #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17) + #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18) + #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19) + #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25) + +#define USB_R2 0x08 + #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20) + #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26) + +#define USB_R3 0x0c + #define USB_R3_P30_SSC_ENABLE BIT(0) + #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1) + #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4) + #define USB_R3_P30_REF_SSP_EN BIT(13) + +#define USB_R4 0x10 + #define USB_R4_P21_PORT_RESET_0 BIT(0) + #define USB_R4_P21_SLEEP_M0 BIT(1) + #define USB_R4_MEM_PD_MASK GENMASK(3, 2) + #define USB_R4_P21_ONLY BIT(4) + +#define USB_R5 0x14 + #define USB_R5_ID_DIG_SYNC BIT(0) + #define USB_R5_ID_DIG_REG BIT(1) + #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2) + #define USB_R5_ID_DIG_EN_0 BIT(4) + #define USB_R5_ID_DIG_EN_1 BIT(5) + #define USB_R5_ID_DIG_CURR BIT(6) + #define USB_R5_ID_DIG_IRQ BIT(7) + #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) + #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) + +/* USB2 Ports Control Registers */ + +#define U2P_R0 0x0 + #define U2P_R0_HOST_DEVICE BIT(0) + #define U2P_R0_POWER_OK BIT(1) + #define U2P_R0_HAST_MODE BIT(2) + #define U2P_R0_POWER_ON_RESET BIT(3) + #define U2P_R0_ID_PULLUP BIT(4) + #define U2P_R0_DRV_VBUS BIT(5) + +#define U2P_R1 0x4 + #define U2P_R1_PHY_READY BIT(0) + #define U2P_R1_ID_DIG BIT(1) + #define U2P_R1_OTG_SESSION_VALID BIT(2) + #define U2P_R1_VBUS_VALID BIT(3) + +#define MAX_PHY 5 + +#define USB2_MAX_PHY 4 +#define USB3_PHY 4 + +struct dwc3_meson_g12a { + struct device *dev; + struct regmap *regmap; + struct clk *clk; + struct reset_control *reset; + struct phy *phys[5]; + enum usb_dr_mode phy_modes[5]; + enum phy_mode otg_phy_mode; + unsigned int usb2_ports; + unsigned int usb3_ports; + struct dentry *root; +}; + +#define U2P_REG_SIZE 0x20 +#define USB_REG_OFFSET 0x80 + +static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv, + int i, enum usb_dr_mode mode) +{ + switch (mode) { + case USB_DR_MODE_HOST: + case USB_DR_MODE_OTG: + case USB_DR_MODE_UNKNOWN: + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, + U2P_R0_HOST_DEVICE); + break; + + case USB_DR_MODE_PERIPHERAL: + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, 0); + break; + } +} + +static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv) +{ + enum usb_dr_mode id_mode; + u32 val; + int i; + + /* Read ID current level */ + regmap_read(priv->regmap, USB_R5, &val); + if (val & USB_R5_ID_DIG_CURR) + id_mode = USB_DR_MODE_PERIPHERAL; + else + id_mode = USB_DR_MODE_HOST; + + dev_info(priv->dev, "ID mode %s\n", + id_mode == USB_DR_MODE_HOST ? "host" : "peripheral"); + + for (i = 0 ; i < USB2_MAX_PHY ; ++i) { + if (!priv->phys[i]) + continue; + + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_POWER_ON_RESET, + U2P_R0_POWER_ON_RESET); + + if (priv->phy_modes[i] == USB_DR_MODE_PERIPHERAL || + (priv->phy_modes[i] == USB_DR_MODE_OTG && + id_mode == USB_DR_MODE_PERIPHERAL)) { + dwc3_meson_g12a_usb2_set_mode(priv, i, + USB_DR_MODE_PERIPHERAL); + + if (priv->phy_modes[i] == USB_DR_MODE_OTG) + priv->otg_phy_mode = PHY_MODE_USB_DEVICE; + } else { + dwc3_meson_g12a_usb2_set_mode(priv, i, + USB_DR_MODE_HOST); + + if (priv->phy_modes[i] == USB_DR_MODE_OTG) + priv->otg_phy_mode = PHY_MODE_USB_HOST; + } + + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_POWER_ON_RESET, 0); + } + + return 0; +} + +static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv) +{ + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R3, + USB_R3_P30_SSC_RANGE_MASK | + USB_R3_P30_REF_SSP_EN, + USB_R3_P30_SSC_ENABLE | + FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) | + USB_R3_P30_REF_SSP_EN); + udelay(2); + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R2, + USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, + FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15)); + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R2, + USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, + FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20)); + + udelay(2); + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R1, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT); + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R1, + USB_R1_P30_PCS_TX_SWING_FULL_MASK, + FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127)); +} + +static void dwc3_meson_g12a_usb_init_mode(struct dwc3_meson_g12a *priv, + bool is_peripheral) +{ + if (is_peripheral) { + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R0, + USB_R0_U2D_ACT, USB_R0_U2D_ACT); + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R0, + USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0); + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R4, + USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0); + } else { + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R0, + USB_R0_U2D_ACT, 0); + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R4, + USB_R4_P21_SLEEP_M0, 0); + } +} + +static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv) +{ + int ret; + + ret = dwc3_meson_g12a_usb2_init(priv); + if (ret) + return ret; + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R1, + USB_R1_U3H_FLADJ_30MHZ_REG_MASK, + FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20)); + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R5, + USB_R5_ID_DIG_EN_0, + USB_R5_ID_DIG_EN_0); + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R5, + USB_R5_ID_DIG_EN_1, + USB_R5_ID_DIG_EN_1); + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R5, + USB_R5_ID_DIG_TH_MASK, + FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff)); + + /* If we have an actual SuperSpeed port, initialize it */ + if (priv->usb3_ports) + dwc3_meson_g12a_usb3_init(priv); + + dwc3_meson_g12a_usb_init_mode(priv, + (priv->otg_phy_mode == PHY_MODE_USB_DEVICE)); + + return 0; +} + +static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = USB_REG_OFFSET + USB_R5, +}; + +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv) +{ + struct device_node *port, *phy_node; + struct of_phandle_args args; + enum usb_dr_mode mode; + const char *dr_mode; + struct phy *phy; + int ret, i; + + for (i = 0 ; i < MAX_PHY ; ++i) { + port = of_graph_get_port_by_id(priv->dev->of_node, i); + + /* Ignore port if not defined or disabled */ + if (!of_device_is_available(port)) { + of_node_put(port); + continue; + } + + /* Get associated PHY */ + phy = of_phy_get(port, NULL); + if (IS_ERR(phy)) { + of_node_put(port); + ret = PTR_ERR(phy); + goto err_phy_get; + } + + of_node_put(port); + + /* Get phy dr_mode */ + ret = of_parse_phandle_with_args(port, "phys", "#phy-cells", + 0, &args); + if (ret) { + of_node_put(port); + goto err_phy_get; + } + + phy_node = args.np; + + ret = of_property_read_string(phy_node, "dr_mode", &dr_mode); + if (ret) { + dr_mode = "unknown"; + mode = USB_DR_MODE_UNKNOWN; + } else { + if (!strcmp(dr_mode, "host")) + mode = USB_DR_MODE_HOST; + else if (!strcmp(dr_mode, "otg")) + mode = USB_DR_MODE_OTG; + else if (!strcmp(dr_mode, "peripheral")) + mode = USB_DR_MODE_PERIPHERAL; + else { + mode = USB_DR_MODE_UNKNOWN; + dr_mode = "unknown"; + } + } + + dev_info(priv->dev, "port%d: %s mode %s\n", + i, of_node_full_name(phy_node), dr_mode); + + of_node_put(phy_node); + + priv->phy_modes[i] = mode; + priv->phys[i] = phy; + + if (i == USB3_PHY) + priv->usb3_ports++; + else + priv->usb2_ports++; + } + + dev_info(priv->dev, "usb2 ports: %d\n", priv->usb2_ports); + dev_info(priv->dev, "usb3 ports: %d\n", priv->usb3_ports); + + return 0; + +err_phy_get: + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_put(priv->phys[i]); + + return ret; +} + +static int dwc3_meson_g12a_mode_force_get(void *data, u64 *val) +{ + struct dwc3_meson_g12a *priv = data; + + if (priv->otg_phy_mode == PHY_MODE_USB_HOST) + *val = 1; + else if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) + *val = 0; + else + return -EINVAL; + + return 0; +} + +static int dwc3_meson_g12a_mode_force_set(void *data, u64 val) +{ + struct dwc3_meson_g12a *priv = data; + int i; + + if ((val && priv->otg_phy_mode == PHY_MODE_USB_HOST) || + (!val && priv->otg_phy_mode == PHY_MODE_USB_DEVICE)) + return 0; + + for (i = 0 ; i < USB2_MAX_PHY ; ++i) { + if (!priv->phys[i]) + continue; + + if (priv->phy_modes[i] != USB_DR_MODE_OTG) + continue; + + if (val) { + dev_info(priv->dev, "switching to Host Mode\n"); + + dwc3_meson_g12a_usb2_set_mode(priv, i, + USB_DR_MODE_HOST); + + dwc3_meson_g12a_usb_init_mode(priv, false); + + priv->otg_phy_mode = PHY_MODE_USB_HOST; + } else { + dev_info(priv->dev, "switching to Device Mode\n"); + + dwc3_meson_g12a_usb2_set_mode(priv, i, + USB_DR_MODE_PERIPHERAL); + + dwc3_meson_g12a_usb_init_mode(priv, true); + + priv->otg_phy_mode = PHY_MODE_USB_DEVICE; + } + + return phy_set_mode(priv->phys[i], priv->otg_phy_mode); + } + + return -EINVAL; +} + +DEFINE_DEBUGFS_ATTRIBUTE(dwc3_meson_g12a_mode_force_fops, + dwc3_meson_g12a_mode_force_get, + dwc3_meson_g12a_mode_force_set, "%llu\n"); + +static int dwc3_meson_g12a_otg_id_get(void *data, u64 *val) +{ + struct dwc3_meson_g12a *priv = data; + u32 reg; + + regmap_read(priv->regmap, USB_R5, ®); + + *val = (reg & USB_R5_ID_DIG_CURR); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(dwc3_meson_g12a_otg_id_fops, + dwc3_meson_g12a_otg_id_get, NULL, "%llu\n"); + +/* We provide a DebugFS interface to get the ID value and force OTG switch */ +static int dwc3_meson_g12a_debugfs_init(struct dwc3_meson_g12a *priv) +{ + priv->root = debugfs_create_dir("dwc3-meson-g12a", NULL); + if (IS_ERR(priv->root)) + return PTR_ERR(priv->root); + + debugfs_create_file("mode_force", 0600, priv->root, priv, + &dwc3_meson_g12a_mode_force_fops); + + debugfs_create_file("otg_id", 0400, priv->root, priv, + &dwc3_meson_g12a_otg_id_fops); + + return 0; +} + +static int dwc3_meson_g12a_probe(struct platform_device *pdev) +{ + struct dwc3_meson_g12a *priv; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + void __iomem *base; + struct resource *res; + int ret, i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(dev, base, + &phy_meson_g12a_usb3_regmap_conf); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->clk = devm_clk_get(dev, "usb"); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + platform_set_drvdata(pdev, priv); + priv->dev = dev; + + priv->reset = devm_reset_control_get(dev, "usb"); + if (IS_ERR(priv->reset)) { + ret = PTR_ERR(priv->reset); + dev_err(dev, "failed to get device reset, err=%d\n", ret); + return ret; + } + + ret = reset_control_reset(priv->reset); + if (ret) + return ret; + + ret = dwc3_meson_g12a_get_phys(priv); + if (ret) + return ret; + + dwc3_meson_g12a_usb_init(priv); + + /* Init PHYs */ + for (i = 0 ; i < MAX_PHY ; ++i) { + if (priv->phys[i]) { + ret = phy_init(priv->phys[i]); + if (ret) + goto err_phys_put; + } + } + + /* Set OTG PHY mode */ + for (i = 0 ; i < MAX_PHY ; ++i) { + if (priv->phys[i] && priv->phy_modes[i] == USB_DR_MODE_OTG) { + ret = phy_set_mode(priv->phys[i], priv->otg_phy_mode); + if (ret) + goto err_phys_put; + } + } + + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) { + clk_disable_unprepare(priv->clk); + clk_put(priv->clk); + + goto err_phys_exit; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + if (dwc3_meson_g12a_debugfs_init(priv)) + dev_dbg(dev, "Failed to add DebugFS interface\n"); + + return 0; + +err_phys_exit: + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_exit(priv->phys[i]); + +err_phys_put: + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_put(priv->phys[i]); + + return ret; +} + +static int dwc3_meson_g12a_remove(struct platform_device *pdev) +{ + struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int i; + + debugfs_remove_recursive(priv->root); + + of_platform_depopulate(dev); + + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_exit(priv->phys[i]); + + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_put(priv->phys[i]); + + clk_disable_unprepare(priv->clk); + clk_put(priv->clk); + + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + pm_runtime_set_suspended(dev); + + return 0; +} + +static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + + clk_disable(priv->clk); + + return 0; +} + +static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + + return clk_enable(priv->clk); +} + +static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + int i; + + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_exit(priv->phys[i]); + + reset_control_assert(priv->reset); + + return 0; +} + +static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + int i, ret; + + reset_control_deassert(priv->reset); + + dwc3_meson_g12a_usb_init(priv); + + /* Init PHYs */ + for (i = 0 ; i < MAX_PHY ; ++i) { + if (priv->phys[i]) { + ret = phy_init(priv->phys[i]); + if (ret) + return ret; + } + } + + return 0; +} + +static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume) + SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend, + dwc3_meson_g12a_runtime_resume, NULL) +}; + +static const struct of_device_id dwc3_meson_g12a_match[] = { + { .compatible = "amlogic,meson-g12a-usb-ctrl" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); + +static struct platform_driver dwc3_meson_g12a_driver = { + .probe = dwc3_meson_g12a_probe, + .remove = dwc3_meson_g12a_remove, + .driver = { + .name = "dwc3-meson-g12a", + .of_match_table = dwc3_meson_g12a_match, + .pm = &dwc3_meson_g12a_dev_pm_ops, + }, +}; + +module_platform_driver(dwc3_meson_g12a_driver); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer"); +MODULE_AUTHOR("Neil Armstrong "); -- 2.20.1 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel 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=-9.0 required=3.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable 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 9E850C282C4 for ; Tue, 12 Feb 2019 15:17:19 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 696EB2075C for ; Tue, 12 Feb 2019 15:17:19 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="u7WaQNkv"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=baylibre-com.20150623.gappssmtp.com header.i=@baylibre-com.20150623.gappssmtp.com header.b="iQO8kbl1" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 696EB2075C Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-amlogic-bounces+linux-amlogic=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=88YTHPfcpftaRnYq3+1SJPDHAyODAOz4ZPFl9pdrvfY=; b=u7WaQNkvbUzoRs fOBiJGAf9mjCW51m7zdyHjBB/ZgOPGJE4b4mz90Vr02tkiOEGPpeaVSlBzXScxzi356qHDmJIBqxJ X9FMZTlD+uRsEsFHPvRc8IGkLtTL1NRPGUXGW8ExQZISVss3BiFaRHxf2ZVwFEBq8bFCkJvcdz+y7 XfHJVNWsIh36wPj3gnl/k/J6Odo6MU6R4ls+ShT3HoVXPmiXM42c/zHP9HT+g/h98Qvadbpne0bf0 7tXCCBxlyLfgClnd7CSz1x4Xxu9AGp0CCsju8VPgkefMTtnERKFaF9cJiRYw20ZaJuCI+P76V0Qg6 aXQyDGnnwEJIUqSJ3KpA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gtZo8-0001zV-T9; Tue, 12 Feb 2019 15:17:12 +0000 Received: from mail-wm1-x343.google.com ([2a00:1450:4864:20::343]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gtZlY-0006dh-0o for linux-amlogic@lists.infradead.org; Tue, 12 Feb 2019 15:14:45 +0000 Received: by mail-wm1-x343.google.com with SMTP id y185so1597704wmd.1 for ; Tue, 12 Feb 2019 07:14:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Gsq17jOveVaPpdOsp4iUxKzX6UR/llKkyBjljOGUIGI=; b=iQO8kbl1ees3MCrw+wTvgK6b9jiCQGIdWPH5R10FXFSyyc0JUWe0c8L1baaWsOO2w7 3td1WNXBuKXJbUosze4OsPuSLCFwK+vHGKm+biurRAudWDHoOhVDiLxNbqZkDsGo2jOt rUzug7jUMrcuomnQPQXt0BC5/38XOP3WayE6UmciufOOPLXdIgroZSpoUc04jcemCIUy uaV0q5vTGoDI6fZcgnPM4tuZLyWwgQnS5QhHUIAIeXsK8RHU5HDezLxXqHRkKa1ioYUH gE78W04/zPqxgP/obJKS9ieqCofGMwReyVo1D2oQB19W4kjpNu01+NNusKC74Up0hipz IDNA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Gsq17jOveVaPpdOsp4iUxKzX6UR/llKkyBjljOGUIGI=; b=QRXTnNmzkwANQx6g5UfbBamh0v4JARfXg0Nvjbs22Q8Y4SD00SHV7v5ektrsiRtMfd ywz6PQM3p3/Q4Vo7DAjFfMmJx0wSHF6430VbnG2qhTx478s0bNwjFy/nJadLvhFi2Vgc AeZen6uZlkP23z3DAfvw6GT4lepgFM44Ko3wrRBouNCoNaRwL/nJzHR0YM/E2iH0kQk0 oMj3xLxW3BCfZn90z9vefSWvVF07ytJurLiJUbDdWuTcjPUYPcsVyXUToYKxqn5Kc+G2 8fWIOMhes0oEy54nqbZ62+dl+z9xydfExm1cJzDWNOYTxsvwExgyZmo05LTvTMDoDzuV 3vzw== X-Gm-Message-State: AHQUAubWMMRRX786G6Z0Ov1st3KRo0sjY5t7R/Nh/HMtQjSo0kGyUg5z RGHRgOkYfFktsStMcrXVXCys3g== X-Google-Smtp-Source: AHgI3Ia0tlG9NoF2lsr6Zy/7oPVT7yf1S3TetRm1/tz233B6JwUmBSnPeF6lG9+13EF1t8wuWcK4KQ== X-Received: by 2002:a1c:400a:: with SMTP id n10mr3220840wma.56.1549984469970; Tue, 12 Feb 2019 07:14:29 -0800 (PST) Received: from bender.baylibre.local (lmontsouris-657-1-212-31.w90-63.abo.wanadoo.fr. [90.63.244.31]) by smtp.gmail.com with ESMTPSA id c1sm1589515wmb.14.2019.02.12.07.14.28 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 12 Feb 2019 07:14:28 -0800 (PST) From: Neil Armstrong To: gregkh@linuxfoundation.org, hminas@synopsys.com, balbi@kernel.org, kishon@ti.com Subject: [PATCH 8/8] usb: dwc3: Add Amlogic G12A DWC3 glue Date: Tue, 12 Feb 2019 16:14:13 +0100 Message-Id: <20190212151413.24632-9-narmstrong@baylibre.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190212151413.24632-1-narmstrong@baylibre.com> References: <20190212151413.24632-1-narmstrong@baylibre.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190212_071432_776062_45EC5992 X-CRM114-Status: GOOD ( 18.50 ) X-BeenThere: linux-amlogic@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-amlogic@lists.infradead.org, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Neil Armstrong Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-amlogic" Errors-To: linux-amlogic-bounces+linux-amlogic=archiver.kernel.org@lists.infradead.org Adds support for Amlogic G12A USB Control Glue HW. The Amlogic G12A SoC Family embeds 2 USB Controllers : - a DWC3 IP configured as Host for USB2 and USB3 - a DWC2 IP configured as Peripheral USB2 Only A glue connects these both controllers to 2 USB2 PHYs, and optionnally to an USB3+PCIE Combo PHY shared with the PCIE controller. The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including routing of the OTG PHY between the DWC3 and DWC2 controllers, and setups the on-chip OTG mode selection for this PHY. The PHYs are childen of the Glue node since the Glue controls the interface with the PHY, not the DWC3 controller. The drivers collects the mode of each PHY and determine which PHY is to be routed between the DWC2 and DWC3 controllers. This drivers supports the on-probe setup of the OTG mode, and manually via a debugfs interface. The IRQ mode change detect is yet to be added in a future patchset, mainly due to lack of hardware to validate on. Signed-off-by: Neil Armstrong --- drivers/usb/dwc3/Kconfig | 9 + drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-meson-g12a.c | 650 +++++++++++++++++++++++++++++ 3 files changed, 660 insertions(+) create mode 100644 drivers/usb/dwc3/dwc3-meson-g12a.c diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 1a0404fda596..4335e5e76bbb 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -93,6 +93,15 @@ config USB_DWC3_KEYSTONE Support of USB2/3 functionality in TI Keystone2 platforms. Say 'Y' or 'M' here if you have one such device +config USB_DWC3_MESON_G12A + tristate "Amlogic Meson G12A Platforms" + depends on OF && COMMON_CLK + depends on ARCH_MESON || COMPILE_TEST + default USB_DWC3 + help + Support USB2/3 functionality in Amlogic G12A platforms. + Say 'Y' or 'M' if you have one such device. + config USB_DWC3_OF_SIMPLE tristate "Generic OF Simple Glue Layer" depends on OF && COMMON_CLK diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 6e3ef6144e5d..ae86da0dc5bd 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o obj-$(CONFIG_USB_DWC3_HAPS) += dwc3-haps.o obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o +obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c new file mode 100644 index 000000000000..abeff2d56b1d --- /dev/null +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -0,0 +1,650 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB Glue for Amlogic G12A SoCs + * + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong + */ + +/* + * The USB is organized with a glue around the DWC3 Controller IP as : + * - Control registers for each USB2 Ports + * - Control registers for the USB PHY layer + * - SuperSpeed PHY can be enabled only if port is used + * + * TOFIX: + * - Add dynamic OTG switching with ID change interrupt + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* USB Glue Control Registers */ + +#define USB_R0 0x00 + #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17) + #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18) + #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19) + #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29) + #define USB_R0_U2D_ACT BIT(31) + +#define USB_R1 0x04 + #define USB_R1_U3H_BIGENDIAN_GS BIT(0) + #define USB_R1_U3H_PME_ENABLE BIT(1) + #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(4, 2) + #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(9, 7) + #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(13, 12) + #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16) + #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17) + #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18) + #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19) + #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25) + +#define USB_R2 0x08 + #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20) + #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26) + +#define USB_R3 0x0c + #define USB_R3_P30_SSC_ENABLE BIT(0) + #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1) + #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4) + #define USB_R3_P30_REF_SSP_EN BIT(13) + +#define USB_R4 0x10 + #define USB_R4_P21_PORT_RESET_0 BIT(0) + #define USB_R4_P21_SLEEP_M0 BIT(1) + #define USB_R4_MEM_PD_MASK GENMASK(3, 2) + #define USB_R4_P21_ONLY BIT(4) + +#define USB_R5 0x14 + #define USB_R5_ID_DIG_SYNC BIT(0) + #define USB_R5_ID_DIG_REG BIT(1) + #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2) + #define USB_R5_ID_DIG_EN_0 BIT(4) + #define USB_R5_ID_DIG_EN_1 BIT(5) + #define USB_R5_ID_DIG_CURR BIT(6) + #define USB_R5_ID_DIG_IRQ BIT(7) + #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) + #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) + +/* USB2 Ports Control Registers */ + +#define U2P_R0 0x0 + #define U2P_R0_HOST_DEVICE BIT(0) + #define U2P_R0_POWER_OK BIT(1) + #define U2P_R0_HAST_MODE BIT(2) + #define U2P_R0_POWER_ON_RESET BIT(3) + #define U2P_R0_ID_PULLUP BIT(4) + #define U2P_R0_DRV_VBUS BIT(5) + +#define U2P_R1 0x4 + #define U2P_R1_PHY_READY BIT(0) + #define U2P_R1_ID_DIG BIT(1) + #define U2P_R1_OTG_SESSION_VALID BIT(2) + #define U2P_R1_VBUS_VALID BIT(3) + +#define MAX_PHY 5 + +#define USB2_MAX_PHY 4 +#define USB3_PHY 4 + +struct dwc3_meson_g12a { + struct device *dev; + struct regmap *regmap; + struct clk *clk; + struct reset_control *reset; + struct phy *phys[5]; + enum usb_dr_mode phy_modes[5]; + enum phy_mode otg_phy_mode; + unsigned int usb2_ports; + unsigned int usb3_ports; + struct dentry *root; +}; + +#define U2P_REG_SIZE 0x20 +#define USB_REG_OFFSET 0x80 + +static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv, + int i, enum usb_dr_mode mode) +{ + switch (mode) { + case USB_DR_MODE_HOST: + case USB_DR_MODE_OTG: + case USB_DR_MODE_UNKNOWN: + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, + U2P_R0_HOST_DEVICE); + break; + + case USB_DR_MODE_PERIPHERAL: + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, 0); + break; + } +} + +static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv) +{ + enum usb_dr_mode id_mode; + u32 val; + int i; + + /* Read ID current level */ + regmap_read(priv->regmap, USB_R5, &val); + if (val & USB_R5_ID_DIG_CURR) + id_mode = USB_DR_MODE_PERIPHERAL; + else + id_mode = USB_DR_MODE_HOST; + + dev_info(priv->dev, "ID mode %s\n", + id_mode == USB_DR_MODE_HOST ? "host" : "peripheral"); + + for (i = 0 ; i < USB2_MAX_PHY ; ++i) { + if (!priv->phys[i]) + continue; + + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_POWER_ON_RESET, + U2P_R0_POWER_ON_RESET); + + if (priv->phy_modes[i] == USB_DR_MODE_PERIPHERAL || + (priv->phy_modes[i] == USB_DR_MODE_OTG && + id_mode == USB_DR_MODE_PERIPHERAL)) { + dwc3_meson_g12a_usb2_set_mode(priv, i, + USB_DR_MODE_PERIPHERAL); + + if (priv->phy_modes[i] == USB_DR_MODE_OTG) + priv->otg_phy_mode = PHY_MODE_USB_DEVICE; + } else { + dwc3_meson_g12a_usb2_set_mode(priv, i, + USB_DR_MODE_HOST); + + if (priv->phy_modes[i] == USB_DR_MODE_OTG) + priv->otg_phy_mode = PHY_MODE_USB_HOST; + } + + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_POWER_ON_RESET, 0); + } + + return 0; +} + +static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv) +{ + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R3, + USB_R3_P30_SSC_RANGE_MASK | + USB_R3_P30_REF_SSP_EN, + USB_R3_P30_SSC_ENABLE | + FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) | + USB_R3_P30_REF_SSP_EN); + udelay(2); + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R2, + USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, + FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15)); + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R2, + USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, + FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20)); + + udelay(2); + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R1, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT); + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R1, + USB_R1_P30_PCS_TX_SWING_FULL_MASK, + FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127)); +} + +static void dwc3_meson_g12a_usb_init_mode(struct dwc3_meson_g12a *priv, + bool is_peripheral) +{ + if (is_peripheral) { + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R0, + USB_R0_U2D_ACT, USB_R0_U2D_ACT); + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R0, + USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0); + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R4, + USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0); + } else { + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R0, + USB_R0_U2D_ACT, 0); + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R4, + USB_R4_P21_SLEEP_M0, 0); + } +} + +static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv) +{ + int ret; + + ret = dwc3_meson_g12a_usb2_init(priv); + if (ret) + return ret; + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R1, + USB_R1_U3H_FLADJ_30MHZ_REG_MASK, + FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20)); + + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R5, + USB_R5_ID_DIG_EN_0, + USB_R5_ID_DIG_EN_0); + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R5, + USB_R5_ID_DIG_EN_1, + USB_R5_ID_DIG_EN_1); + regmap_update_bits(priv->regmap, USB_REG_OFFSET + USB_R5, + USB_R5_ID_DIG_TH_MASK, + FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff)); + + /* If we have an actual SuperSpeed port, initialize it */ + if (priv->usb3_ports) + dwc3_meson_g12a_usb3_init(priv); + + dwc3_meson_g12a_usb_init_mode(priv, + (priv->otg_phy_mode == PHY_MODE_USB_DEVICE)); + + return 0; +} + +static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = USB_REG_OFFSET + USB_R5, +}; + +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv) +{ + struct device_node *port, *phy_node; + struct of_phandle_args args; + enum usb_dr_mode mode; + const char *dr_mode; + struct phy *phy; + int ret, i; + + for (i = 0 ; i < MAX_PHY ; ++i) { + port = of_graph_get_port_by_id(priv->dev->of_node, i); + + /* Ignore port if not defined or disabled */ + if (!of_device_is_available(port)) { + of_node_put(port); + continue; + } + + /* Get associated PHY */ + phy = of_phy_get(port, NULL); + if (IS_ERR(phy)) { + of_node_put(port); + ret = PTR_ERR(phy); + goto err_phy_get; + } + + of_node_put(port); + + /* Get phy dr_mode */ + ret = of_parse_phandle_with_args(port, "phys", "#phy-cells", + 0, &args); + if (ret) { + of_node_put(port); + goto err_phy_get; + } + + phy_node = args.np; + + ret = of_property_read_string(phy_node, "dr_mode", &dr_mode); + if (ret) { + dr_mode = "unknown"; + mode = USB_DR_MODE_UNKNOWN; + } else { + if (!strcmp(dr_mode, "host")) + mode = USB_DR_MODE_HOST; + else if (!strcmp(dr_mode, "otg")) + mode = USB_DR_MODE_OTG; + else if (!strcmp(dr_mode, "peripheral")) + mode = USB_DR_MODE_PERIPHERAL; + else { + mode = USB_DR_MODE_UNKNOWN; + dr_mode = "unknown"; + } + } + + dev_info(priv->dev, "port%d: %s mode %s\n", + i, of_node_full_name(phy_node), dr_mode); + + of_node_put(phy_node); + + priv->phy_modes[i] = mode; + priv->phys[i] = phy; + + if (i == USB3_PHY) + priv->usb3_ports++; + else + priv->usb2_ports++; + } + + dev_info(priv->dev, "usb2 ports: %d\n", priv->usb2_ports); + dev_info(priv->dev, "usb3 ports: %d\n", priv->usb3_ports); + + return 0; + +err_phy_get: + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_put(priv->phys[i]); + + return ret; +} + +static int dwc3_meson_g12a_mode_force_get(void *data, u64 *val) +{ + struct dwc3_meson_g12a *priv = data; + + if (priv->otg_phy_mode == PHY_MODE_USB_HOST) + *val = 1; + else if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) + *val = 0; + else + return -EINVAL; + + return 0; +} + +static int dwc3_meson_g12a_mode_force_set(void *data, u64 val) +{ + struct dwc3_meson_g12a *priv = data; + int i; + + if ((val && priv->otg_phy_mode == PHY_MODE_USB_HOST) || + (!val && priv->otg_phy_mode == PHY_MODE_USB_DEVICE)) + return 0; + + for (i = 0 ; i < USB2_MAX_PHY ; ++i) { + if (!priv->phys[i]) + continue; + + if (priv->phy_modes[i] != USB_DR_MODE_OTG) + continue; + + if (val) { + dev_info(priv->dev, "switching to Host Mode\n"); + + dwc3_meson_g12a_usb2_set_mode(priv, i, + USB_DR_MODE_HOST); + + dwc3_meson_g12a_usb_init_mode(priv, false); + + priv->otg_phy_mode = PHY_MODE_USB_HOST; + } else { + dev_info(priv->dev, "switching to Device Mode\n"); + + dwc3_meson_g12a_usb2_set_mode(priv, i, + USB_DR_MODE_PERIPHERAL); + + dwc3_meson_g12a_usb_init_mode(priv, true); + + priv->otg_phy_mode = PHY_MODE_USB_DEVICE; + } + + return phy_set_mode(priv->phys[i], priv->otg_phy_mode); + } + + return -EINVAL; +} + +DEFINE_DEBUGFS_ATTRIBUTE(dwc3_meson_g12a_mode_force_fops, + dwc3_meson_g12a_mode_force_get, + dwc3_meson_g12a_mode_force_set, "%llu\n"); + +static int dwc3_meson_g12a_otg_id_get(void *data, u64 *val) +{ + struct dwc3_meson_g12a *priv = data; + u32 reg; + + regmap_read(priv->regmap, USB_R5, ®); + + *val = (reg & USB_R5_ID_DIG_CURR); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(dwc3_meson_g12a_otg_id_fops, + dwc3_meson_g12a_otg_id_get, NULL, "%llu\n"); + +/* We provide a DebugFS interface to get the ID value and force OTG switch */ +static int dwc3_meson_g12a_debugfs_init(struct dwc3_meson_g12a *priv) +{ + priv->root = debugfs_create_dir("dwc3-meson-g12a", NULL); + if (IS_ERR(priv->root)) + return PTR_ERR(priv->root); + + debugfs_create_file("mode_force", 0600, priv->root, priv, + &dwc3_meson_g12a_mode_force_fops); + + debugfs_create_file("otg_id", 0400, priv->root, priv, + &dwc3_meson_g12a_otg_id_fops); + + return 0; +} + +static int dwc3_meson_g12a_probe(struct platform_device *pdev) +{ + struct dwc3_meson_g12a *priv; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + void __iomem *base; + struct resource *res; + int ret, i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(dev, base, + &phy_meson_g12a_usb3_regmap_conf); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->clk = devm_clk_get(dev, "usb"); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + platform_set_drvdata(pdev, priv); + priv->dev = dev; + + priv->reset = devm_reset_control_get(dev, "usb"); + if (IS_ERR(priv->reset)) { + ret = PTR_ERR(priv->reset); + dev_err(dev, "failed to get device reset, err=%d\n", ret); + return ret; + } + + ret = reset_control_reset(priv->reset); + if (ret) + return ret; + + ret = dwc3_meson_g12a_get_phys(priv); + if (ret) + return ret; + + dwc3_meson_g12a_usb_init(priv); + + /* Init PHYs */ + for (i = 0 ; i < MAX_PHY ; ++i) { + if (priv->phys[i]) { + ret = phy_init(priv->phys[i]); + if (ret) + goto err_phys_put; + } + } + + /* Set OTG PHY mode */ + for (i = 0 ; i < MAX_PHY ; ++i) { + if (priv->phys[i] && priv->phy_modes[i] == USB_DR_MODE_OTG) { + ret = phy_set_mode(priv->phys[i], priv->otg_phy_mode); + if (ret) + goto err_phys_put; + } + } + + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) { + clk_disable_unprepare(priv->clk); + clk_put(priv->clk); + + goto err_phys_exit; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + if (dwc3_meson_g12a_debugfs_init(priv)) + dev_dbg(dev, "Failed to add DebugFS interface\n"); + + return 0; + +err_phys_exit: + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_exit(priv->phys[i]); + +err_phys_put: + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_put(priv->phys[i]); + + return ret; +} + +static int dwc3_meson_g12a_remove(struct platform_device *pdev) +{ + struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int i; + + debugfs_remove_recursive(priv->root); + + of_platform_depopulate(dev); + + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_exit(priv->phys[i]); + + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_put(priv->phys[i]); + + clk_disable_unprepare(priv->clk); + clk_put(priv->clk); + + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + pm_runtime_set_suspended(dev); + + return 0; +} + +static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + + clk_disable(priv->clk); + + return 0; +} + +static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + + return clk_enable(priv->clk); +} + +static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + int i; + + for (i = 0 ; i < MAX_PHY ; ++i) + if (priv->phys[i]) + phy_exit(priv->phys[i]); + + reset_control_assert(priv->reset); + + return 0; +} + +static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + int i, ret; + + reset_control_deassert(priv->reset); + + dwc3_meson_g12a_usb_init(priv); + + /* Init PHYs */ + for (i = 0 ; i < MAX_PHY ; ++i) { + if (priv->phys[i]) { + ret = phy_init(priv->phys[i]); + if (ret) + return ret; + } + } + + return 0; +} + +static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume) + SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend, + dwc3_meson_g12a_runtime_resume, NULL) +}; + +static const struct of_device_id dwc3_meson_g12a_match[] = { + { .compatible = "amlogic,meson-g12a-usb-ctrl" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); + +static struct platform_driver dwc3_meson_g12a_driver = { + .probe = dwc3_meson_g12a_probe, + .remove = dwc3_meson_g12a_remove, + .driver = { + .name = "dwc3-meson-g12a", + .of_match_table = dwc3_meson_g12a_match, + .pm = &dwc3_meson_g12a_dev_pm_ops, + }, +}; + +module_platform_driver(dwc3_meson_g12a_driver); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer"); +MODULE_AUTHOR("Neil Armstrong "); -- 2.20.1 _______________________________________________ linux-amlogic mailing list linux-amlogic@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-amlogic