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 77374C10F0C for ; Mon, 4 Mar 2019 10:39:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 1F84E20663 for ; Mon, 4 Mar 2019 10:39:14 +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="gmWBZCo4" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726485AbfCDKjN (ORCPT ); Mon, 4 Mar 2019 05:39:13 -0500 Received: from mail-wm1-f65.google.com ([209.85.128.65]:38140 "EHLO mail-wm1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726420AbfCDKjB (ORCPT ); Mon, 4 Mar 2019 05:39:01 -0500 Received: by mail-wm1-f65.google.com with SMTP id a188so4134980wmf.3 for ; Mon, 04 Mar 2019 02:38:59 -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=ml5vlyaCTJkDRI3uHR4Qlz9TvSRkxPULUVun9aFSzZU=; b=gmWBZCo4JqDDaoPQIlK3lQ9amN1ELcwxPHSeQg3IA9HZ8UiKI/HqKYRXJ+WTvc2lBM u1Lbf9ppDvEGzNRky24v3+mjTmYKorQC8+b8zLeBU+llqe3u0XDCi6kcorFT0YJ81eDV WrBcv8SA8j8r6FOBtyYz75XfrFCB+E7juLcW9bv4pJbe07H2yE7gIt79BmcMsOj2E72T taCq7VNx7G+AiRgCC6lcN7W9k+BXB4IU2x2liIL3NGF9/baUG9LhApkuSt/Zi6zHuSgN HrIbkAdNe1rIWM7c32AOtdA4PoWhhySzVakANPxduRX1rhat8v9qmYkVSr1pn0jzLllf 6v3Q== 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=ml5vlyaCTJkDRI3uHR4Qlz9TvSRkxPULUVun9aFSzZU=; b=AVSTyQALF8yBFNw9cHqzoSXooRzccT/vzRX0EUac05FJyk9cvjElRK0FO0BsMrqlGP s8eJY8IFmQslBqnUqFRRy1DME3WMZ/ipc2CthrHijBEmZ7tOYkWm9YXmxe7NQEQ54W/8 Dkmm3sM29sXsW28nEs+5CmCET+fYmvLmKsnqyVlhX85JlTSCd76EoE3xqHO4VjSqA7Ak XDCUhEVGF0TQtqCUFtczPP7hylFVRrXOcEjrfDIRHqPABjptmLQW3tgXf2xMdiRyeL+4 lxkJdoU5NNfqI02HFnQVuqqPcvR4vLrw8wNpYDw412CWxVnm44HAwBksdFCo0MGDzI5G WyVg== X-Gm-Message-State: APjAAAXtzZaPmB7PdxeyjIjPeGXlJ3QcX0+lbbCQKyR8Fvay69CHvbkr 7XbhUZ4i2xAVz7sVNjlr0HCjQ+/UClN0rg== X-Google-Smtp-Source: AHgI3Iax/u9i0KQCYJyr3mdF7C2SUkZ9vB1RjaHe49SMCnUVz0fvHxpGTMIwN1K1lULoRz8w+kDWzQ== X-Received: by 2002:a1c:ab88:: with SMTP id u130mr10640966wme.148.1551695938936; Mon, 04 Mar 2019 02:38:58 -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 g24sm5505676wmh.45.2019.03.04.02.38.57 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 04 Mar 2019 02:38:58 -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 v2 8/8] usb: dwc3: Add Amlogic G12A DWC3 glue Date: Mon, 4 Mar 2019 11:38:46 +0100 Message-Id: <20190304103846.2060-9-narmstrong@baylibre.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190304103846.2060-1-narmstrong@baylibre.com> References: <20190304103846.2060-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. 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 | 10 + drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-meson-g12a.c | 601 +++++++++++++++++++++++++++++ 3 files changed, 612 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..21ce7368d325 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -93,6 +93,16 @@ 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 + select USB_ROLE_SWITCH + 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..75942614a034 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -0,0 +1,601 @@ +// 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 + +/* USB2 Ports Control Registers */ + +#define U2P_REG_SIZE 0x20 + +#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) + +/* USB Glue Control Registers */ + +#define USB_R0 0x80 + #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 0x84 + #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 0x88 + #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 0x8c + #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 0x90 + #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 0x94 + #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) + +enum { + USB2_HOST_PHY = 0, + USB2_OTG_PHY, + USB3_HOST_PHY, + PHY_COUNT, +}; + +static const char *phy_names[PHY_COUNT] = { + "usb2-phy0", "usb2-phy1", "usb3-phy0", +}; + +struct dwc3_meson_g12a { + struct device *dev; + struct regmap *regmap; + struct clk *clk; + struct reset_control *reset; + struct phy *phys[PHY_COUNT]; + enum usb_dr_mode otg_mode; + enum phy_mode otg_phy_mode; + unsigned int usb2_ports; + unsigned int usb3_ports; + struct regulator *vbus; + struct usb_role_switch_desc switch_desc; + struct usb_role_switch *role_switch; +}; + +static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv, + int i, enum phy_mode mode) +{ + if (mode == PHY_MODE_USB_HOST) + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, + U2P_R0_HOST_DEVICE); + else + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, 0); +} + +static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv) +{ + int i; + + if (priv->otg_mode == USB_DR_MODE_PERIPHERAL) + priv->otg_phy_mode = PHY_MODE_USB_DEVICE; + else + priv->otg_phy_mode = PHY_MODE_USB_HOST; + + for (i = 0 ; i < USB3_HOST_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 (i == USB2_OTG_PHY) { + regmap_update_bits(priv->regmap, + U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS, + U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS); + + dwc3_meson_g12a_usb2_set_mode(priv, i, + priv->otg_phy_mode); + } else + dwc3_meson_g12a_usb2_set_mode(priv, i, + 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_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_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_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_R1, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT); + + regmap_update_bits(priv->regmap, 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) +{ + if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) { + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, USB_R0_U2D_ACT); + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0); + regmap_update_bits(priv->regmap, USB_R4, + USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0); + } else { + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, 0); + regmap_update_bits(priv->regmap, 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_R1, + USB_R1_U3H_FLADJ_30MHZ_REG_MASK, + FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20)); + + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_0, + USB_R5_ID_DIG_EN_0); + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_1, + USB_R5_ID_DIG_EN_1); + regmap_update_bits(priv->regmap, 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); + + 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_R5, +}; + +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv) +{ + int i; + + for (i = 0 ; i < PHY_COUNT ; ++i) { + priv->phys[i] = devm_phy_optional_get(priv->dev, phy_names[i]); + if (!priv->phys[i]) + continue; + + if (IS_ERR(priv->phys[i])) + return PTR_ERR(priv->phys[i]); + + if (i == USB3_HOST_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; +} + +static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv) +{ + u32 reg; + + regmap_read(priv->regmap, USB_R5, ®); + + if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG)) + return PHY_MODE_USB_DEVICE; + + return PHY_MODE_USB_HOST; +} + +static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv, + enum phy_mode mode) +{ + int ret; + + if (!priv->phys[USB2_OTG_PHY]) + return -EINVAL; + + if (mode == PHY_MODE_USB_HOST) + dev_info(priv->dev, "switching to Host Mode\n"); + else + dev_info(priv->dev, "switching to Device Mode\n"); + + if (priv->vbus) { + if (mode == PHY_MODE_USB_DEVICE) + ret = regulator_disable(priv->vbus); + else + ret = regulator_enable(priv->vbus); + if (ret) + return ret; + } + + priv->otg_phy_mode = mode; + + dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode); + + dwc3_meson_g12a_usb_init_mode(priv); + + return phy_set_mode(priv->phys[USB2_OTG_PHY], mode); +} + +static int dwc3_meson_g12a_role_set(struct device *dev, enum usb_role role) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + enum phy_mode mode; + + if (role == USB_ROLE_NONE) + return 0; + + mode = role == USB_ROLE_HOST ? PHY_MODE_USB_HOST : PHY_MODE_USB_DEVICE; + + if (mode == priv->otg_phy_mode) + return 0; + + return dwc3_meson_g12a_otg_mode_set(priv, mode); +} + +static enum usb_role dwc3_meson_g12a_role_get(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + + return priv->otg_phy_mode == PHY_MODE_USB_HOST ? + USB_ROLE_HOST : USB_ROLE_DEVICE; +} + +static struct device *dwc3_meson_g12_find_child(struct device *dev, + const char *compatible) +{ + struct platform_device *pdev; + struct device_node *np; + + np = of_find_compatible_node(dev->of_node, NULL, compatible); + if (!np) + return NULL; + + pdev = of_find_device_by_node(np); + of_node_put(np); + if (!pdev) + return NULL; + + return &pdev->dev; +} + +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; + enum phy_mode otg_id; + 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->vbus = devm_regulator_get_optional(dev, "vbus"); + if (IS_ERR(priv->vbus)) { + if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) + return PTR_ERR(priv->vbus); + priv->vbus = NULL; + } + + priv->clk = devm_clk_get(dev, NULL); + 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, NULL); + 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; + + ret = regulator_enable(priv->vbus); + if (ret) + return ret; + + /* Get dr_mode */ + priv->otg_mode = usb_get_dr_mode(dev); + + dwc3_meson_g12a_usb_init(priv); + + /* Set PHY Power */ + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = phy_power_on(priv->phys[i]); + if (ret) + goto err_phys_put; + } + + /* Init PHYs */ + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = phy_init(priv->phys[i]); + if (ret) + goto err_phys_power; + } + + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) { + clk_disable_unprepare(priv->clk); + clk_put(priv->clk); + + goto err_phys_exit; + } + + /* Setup OTG mode corresponding to the ID pin */ + if (priv->otg_mode == USB_DR_MODE_OTG) { + /* TOFIX Handle ID mode toggling via IRQ */ + otg_id = dwc3_meson_g12a_get_id(priv); + if (otg_id != priv->otg_phy_mode) { + if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) + dev_warn(dev, "Failed to switch OTG mode\n"); + } + } + + /* Setup role switcher */ + priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev, + "snps,dwc3"); + priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2"); + priv->switch_desc.allow_userspace_control = true; + priv->switch_desc.set = dwc3_meson_g12a_role_set; + priv->switch_desc.get = dwc3_meson_g12a_role_get; + + priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc); + if (IS_ERR(priv->role_switch)) + dev_warn(dev, "Unable to register Role Switch\n"); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + return 0; + +err_phys_exit: + for (i = 0 ; i < PHY_COUNT ; ++i) + phy_exit(priv->phys[i]); + +err_phys_power: + for (i = 0 ; i < PHY_COUNT ; ++i) + phy_power_off(priv->phys[i]); + +err_phys_put: + for (i = 0 ; i < PHY_COUNT ; ++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; + + usb_role_switch_unregister(priv->role_switch); + + of_platform_depopulate(dev); + + for (i = 0 ; i < PHY_COUNT ; ++i) { + phy_power_off(priv->phys[i]); + phy_exit(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 < PHY_COUNT ; ++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 < PHY_COUNT ; ++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: [v2,8/8] usb: dwc3: Add Amlogic G12A DWC3 glue From: Neil Armstrong Message-Id: <20190304103846.2060-9-narmstrong@baylibre.com> Date: Mon, 4 Mar 2019 11:38:46 +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 Zm9yIHRoaXMgUEhZLgoKVGhpcyBkcml2ZXJzIHN1cHBvcnRzIHRoZSBvbi1wcm9iZSBzZXR1cCBv ZiB0aGUgT1RHIG1vZGUsIGFuZCBtYW51YWxseQp2aWEgYSBkZWJ1Z2ZzIGludGVyZmFjZS4gVGhl IElSUSBtb2RlIGNoYW5nZSBkZXRlY3QgaXMgeWV0IHRvIGJlIGFkZGVkCmluIGEgZnV0dXJlIHBh dGNoc2V0LCBtYWlubHkgZHVlIHRvIGxhY2sgb2YgaGFyZHdhcmUgdG8gdmFsaWRhdGUgb24uCgpT aWduZWQtb2ZmLWJ5OiBOZWlsIEFybXN0cm9uZyA8bmFybXN0cm9uZ0BiYXlsaWJyZS5jb20+Ci0t LQogZHJpdmVycy91c2IvZHdjMy9LY29uZmlnICAgICAgICAgICB8ICAxMCArCiBkcml2ZXJzL3Vz Yi9kd2MzL01ha2VmaWxlICAgICAgICAgIHwgICAxICsKIGRyaXZlcnMvdXNiL2R3YzMvZHdjMy1t ZXNvbi1nMTJhLmMgfCA2MDEgKysrKysrKysrKysrKysrKysrKysrKysrKysrKysKIDMgZmlsZXMg Y2hhbmdlZCwgNjEyIGluc2VydGlvbnMoKykKIGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJzL3Vz Yi9kd2MzL2R3YzMtbWVzb24tZzEyYS5jCgpkaWZmIC0tZ2l0IGEvZHJpdmVycy91c2IvZHdjMy9L Y29uZmlnIGIvZHJpdmVycy91c2IvZHdjMy9LY29uZmlnCmluZGV4IDFhMDQwNGZkYTU5Ni4uMjFj ZTczNjhkMzI1IDEwMDY0NAotLS0gYS9kcml2ZXJzL3VzYi9kd2MzL0tjb25maWcKKysrIGIvZHJp dmVycy91c2IvZHdjMy9LY29uZmlnCkBAIC05Myw2ICs5MywxNiBAQCBjb25maWcgVVNCX0RXQzNf S0VZU1RPTkUKIAkgIFN1cHBvcnQgb2YgVVNCMi8zIGZ1bmN0aW9uYWxpdHkgaW4gVEkgS2V5c3Rv bmUyIHBsYXRmb3Jtcy4KIAkgIFNheSAnWScgb3IgJ00nIGhlcmUgaWYgeW91IGhhdmUgb25lIHN1 Y2ggZGV2aWNlCiAKK2NvbmZpZyBVU0JfRFdDM19NRVNPTl9HMTJBCisgICAgICAgdHJpc3RhdGUg IkFtbG9naWMgTWVzb24gRzEyQSBQbGF0Zm9ybXMiCisgICAgICAgZGVwZW5kcyBvbiBPRiAmJiBD T01NT05fQ0xLCisgICAgICAgZGVwZW5kcyBvbiBBUkNIX01FU09OIHx8IENPTVBJTEVfVEVTVAor ICAgICAgIGRlZmF1bHQgVVNCX0RXQzMKKyAgICAgICBzZWxlY3QgVVNCX1JPTEVfU1dJVENICisg ICAgICAgaGVscAorICAgICAgICAgU3VwcG9ydCBVU0IyLzMgZnVuY3Rpb25hbGl0eSBpbiBBbWxv Z2ljIEcxMkEgcGxhdGZvcm1zLgorCSBTYXkgJ1knIG9yICdNJyBpZiB5b3UgaGF2ZSBvbmUgc3Vj aCBkZXZpY2UuCisKIGNvbmZpZyBVU0JfRFdDM19PRl9TSU1QTEUKICAgICAgICB0cmlzdGF0ZSAi R2VuZXJpYyBPRiBTaW1wbGUgR2x1ZSBMYXllciIKICAgICAgICBkZXBlbmRzIG9uIE9GICYmIENP TU1PTl9DTEsKZGlmZiAtLWdpdCBhL2RyaXZlcnMvdXNiL2R3YzMvTWFrZWZpbGUgYi9kcml2ZXJz L3VzYi9kd2MzL01ha2VmaWxlCmluZGV4IDZlM2VmNjE0NGU1ZC4uYWU4NmRhMGRjNWJkIDEwMDY0 NAotLS0gYS9kcml2ZXJzL3VzYi9kd2MzL01ha2VmaWxlCisrKyBiL2RyaXZlcnMvdXNiL2R3YzMv TWFrZWZpbGUKQEAgLTQ3LDYgKzQ3LDcgQEAgb2JqLSQoQ09ORklHX1VTQl9EV0MzX0VYWU5PUykJ CSs9IGR3YzMtZXh5bm9zLm8KIG9iai0kKENPTkZJR19VU0JfRFdDM19QQ0kpCQkrPSBkd2MzLXBj aS5vCiBvYmotJChDT05GSUdfVVNCX0RXQzNfSEFQUykJCSs9IGR3YzMtaGFwcy5vCiBvYmotJChD T05GSUdfVVNCX0RXQzNfS0VZU1RPTkUpCQkrPSBkd2MzLWtleXN0b25lLm8KK29iai0kKENPTkZJ R19VU0JfRFdDM19NRVNPTl9HMTJBKQkrPSBkd2MzLW1lc29uLWcxMmEubwogb2JqLSQoQ09ORklH X1VTQl9EV0MzX09GX1NJTVBMRSkJKz0gZHdjMy1vZi1zaW1wbGUubwogb2JqLSQoQ09ORklHX1VT Ql9EV0MzX1NUKQkJKz0gZHdjMy1zdC5vCiBvYmotJChDT05GSUdfVVNCX0RXQzNfUUNPTSkJCSs9 IGR3YzMtcWNvbS5vCmRpZmYgLS1naXQgYS9kcml2ZXJzL3VzYi9kd2MzL2R3YzMtbWVzb24tZzEy YS5jIGIvZHJpdmVycy91c2IvZHdjMy9kd2MzLW1lc29uLWcxMmEuYwpuZXcgZmlsZSBtb2RlIDEw MDY0NAppbmRleCAwMDAwMDAwMDAwMDAuLjc1OTQyNjE0YTAzNAotLS0gL2Rldi9udWxsCisrKyBi L2RyaXZlcnMvdXNiL2R3YzMvZHdjMy1tZXNvbi1nMTJhLmMKQEAgLTAsMCArMSw2MDEgQEAKKy8v IFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBHUEwtMi4wCisvKgorICogVVNCIEdsdWUgZm9yIEFt bG9naWMgRzEyQSBTb0NzCisgKgorICogQ29weXJpZ2h0IChjKSAyMDE5IEJheUxpYnJlLCBTQVMK KyAqIEF1dGhvcjogTmVpbCBBcm1zdHJvbmcgPG5hcm1zdHJvbmdAYmF5bGlicmUuY29tPgorICov CisKKy8qCisgKiBUaGUgVVNCIGlzIG9yZ2FuaXplZCB3aXRoIGEgZ2x1ZSBhcm91bmQgdGhlIERX QzMgQ29udHJvbGxlciBJUCBhcyA6CisgKiAtIENvbnRyb2wgcmVnaXN0ZXJzIGZvciBlYWNoIFVT QjIgUG9ydHMKKyAqIC0gQ29udHJvbCByZWdpc3RlcnMgZm9yIHRoZSBVU0IgUEhZIGxheWVyCisg KiAtIFN1cGVyU3BlZWQgUEhZIGNhbiBiZSBlbmFibGVkIG9ubHkgaWYgcG9ydCBpcyB1c2VkCisg KgorICogVE9GSVg6CisgKiAtIEFkZCBkeW5hbWljIE9URyBzd2l0Y2hpbmcgd2l0aCBJRCBjaGFu Z2UgaW50ZXJydXB0CisgKi8KKworI2luY2x1ZGUgPGxpbnV4L21vZHVsZS5oPgorI2luY2x1ZGUg PGxpbnV4L2tlcm5lbC5oPgorI2luY2x1ZGUgPGxpbnV4L3BsYXRmb3JtX2RldmljZS5oPgorI2lu Y2x1ZGUgPGxpbnV4L2Nsay5oPgorI2luY2x1ZGUgPGxpbnV4L29mLmg+CisjaW5jbHVkZSA8bGlu dXgvb2ZfcGxhdGZvcm0uaD4KKyNpbmNsdWRlIDxsaW51eC9wbV9ydW50aW1lLmg+CisjaW5jbHVk ZSA8bGludXgvcmVnbWFwLmg+CisjaW5jbHVkZSA8bGludXgvYml0ZmllbGQuaD4KKyNpbmNsdWRl IDxsaW51eC9iaXRvcHMuaD4KKyNpbmNsdWRlIDxsaW51eC9yZXNldC5oPgorI2luY2x1ZGUgPGxp bnV4L3BoeS9waHkuaD4KKyNpbmNsdWRlIDxsaW51eC91c2Ivb3RnLmg+CisjaW5jbHVkZSA8bGlu dXgvdXNiL3JvbGUuaD4KKyNpbmNsdWRlIDxsaW51eC9yZWd1bGF0b3IvY29uc3VtZXIuaD4KKwor LyogVVNCMiBQb3J0cyBDb250cm9sIFJlZ2lzdGVycyAqLworCisjZGVmaW5lIFUyUF9SRUdfU0la RQkJCQkJCTB4MjAKKworI2RlZmluZSBVMlBfUjAJCQkJCQkJMHgwCisJI2RlZmluZSBVMlBfUjBf SE9TVF9ERVZJQ0UJCQkJQklUKDApCisJI2RlZmluZSBVMlBfUjBfUE9XRVJfT0sJCQkJCUJJVCgx KQorCSNkZWZpbmUgVTJQX1IwX0hBU1RfTU9ERQkJCQlCSVQoMikKKwkjZGVmaW5lIFUyUF9SMF9Q T1dFUl9PTl9SRVNFVAkJCQlCSVQoMykKKwkjZGVmaW5lIFUyUF9SMF9JRF9QVUxMVVAJCQkJQklU KDQpCisJI2RlZmluZSBVMlBfUjBfRFJWX1ZCVVMJCQkJCUJJVCg1KQorCisjZGVmaW5lIFUyUF9S MQkJCQkJCQkweDQKKwkjZGVmaW5lIFUyUF9SMV9QSFlfUkVBRFkJCQkJQklUKDApCisJI2RlZmlu ZSBVMlBfUjFfSURfRElHCQkJCQlCSVQoMSkKKwkjZGVmaW5lIFUyUF9SMV9PVEdfU0VTU0lPTl9W QUxJRAkJCUJJVCgyKQorCSNkZWZpbmUgVTJQX1IxX1ZCVVNfVkFMSUQJCQkJQklUKDMpCisKKy8q IFVTQiBHbHVlIENvbnRyb2wgUmVnaXN0ZXJzICovCisKKyNkZWZpbmUgVVNCX1IwCQkJCQkJCTB4 ODAKKwkjZGVmaW5lIFVTQl9SMF9QMzBfTEFORTBfVFgyUlhfTE9PUEJBQ0sJCQlCSVQoMTcpCisJ I2RlZmluZSBVU0JfUjBfUDMwX0xBTkUwX0VYVF9QQ0xLX1JFUQkJCUJJVCgxOCkKKwkjZGVmaW5l IFVTQl9SMF9QMzBfUENTX1JYX0xPU19NQVNLX1ZBTF9NQVNLCQlHRU5NQVNLKDI4LCAxOSkKKwkj ZGVmaW5lIFVTQl9SMF9VMkRfU1NfU0NBTEVET1dOX01PREVfTUFTSwkJR0VOTUFTSygzMCwgMjkp CisJI2RlZmluZSBVU0JfUjBfVTJEX0FDVAkJCQkJQklUKDMxKQorCisjZGVmaW5lIFVTQl9SMQkJ CQkJCQkweDg0CisJI2RlZmluZSBVU0JfUjFfVTNIX0JJR0VORElBTl9HUwkJCQlCSVQoMCkKKwkj ZGVmaW5lIFVTQl9SMV9VM0hfUE1FX0VOQUJMRQkJCQlCSVQoMSkKKwkjZGVmaW5lIFVTQl9SMV9V M0hfSFVCX1BPUlRfT1ZFUkNVUlJFTlRfTUFTSwkJR0VOTUFTSyg0LCAyKQorCSNkZWZpbmUgVVNC X1IxX1UzSF9IVUJfUE9SVF9QRVJNX0FUVEFDSF9NQVNLCQlHRU5NQVNLKDksIDcpCisJI2RlZmlu ZSBVU0JfUjFfVTNIX0hPU1RfVTJfUE9SVF9ESVNBQkxFX01BU0sJCUdFTk1BU0soMTMsIDEyKQor CSNkZWZpbmUgVVNCX1IxX1UzSF9IT1NUX1UzX1BPUlRfRElTQUJMRQkJCUJJVCgxNikKKwkjZGVm aW5lIFVTQl9SMV9VM0hfSE9TVF9QT1JUX1BPV0VSX0NPTlRST0xfUFJFU0VOVAlCSVQoMTcpCisJ I2RlZmluZSBVU0JfUjFfVTNIX0hPU1RfTVNJX0VOQUJMRQkJCUJJVCgxOCkKKwkjZGVmaW5lIFVT Ql9SMV9VM0hfRkxBREpfMzBNSFpfUkVHX01BU0sJCQlHRU5NQVNLKDI0LCAxOSkKKwkjZGVmaW5l IFVTQl9SMV9QMzBfUENTX1RYX1NXSU5HX0ZVTExfTUFTSwkJR0VOTUFTSygzMSwgMjUpCisKKyNk ZWZpbmUgVVNCX1IyCQkJCQkJCTB4ODgKKwkjZGVmaW5lIFVTQl9SMl9QMzBfUENTX1RYX0RFRU1Q SF8zUDVEQl9NQVNLCQlHRU5NQVNLKDI1LCAyMCkKKwkjZGVmaW5lIFVTQl9SMl9QMzBfUENTX1RY X0RFRU1QSF82REJfTUFTSwkJR0VOTUFTSygzMSwgMjYpCisKKyNkZWZpbmUgVVNCX1IzCQkJCQkJ CTB4OGMKKwkjZGVmaW5lIFVTQl9SM19QMzBfU1NDX0VOQUJMRQkJCQlCSVQoMCkKKwkjZGVmaW5l IFVTQl9SM19QMzBfU1NDX1JBTkdFX01BU0sJCQlHRU5NQVNLKDMsIDEpCisJI2RlZmluZSBVU0Jf UjNfUDMwX1NTQ19SRUZfQ0xLX1NFTF9NQVNLCQkJR0VOTUFTSygxMiwgNCkKKwkjZGVmaW5lIFVT Ql9SM19QMzBfUkVGX1NTUF9FTgkJCQlCSVQoMTMpCisKKyNkZWZpbmUgVVNCX1I0CQkJCQkJCTB4 OTAKKwkjZGVmaW5lIFVTQl9SNF9QMjFfUE9SVF9SRVNFVF8wCQkJCUJJVCgwKQorCSNkZWZpbmUg VVNCX1I0X1AyMV9TTEVFUF9NMAkJCQlCSVQoMSkKKwkjZGVmaW5lIFVTQl9SNF9NRU1fUERfTUFT SwkJCQlHRU5NQVNLKDMsIDIpCisJI2RlZmluZSBVU0JfUjRfUDIxX09OTFkJCQkJCUJJVCg0KQor CisjZGVmaW5lIFVTQl9SNQkJCQkJCQkweDk0CisJI2RlZmluZSBVU0JfUjVfSURfRElHX1NZTkMJ CQkJQklUKDApCisJI2RlZmluZSBVU0JfUjVfSURfRElHX1JFRwkJCQlCSVQoMSkKKwkjZGVmaW5l IFVTQl9SNV9JRF9ESUdfQ0ZHX01BU0sJCQkJR0VOTUFTSygzLCAyKQorCSNkZWZpbmUgVVNCX1I1 X0lEX0RJR19FTl8wCQkJCUJJVCg0KQorCSNkZWZpbmUgVVNCX1I1X0lEX0RJR19FTl8xCQkJCUJJ VCg1KQorCSNkZWZpbmUgVVNCX1I1X0lEX0RJR19DVVJSCQkJCUJJVCg2KQorCSNkZWZpbmUgVVNC X1I1X0lEX0RJR19JUlEJCQkJQklUKDcpCisJI2RlZmluZSBVU0JfUjVfSURfRElHX1RIX01BU0sJ CQkJR0VOTUFTSygxNSwgOCkKKwkjZGVmaW5lIFVTQl9SNV9JRF9ESUdfQ05UX01BU0sJCQkJR0VO TUFTSygyMywgMTYpCisKK2VudW0geworCVVTQjJfSE9TVF9QSFkgPSAwLAorCVVTQjJfT1RHX1BI WSwKKwlVU0IzX0hPU1RfUEhZLAorCVBIWV9DT1VOVCwKK307CisKK3N0YXRpYyBjb25zdCBjaGFy ICpwaHlfbmFtZXNbUEhZX0NPVU5UXSA9IHsKKwkidXNiMi1waHkwIiwgInVzYjItcGh5MSIsICJ1 c2IzLXBoeTAiLAorfTsKKworc3RydWN0IGR3YzNfbWVzb25fZzEyYSB7CisJc3RydWN0IGRldmlj ZQkJKmRldjsKKwlzdHJ1Y3QgcmVnbWFwCQkqcmVnbWFwOworCXN0cnVjdCBjbGsJCSpjbGs7CisJ c3RydWN0IHJlc2V0X2NvbnRyb2wJKnJlc2V0OworCXN0cnVjdCBwaHkJCSpwaHlzW1BIWV9DT1VO VF07CisJZW51bSB1c2JfZHJfbW9kZQlvdGdfbW9kZTsKKwllbnVtIHBoeV9tb2RlCQlvdGdfcGh5 X21vZGU7CisJdW5zaWduZWQgaW50CQl1c2IyX3BvcnRzOworCXVuc2lnbmVkIGludAkJdXNiM19w b3J0czsKKwlzdHJ1Y3QgcmVndWxhdG9yCSp2YnVzOworCXN0cnVjdCB1c2Jfcm9sZV9zd2l0Y2hf ZGVzYyBzd2l0Y2hfZGVzYzsKKwlzdHJ1Y3QgdXNiX3JvbGVfc3dpdGNoCSpyb2xlX3N3aXRjaDsK K307CisKK3N0YXRpYyB2b2lkIGR3YzNfbWVzb25fZzEyYV91c2IyX3NldF9tb2RlKHN0cnVjdCBk d2MzX21lc29uX2cxMmEgKnByaXYsCisJCQkJCSAgaW50IGksIGVudW0gcGh5X21vZGUgbW9kZSkK K3sKKwlpZiAobW9kZSA9PSBQSFlfTU9ERV9VU0JfSE9TVCkKKwkJcmVnbWFwX3VwZGF0ZV9iaXRz KHByaXYtPnJlZ21hcCwgVTJQX1IwICsgKFUyUF9SRUdfU0laRSAqIGkpLAorCQkJCVUyUF9SMF9I T1NUX0RFVklDRSwKKwkJCQlVMlBfUjBfSE9TVF9ERVZJQ0UpOworCWVsc2UKKwkJcmVnbWFwX3Vw ZGF0ZV9iaXRzKHByaXYtPnJlZ21hcCwgVTJQX1IwICsgKFUyUF9SRUdfU0laRSAqIGkpLAorCQkJ CVUyUF9SMF9IT1NUX0RFVklDRSwgMCk7Cit9CisKK3N0YXRpYyBpbnQgZHdjM19tZXNvbl9nMTJh X3VzYjJfaW5pdChzdHJ1Y3QgZHdjM19tZXNvbl9nMTJhICpwcml2KQoreworCWludCBpOworCisJ aWYgKHByaXYtPm90Z19tb2RlID09IFVTQl9EUl9NT0RFX1BFUklQSEVSQUwpCisJCXByaXYtPm90 Z19waHlfbW9kZSA9IFBIWV9NT0RFX1VTQl9ERVZJQ0U7CisJZWxzZQorCQlwcml2LT5vdGdfcGh5 X21vZGUgPSBQSFlfTU9ERV9VU0JfSE9TVDsKKworCWZvciAoaSA9IDAgOyBpIDwgVVNCM19IT1NU X1BIWSA7ICsraSkgeworCQlpZiAoIXByaXYtPnBoeXNbaV0pCisJCQljb250aW51ZTsKKworCQly ZWdtYXBfdXBkYXRlX2JpdHMocHJpdi0+cmVnbWFwLCBVMlBfUjAgKyAoVTJQX1JFR19TSVpFICog aSksCisJCQkJICAgVTJQX1IwX1BPV0VSX09OX1JFU0VULAorCQkJCSAgIFUyUF9SMF9QT1dFUl9P Tl9SRVNFVCk7CisKKwkJaWYgKGkgPT0gVVNCMl9PVEdfUEhZKSB7CisJCQlyZWdtYXBfdXBkYXRl X2JpdHMocHJpdi0+cmVnbWFwLAorCQkJCVUyUF9SMCArIChVMlBfUkVHX1NJWkUgKiBpKSwKKwkJ CQlVMlBfUjBfSURfUFVMTFVQIHwgVTJQX1IwX0RSVl9WQlVTLAorCQkJCVUyUF9SMF9JRF9QVUxM VVAgfCBVMlBfUjBfRFJWX1ZCVVMpOworCisJCQlkd2MzX21lc29uX2cxMmFfdXNiMl9zZXRfbW9k ZShwcml2LCBpLAorCQkJCQkJICAgICAgcHJpdi0+b3RnX3BoeV9tb2RlKTsKKwkJfSBlbHNlCisJ CQlkd2MzX21lc29uX2cxMmFfdXNiMl9zZXRfbW9kZShwcml2LCBpLAorCQkJCQkJICAgICAgUEhZ X01PREVfVVNCX0hPU1QpOworCisJCXJlZ21hcF91cGRhdGVfYml0cyhwcml2LT5yZWdtYXAsIFUy UF9SMCArIChVMlBfUkVHX1NJWkUgKiBpKSwKKwkJCQkgICBVMlBfUjBfUE9XRVJfT05fUkVTRVQs IDApOworCX0KKworCXJldHVybiAwOworfQorCitzdGF0aWMgdm9pZCBkd2MzX21lc29uX2cxMmFf dXNiM19pbml0KHN0cnVjdCBkd2MzX21lc29uX2cxMmEgKnByaXYpCit7CisJcmVnbWFwX3VwZGF0 ZV9iaXRzKHByaXYtPnJlZ21hcCwgVVNCX1IzLAorCQkJVVNCX1IzX1AzMF9TU0NfUkFOR0VfTUFT SyB8CisJCQlVU0JfUjNfUDMwX1JFRl9TU1BfRU4sCisJCQlVU0JfUjNfUDMwX1NTQ19FTkFCTEUg fAorCQkJRklFTERfUFJFUChVU0JfUjNfUDMwX1NTQ19SQU5HRV9NQVNLLCAyKSB8CisJCQlVU0Jf UjNfUDMwX1JFRl9TU1BfRU4pOworCXVkZWxheSgyKTsKKworCXJlZ21hcF91cGRhdGVfYml0cyhw cml2LT5yZWdtYXAsIFVTQl9SMiwKKwkJCVVTQl9SMl9QMzBfUENTX1RYX0RFRU1QSF8zUDVEQl9N QVNLLAorCQkJRklFTERfUFJFUChVU0JfUjJfUDMwX1BDU19UWF9ERUVNUEhfM1A1REJfTUFTSywg MHgxNSkpOworCisJcmVnbWFwX3VwZGF0ZV9iaXRzKHByaXYtPnJlZ21hcCwgVVNCX1IyLAorCQkJ VVNCX1IyX1AzMF9QQ1NfVFhfREVFTVBIXzZEQl9NQVNLLAorCQkJRklFTERfUFJFUChVU0JfUjJf UDMwX1BDU19UWF9ERUVNUEhfNkRCX01BU0ssIDB4MjApKTsKKworCXVkZWxheSgyKTsKKworCXJl Z21hcF91cGRhdGVfYml0cyhwcml2LT5yZWdtYXAsIFVTQl9SMSwKKwkJCVVTQl9SMV9VM0hfSE9T VF9QT1JUX1BPV0VSX0NPTlRST0xfUFJFU0VOVCwKKwkJCVVTQl9SMV9VM0hfSE9TVF9QT1JUX1BP V0VSX0NPTlRST0xfUFJFU0VOVCk7CisKKwlyZWdtYXBfdXBkYXRlX2JpdHMocHJpdi0+cmVnbWFw LCBVU0JfUjEsCisJCQlVU0JfUjFfUDMwX1BDU19UWF9TV0lOR19GVUxMX01BU0ssCisJCQlGSUVM RF9QUkVQKFVTQl9SMV9QMzBfUENTX1RYX1NXSU5HX0ZVTExfTUFTSywgMTI3KSk7Cit9CisKK3N0 YXRpYyB2b2lkIGR3YzNfbWVzb25fZzEyYV91c2JfaW5pdF9tb2RlKHN0cnVjdCBkd2MzX21lc29u X2cxMmEgKnByaXYpCit7CisJaWYgKHByaXYtPm90Z19waHlfbW9kZSA9PSBQSFlfTU9ERV9VU0Jf REVWSUNFKSB7CisJCXJlZ21hcF91cGRhdGVfYml0cyhwcml2LT5yZWdtYXAsIFVTQl9SMCwKKwkJ CQlVU0JfUjBfVTJEX0FDVCwgVVNCX1IwX1UyRF9BQ1QpOworCQlyZWdtYXBfdXBkYXRlX2JpdHMo cHJpdi0+cmVnbWFwLCBVU0JfUjAsCisJCQkJVVNCX1IwX1UyRF9TU19TQ0FMRURPV05fTU9ERV9N QVNLLCAwKTsKKwkJcmVnbWFwX3VwZGF0ZV9iaXRzKHByaXYtPnJlZ21hcCwgVVNCX1I0LAorCQkJ CVVTQl9SNF9QMjFfU0xFRVBfTTAsIFVTQl9SNF9QMjFfU0xFRVBfTTApOworCX0gZWxzZSB7CisJ CXJlZ21hcF91cGRhdGVfYml0cyhwcml2LT5yZWdtYXAsIFVTQl9SMCwKKwkJCQlVU0JfUjBfVTJE X0FDVCwgMCk7CisJCXJlZ21hcF91cGRhdGVfYml0cyhwcml2LT5yZWdtYXAsIFVTQl9SNCwKKwkJ CQlVU0JfUjRfUDIxX1NMRUVQX00wLCAwKTsKKwl9Cit9CisKK3N0YXRpYyBpbnQgZHdjM19tZXNv bl9nMTJhX3VzYl9pbml0KHN0cnVjdCBkd2MzX21lc29uX2cxMmEgKnByaXYpCit7CisJaW50IHJl dDsKKworCXJldCA9IGR3YzNfbWVzb25fZzEyYV91c2IyX2luaXQocHJpdik7CisJaWYgKHJldCkK KwkJcmV0dXJuIHJldDsKKworCXJlZ21hcF91cGRhdGVfYml0cyhwcml2LT5yZWdtYXAsIFVTQl9S MSwKKwkJCVVTQl9SMV9VM0hfRkxBREpfMzBNSFpfUkVHX01BU0ssCisJCQlGSUVMRF9QUkVQKFVT Ql9SMV9VM0hfRkxBREpfMzBNSFpfUkVHX01BU0ssIDB4MjApKTsKKworCXJlZ21hcF91cGRhdGVf Yml0cyhwcml2LT5yZWdtYXAsIFVTQl9SNSwKKwkJCVVTQl9SNV9JRF9ESUdfRU5fMCwKKwkJCVVT Ql9SNV9JRF9ESUdfRU5fMCk7CisJcmVnbWFwX3VwZGF0ZV9iaXRzKHByaXYtPnJlZ21hcCwgVVNC X1I1LAorCQkJVVNCX1I1X0lEX0RJR19FTl8xLAorCQkJVVNCX1I1X0lEX0RJR19FTl8xKTsKKwly ZWdtYXBfdXBkYXRlX2JpdHMocHJpdi0+cmVnbWFwLCBVU0JfUjUsCisJCQlVU0JfUjVfSURfRElH X1RIX01BU0ssCisJCQlGSUVMRF9QUkVQKFVTQl9SNV9JRF9ESUdfVEhfTUFTSywgMHhmZikpOwor CisJLyogSWYgd2UgaGF2ZSBhbiBhY3R1YWwgU3VwZXJTcGVlZCBwb3J0LCBpbml0aWFsaXplIGl0 ICovCisJaWYgKHByaXYtPnVzYjNfcG9ydHMpCisJCWR3YzNfbWVzb25fZzEyYV91c2IzX2luaXQo cHJpdik7CisKKwlkd2MzX21lc29uX2cxMmFfdXNiX2luaXRfbW9kZShwcml2KTsKKworCXJldHVy biAwOworfQorCitzdGF0aWMgY29uc3Qgc3RydWN0IHJlZ21hcF9jb25maWcgcGh5X21lc29uX2cx MmFfdXNiM19yZWdtYXBfY29uZiA9IHsKKwkucmVnX2JpdHMgPSA4LAorCS52YWxfYml0cyA9IDMy LAorCS5yZWdfc3RyaWRlID0gNCwKKwkubWF4X3JlZ2lzdGVyID0gVVNCX1I1LAorfTsKKworc3Rh dGljIGludCBkd2MzX21lc29uX2cxMmFfZ2V0X3BoeXMoc3RydWN0IGR3YzNfbWVzb25fZzEyYSAq cHJpdikKK3sKKwlpbnQgaTsKKworCWZvciAoaSA9IDAgOyBpIDwgUEhZX0NPVU5UIDsgKytpKSB7 CisJCXByaXYtPnBoeXNbaV0gPSBkZXZtX3BoeV9vcHRpb25hbF9nZXQocHJpdi0+ZGV2LCBwaHlf bmFtZXNbaV0pOworCQlpZiAoIXByaXYtPnBoeXNbaV0pCisJCQljb250aW51ZTsKKworCQlpZiAo SVNfRVJSKHByaXYtPnBoeXNbaV0pKQorCQkJcmV0dXJuIFBUUl9FUlIocHJpdi0+cGh5c1tpXSk7 CisKKwkJaWYgKGkgPT0gVVNCM19IT1NUX1BIWSkKKwkJCXByaXYtPnVzYjNfcG9ydHMrKzsKKwkJ ZWxzZQorCQkJcHJpdi0+dXNiMl9wb3J0cysrOworCX0KKworCWRldl9pbmZvKHByaXYtPmRldiwg IlVTQjIgcG9ydHM6ICVkXG4iLCBwcml2LT51c2IyX3BvcnRzKTsKKwlkZXZfaW5mbyhwcml2LT5k ZXYsICJVU0IzIHBvcnRzOiAlZFxuIiwgcHJpdi0+dXNiM19wb3J0cyk7CisKKwlyZXR1cm4gMDsK K30KKworc3RhdGljIGVudW0gcGh5X21vZGUgZHdjM19tZXNvbl9nMTJhX2dldF9pZChzdHJ1Y3Qg ZHdjM19tZXNvbl9nMTJhICpwcml2KQoreworCXUzMiByZWc7CisKKwlyZWdtYXBfcmVhZChwcml2 LT5yZWdtYXAsIFVTQl9SNSwgJnJlZyk7CisKKwlpZiAocmVnICYgKFVTQl9SNV9JRF9ESUdfU1lO QyB8IFVTQl9SNV9JRF9ESUdfUkVHKSkKKwkJcmV0dXJuIFBIWV9NT0RFX1VTQl9ERVZJQ0U7CisK KwlyZXR1cm4gUEhZX01PREVfVVNCX0hPU1Q7Cit9CisKK3N0YXRpYyBpbnQgZHdjM19tZXNvbl9n MTJhX290Z19tb2RlX3NldChzdHJ1Y3QgZHdjM19tZXNvbl9nMTJhICpwcml2LAorCQkJCQllbnVt IHBoeV9tb2RlIG1vZGUpCit7CisJaW50IHJldDsKKworCWlmICghcHJpdi0+cGh5c1tVU0IyX09U R19QSFldKQorCQlyZXR1cm4gLUVJTlZBTDsKKworCWlmIChtb2RlID09IFBIWV9NT0RFX1VTQl9I T1NUKQorCQlkZXZfaW5mbyhwcml2LT5kZXYsICJzd2l0Y2hpbmcgdG8gSG9zdCBNb2RlXG4iKTsK KwllbHNlCisJCWRldl9pbmZvKHByaXYtPmRldiwgInN3aXRjaGluZyB0byBEZXZpY2UgTW9kZVxu Iik7CisKKwlpZiAocHJpdi0+dmJ1cykgeworCQlpZiAobW9kZSA9PSBQSFlfTU9ERV9VU0JfREVW SUNFKQorCQkJcmV0ID0gcmVndWxhdG9yX2Rpc2FibGUocHJpdi0+dmJ1cyk7CisJCWVsc2UKKwkJ CXJldCA9IHJlZ3VsYXRvcl9lbmFibGUocHJpdi0+dmJ1cyk7CisJCWlmIChyZXQpCisJCQlyZXR1 cm4gcmV0OworCX0KKworCXByaXYtPm90Z19waHlfbW9kZSA9IG1vZGU7CisKKwlkd2MzX21lc29u X2cxMmFfdXNiMl9zZXRfbW9kZShwcml2LCBVU0IyX09UR19QSFksIG1vZGUpOworCisJZHdjM19t ZXNvbl9nMTJhX3VzYl9pbml0X21vZGUocHJpdik7CisKKwlyZXR1cm4gcGh5X3NldF9tb2RlKHBy aXYtPnBoeXNbVVNCMl9PVEdfUEhZXSwgbW9kZSk7Cit9CisKK3N0YXRpYyBpbnQgZHdjM19tZXNv bl9nMTJhX3JvbGVfc2V0KHN0cnVjdCBkZXZpY2UgKmRldiwgZW51bSB1c2Jfcm9sZSByb2xlKQor eworCXN0cnVjdCBkd2MzX21lc29uX2cxMmEgKnByaXYgPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsK KwllbnVtIHBoeV9tb2RlIG1vZGU7CisKKwlpZiAocm9sZSA9PSBVU0JfUk9MRV9OT05FKQorCQly ZXR1cm4gMDsKKworCW1vZGUgPSByb2xlID09IFVTQl9ST0xFX0hPU1QgPyBQSFlfTU9ERV9VU0Jf SE9TVCA6IFBIWV9NT0RFX1VTQl9ERVZJQ0U7CisKKwlpZiAobW9kZSA9PSBwcml2LT5vdGdfcGh5 X21vZGUpCisJCXJldHVybiAwOworCisJcmV0dXJuIGR3YzNfbWVzb25fZzEyYV9vdGdfbW9kZV9z ZXQocHJpdiwgbW9kZSk7Cit9CisKK3N0YXRpYyBlbnVtIHVzYl9yb2xlIGR3YzNfbWVzb25fZzEy YV9yb2xlX2dldChzdHJ1Y3QgZGV2aWNlICpkZXYpCit7CisJc3RydWN0IGR3YzNfbWVzb25fZzEy YSAqcHJpdiA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOworCisJcmV0dXJuIHByaXYtPm90Z19waHlf bW9kZSA9PSBQSFlfTU9ERV9VU0JfSE9TVCA/CisJCVVTQl9ST0xFX0hPU1QgOiBVU0JfUk9MRV9E RVZJQ0U7Cit9CisKK3N0YXRpYyBzdHJ1Y3QgZGV2aWNlICpkd2MzX21lc29uX2cxMl9maW5kX2No aWxkKHN0cnVjdCBkZXZpY2UgKmRldiwKKwkJCQkJCWNvbnN0IGNoYXIgKmNvbXBhdGlibGUpCit7 CisJc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldjsKKwlzdHJ1Y3QgZGV2aWNlX25vZGUgKm5w OworCisJbnAgPSBvZl9maW5kX2NvbXBhdGlibGVfbm9kZShkZXYtPm9mX25vZGUsIE5VTEwsIGNv bXBhdGlibGUpOworCWlmICghbnApCisJCXJldHVybiBOVUxMOworCisJcGRldiA9IG9mX2ZpbmRf ZGV2aWNlX2J5X25vZGUobnApOworCW9mX25vZGVfcHV0KG5wKTsKKwlpZiAoIXBkZXYpCisJCXJl dHVybiBOVUxMOworCisJcmV0dXJuICZwZGV2LT5kZXY7Cit9CisKK3N0YXRpYyBpbnQgZHdjM19t ZXNvbl9nMTJhX3Byb2JlKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYpCit7CisJc3RydWN0 IGR3YzNfbWVzb25fZzEyYQkqcHJpdjsKKwlzdHJ1Y3QgZGV2aWNlCQkqZGV2ID0gJnBkZXYtPmRl djsKKwlzdHJ1Y3QgZGV2aWNlX25vZGUJKm5wID0gZGV2LT5vZl9ub2RlOworCXZvaWQgX19pb21l bSAqYmFzZTsKKwlzdHJ1Y3QgcmVzb3VyY2UgKnJlczsKKwllbnVtIHBoeV9tb2RlIG90Z19pZDsK KwlpbnQgcmV0LCBpOworCisJcHJpdiA9IGRldm1fa3phbGxvYyhkZXYsIHNpemVvZigqcHJpdiks IEdGUF9LRVJORUwpOworCWlmICghcHJpdikKKwkJcmV0dXJuIC1FTk9NRU07CisKKwlyZXMgPSBw bGF0Zm9ybV9nZXRfcmVzb3VyY2UocGRldiwgSU9SRVNPVVJDRV9NRU0sIDApOworCWJhc2UgPSBk ZXZtX2lvcmVtYXBfcmVzb3VyY2UoZGV2LCByZXMpOworCWlmIChJU19FUlIoYmFzZSkpCisJCXJl dHVybiBQVFJfRVJSKGJhc2UpOworCisJcHJpdi0+cmVnbWFwID0gZGV2bV9yZWdtYXBfaW5pdF9t bWlvKGRldiwgYmFzZSwKKwkJCQkJICAgICAmcGh5X21lc29uX2cxMmFfdXNiM19yZWdtYXBfY29u Zik7CisJaWYgKElTX0VSUihwcml2LT5yZWdtYXApKQorCQlyZXR1cm4gUFRSX0VSUihwcml2LT5y ZWdtYXApOworCisJcHJpdi0+dmJ1cyA9IGRldm1fcmVndWxhdG9yX2dldF9vcHRpb25hbChkZXYs ICJ2YnVzIik7CisJaWYgKElTX0VSUihwcml2LT52YnVzKSkgeworCQlpZiAoUFRSX0VSUihwcml2 LT52YnVzKSA9PSAtRVBST0JFX0RFRkVSKQorCQkJcmV0dXJuIFBUUl9FUlIocHJpdi0+dmJ1cyk7 CisJCXByaXYtPnZidXMgPSBOVUxMOworCX0KKworCXByaXYtPmNsayA9IGRldm1fY2xrX2dldChk ZXYsIE5VTEwpOworCWlmIChJU19FUlIocHJpdi0+Y2xrKSkKKwkJcmV0dXJuIFBUUl9FUlIocHJp di0+Y2xrKTsKKworCXJldCA9IGNsa19wcmVwYXJlX2VuYWJsZShwcml2LT5jbGspOworCWlmIChy ZXQpCisJCXJldHVybiByZXQ7CisKKwlwbGF0Zm9ybV9zZXRfZHJ2ZGF0YShwZGV2LCBwcml2KTsK Kwlwcml2LT5kZXYgPSBkZXY7CisKKwlwcml2LT5yZXNldCA9IGRldm1fcmVzZXRfY29udHJvbF9n ZXQoZGV2LCBOVUxMKTsKKwlpZiAoSVNfRVJSKHByaXYtPnJlc2V0KSkgeworCQlyZXQgPSBQVFJf RVJSKHByaXYtPnJlc2V0KTsKKwkJZGV2X2VycihkZXYsICJmYWlsZWQgdG8gZ2V0IGRldmljZSBy ZXNldCwgZXJyPSVkXG4iLCByZXQpOworCQlyZXR1cm4gcmV0OworCX0KKworCXJldCA9IHJlc2V0 X2NvbnRyb2xfcmVzZXQocHJpdi0+cmVzZXQpOworCWlmIChyZXQpCisJCXJldHVybiByZXQ7CisK KwlyZXQgPSBkd2MzX21lc29uX2cxMmFfZ2V0X3BoeXMocHJpdik7CisJaWYgKHJldCkKKwkJcmV0 dXJuIHJldDsKKworCXJldCA9IHJlZ3VsYXRvcl9lbmFibGUocHJpdi0+dmJ1cyk7CisJaWYgKHJl dCkKKwkJcmV0dXJuIHJldDsKKworCS8qIEdldCBkcl9tb2RlICovCisJcHJpdi0+b3RnX21vZGUg PSB1c2JfZ2V0X2RyX21vZGUoZGV2KTsKKworCWR3YzNfbWVzb25fZzEyYV91c2JfaW5pdChwcml2 KTsKKworCS8qIFNldCBQSFkgUG93ZXIgKi8KKwlmb3IgKGkgPSAwIDsgaSA8IFBIWV9DT1VOVCA7 ICsraSkgeworCQlyZXQgPSBwaHlfcG93ZXJfb24ocHJpdi0+cGh5c1tpXSk7CisJCWlmIChyZXQp CisJCQlnb3RvIGVycl9waHlzX3B1dDsKKwl9CisKKwkvKiBJbml0IFBIWXMgKi8KKwlmb3IgKGkg PSAwIDsgaSA8IFBIWV9DT1VOVCA7ICsraSkgeworCQlyZXQgPSBwaHlfaW5pdChwcml2LT5waHlz W2ldKTsKKwkJaWYgKHJldCkKKwkJCWdvdG8gZXJyX3BoeXNfcG93ZXI7CisJfQorCisJcmV0ID0g b2ZfcGxhdGZvcm1fcG9wdWxhdGUobnAsIE5VTEwsIE5VTEwsIGRldik7CisJaWYgKHJldCkgewor CQljbGtfZGlzYWJsZV91bnByZXBhcmUocHJpdi0+Y2xrKTsKKwkJY2xrX3B1dChwcml2LT5jbGsp OworCisJCWdvdG8gZXJyX3BoeXNfZXhpdDsKKwl9CisKKwkvKiBTZXR1cCBPVEcgbW9kZSBjb3Jy ZXNwb25kaW5nIHRvIHRoZSBJRCBwaW4gKi8KKwlpZiAocHJpdi0+b3RnX21vZGUgPT0gVVNCX0RS X01PREVfT1RHKSB7CisJCS8qIFRPRklYIEhhbmRsZSBJRCBtb2RlIHRvZ2dsaW5nIHZpYSBJUlEg Ki8KKwkJb3RnX2lkID0gZHdjM19tZXNvbl9nMTJhX2dldF9pZChwcml2KTsKKwkJaWYgKG90Z19p ZCAhPSBwcml2LT5vdGdfcGh5X21vZGUpIHsKKwkJCWlmIChkd2MzX21lc29uX2cxMmFfb3RnX21v ZGVfc2V0KHByaXYsIG90Z19pZCkpCisJCQkJZGV2X3dhcm4oZGV2LCAiRmFpbGVkIHRvIHN3aXRj aCBPVEcgbW9kZVxuIik7CisJCX0KKwl9CisKKwkvKiBTZXR1cCByb2xlIHN3aXRjaGVyICovCisJ cHJpdi0+c3dpdGNoX2Rlc2MudXNiMl9wb3J0ID0gZHdjM19tZXNvbl9nMTJfZmluZF9jaGlsZChk ZXYsCisJCQkJCQkJCSJzbnBzLGR3YzMiKTsKKwlwcml2LT5zd2l0Y2hfZGVzYy51ZGMgPSBkd2Mz X21lc29uX2cxMl9maW5kX2NoaWxkKGRldiwgInNucHMsZHdjMiIpOworCXByaXYtPnN3aXRjaF9k ZXNjLmFsbG93X3VzZXJzcGFjZV9jb250cm9sID0gdHJ1ZTsKKwlwcml2LT5zd2l0Y2hfZGVzYy5z ZXQgPSBkd2MzX21lc29uX2cxMmFfcm9sZV9zZXQ7CisJcHJpdi0+c3dpdGNoX2Rlc2MuZ2V0ID0g ZHdjM19tZXNvbl9nMTJhX3JvbGVfZ2V0OworCisJcHJpdi0+cm9sZV9zd2l0Y2ggPSB1c2Jfcm9s ZV9zd2l0Y2hfcmVnaXN0ZXIoZGV2LCAmcHJpdi0+c3dpdGNoX2Rlc2MpOworCWlmIChJU19FUlIo cHJpdi0+cm9sZV9zd2l0Y2gpKQorCQlkZXZfd2FybihkZXYsICJVbmFibGUgdG8gcmVnaXN0ZXIg Um9sZSBTd2l0Y2hcbiIpOworCisJcG1fcnVudGltZV9zZXRfYWN0aXZlKGRldik7CisJcG1fcnVu dGltZV9lbmFibGUoZGV2KTsKKwlwbV9ydW50aW1lX2dldF9zeW5jKGRldik7CisKKwlyZXR1cm4g MDsKKworZXJyX3BoeXNfZXhpdDoKKwlmb3IgKGkgPSAwIDsgaSA8IFBIWV9DT1VOVCA7ICsraSkK KwkJcGh5X2V4aXQocHJpdi0+cGh5c1tpXSk7CisKK2Vycl9waHlzX3Bvd2VyOgorCWZvciAoaSA9 IDAgOyBpIDwgUEhZX0NPVU5UIDsgKytpKQorCQlwaHlfcG93ZXJfb2ZmKHByaXYtPnBoeXNbaV0p OworCitlcnJfcGh5c19wdXQ6CisJZm9yIChpID0gMCA7IGkgPCBQSFlfQ09VTlQgOyArK2kpCisJ CXBoeV9wdXQocHJpdi0+cGh5c1tpXSk7CisKKwlyZXR1cm4gcmV0OworfQorCitzdGF0aWMgaW50 IGR3YzNfbWVzb25fZzEyYV9yZW1vdmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikKK3sK KwlzdHJ1Y3QgZHdjM19tZXNvbl9nMTJhICpwcml2ID0gcGxhdGZvcm1fZ2V0X2RydmRhdGEocGRl dik7CisJc3RydWN0IGRldmljZSAqZGV2ID0gJnBkZXYtPmRldjsKKwlpbnQgaTsKKworCXVzYl9y b2xlX3N3aXRjaF91bnJlZ2lzdGVyKHByaXYtPnJvbGVfc3dpdGNoKTsKKworCW9mX3BsYXRmb3Jt X2RlcG9wdWxhdGUoZGV2KTsKKworCWZvciAoaSA9IDAgOyBpIDwgUEhZX0NPVU5UIDsgKytpKSB7 CisJCXBoeV9wb3dlcl9vZmYocHJpdi0+cGh5c1tpXSk7CisJCXBoeV9leGl0KHByaXYtPnBoeXNb aV0pOworCQlwaHlfcHV0KHByaXYtPnBoeXNbaV0pOworCX0KKworCWNsa19kaXNhYmxlX3VucHJl cGFyZShwcml2LT5jbGspOworCWNsa19wdXQocHJpdi0+Y2xrKTsKKworCXBtX3J1bnRpbWVfZGlz YWJsZShkZXYpOworCXBtX3J1bnRpbWVfcHV0X25vaWRsZShkZXYpOworCXBtX3J1bnRpbWVfc2V0 X3N1c3BlbmRlZChkZXYpOworCisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyBpbnQgX19tYXliZV91 bnVzZWQgZHdjM19tZXNvbl9nMTJhX3J1bnRpbWVfc3VzcGVuZChzdHJ1Y3QgZGV2aWNlICpkZXYp Cit7CisJc3RydWN0IGR3YzNfbWVzb25fZzEyYQkqcHJpdiA9IGRldl9nZXRfZHJ2ZGF0YShkZXYp OworCisJY2xrX2Rpc2FibGUocHJpdi0+Y2xrKTsKKworCXJldHVybiAwOworfQorCitzdGF0aWMg aW50IF9fbWF5YmVfdW51c2VkIGR3YzNfbWVzb25fZzEyYV9ydW50aW1lX3Jlc3VtZShzdHJ1Y3Qg ZGV2aWNlICpkZXYpCit7CisJc3RydWN0IGR3YzNfbWVzb25fZzEyYQkqcHJpdiA9IGRldl9nZXRf ZHJ2ZGF0YShkZXYpOworCisJcmV0dXJuIGNsa19lbmFibGUocHJpdi0+Y2xrKTsKK30KKworc3Rh dGljIGludCBfX21heWJlX3VudXNlZCBkd2MzX21lc29uX2cxMmFfc3VzcGVuZChzdHJ1Y3QgZGV2 aWNlICpkZXYpCit7CisJc3RydWN0IGR3YzNfbWVzb25fZzEyYSAqcHJpdiA9IGRldl9nZXRfZHJ2 ZGF0YShkZXYpOworCWludCBpOworCisJZm9yIChpID0gMCA7IGkgPCBQSFlfQ09VTlQgOyArK2kp CisJCWlmIChwcml2LT5waHlzW2ldKQorCQkJcGh5X2V4aXQocHJpdi0+cGh5c1tpXSk7CisKKwly ZXNldF9jb250cm9sX2Fzc2VydChwcml2LT5yZXNldCk7CisKKwlyZXR1cm4gMDsKK30KKworc3Rh dGljIGludCBfX21heWJlX3VudXNlZCBkd2MzX21lc29uX2cxMmFfcmVzdW1lKHN0cnVjdCBkZXZp Y2UgKmRldikKK3sKKwlzdHJ1Y3QgZHdjM19tZXNvbl9nMTJhICpwcml2ID0gZGV2X2dldF9kcnZk YXRhKGRldik7CisJaW50IGksIHJldDsKKworCXJlc2V0X2NvbnRyb2xfZGVhc3NlcnQocHJpdi0+ cmVzZXQpOworCisJZHdjM19tZXNvbl9nMTJhX3VzYl9pbml0KHByaXYpOworCisJLyogSW5pdCBQ SFlzICovCisJZm9yIChpID0gMCA7IGkgPCBQSFlfQ09VTlQgOyArK2kpIHsKKwkJaWYgKHByaXYt PnBoeXNbaV0pIHsKKwkJCXJldCA9IHBoeV9pbml0KHByaXYtPnBoeXNbaV0pOworCQkJaWYgKHJl dCkKKwkJCQlyZXR1cm4gcmV0OworCQl9CisJfQorCisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyBj b25zdCBzdHJ1Y3QgZGV2X3BtX29wcyBkd2MzX21lc29uX2cxMmFfZGV2X3BtX29wcyA9IHsKKwlT RVRfU1lTVEVNX1NMRUVQX1BNX09QUyhkd2MzX21lc29uX2cxMmFfc3VzcGVuZCwgZHdjM19tZXNv bl9nMTJhX3Jlc3VtZSkKKwlTRVRfUlVOVElNRV9QTV9PUFMoZHdjM19tZXNvbl9nMTJhX3J1bnRp bWVfc3VzcGVuZCwKKwkJCWR3YzNfbWVzb25fZzEyYV9ydW50aW1lX3Jlc3VtZSwgTlVMTCkKK307 CisKK3N0YXRpYyBjb25zdCBzdHJ1Y3Qgb2ZfZGV2aWNlX2lkIGR3YzNfbWVzb25fZzEyYV9tYXRj aFtdID0geworCXsgLmNvbXBhdGlibGUgPSAiYW1sb2dpYyxtZXNvbi1nMTJhLXVzYi1jdHJsIiB9 LAorCXsgLyogU2VudGluZWwgKi8gfQorfTsKK01PRFVMRV9ERVZJQ0VfVEFCTEUob2YsIGR3YzNf bWVzb25fZzEyYV9tYXRjaCk7CisKK3N0YXRpYyBzdHJ1Y3QgcGxhdGZvcm1fZHJpdmVyIGR3YzNf bWVzb25fZzEyYV9kcml2ZXIgPSB7CisJLnByb2JlCQk9IGR3YzNfbWVzb25fZzEyYV9wcm9iZSwK KwkucmVtb3ZlCQk9IGR3YzNfbWVzb25fZzEyYV9yZW1vdmUsCisJLmRyaXZlcgkJPSB7CisJCS5u YW1lCT0gImR3YzMtbWVzb24tZzEyYSIsCisJCS5vZl9tYXRjaF90YWJsZSA9IGR3YzNfbWVzb25f ZzEyYV9tYXRjaCwKKwkJLnBtCT0gJmR3YzNfbWVzb25fZzEyYV9kZXZfcG1fb3BzLAorCX0sCit9 OworCittb2R1bGVfcGxhdGZvcm1fZHJpdmVyKGR3YzNfbWVzb25fZzEyYV9kcml2ZXIpOworTU9E VUxFX0xJQ0VOU0UoIkdQTCB2MiIpOworTU9EVUxFX0RFU0NSSVBUSU9OKCJBbWxvZ2ljIE1lc29u IEcxMkEgVVNCIEdsdWUgTGF5ZXIiKTsKK01PRFVMRV9BVVRIT1IoIk5laWwgQXJtc3Ryb25nIDxu YXJtc3Ryb25nQGJheWxpYnJlLmNvbT4iKTsK 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 1F373C43381 for ; Mon, 4 Mar 2019 10:40:51 +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 E21A820663 for ; Mon, 4 Mar 2019 10:40:50 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="MfvZb/Aj"; 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="gmWBZCo4" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org E21A820663 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=ZoIH8hmRLT94LQ7FIUfdFAxmtQRlb6Y0DOpFkzh0BvE=; b=MfvZb/Aj8+zwr0 F9UDP3GeEkUpdXSgX5aYKCBFGuUc1ethRD0NLzf2BmrhEzDrC3DKHe+9O8BYjr6uO5ig0FV1xs62u hVGnZxOw8uIc9hbhv9nsqXAX3YMhETj2nZZgsZkUpDJLZ6thwoYdAtxo/Pj6jwy+mxJ7T1MyrMcV8 UInjPZ2ZI7Zim/qJuOiRsA1acvnascbq4BZvRkUDaS5nlX0dx3WNrABYK0p/JYveGw8p6uE61zHkc 9wunRSwwYxmnzEMhmBnK4U/VXcdxw4d8PxwXA+0qyU/RaolXVizxAbP7Ignh+Mn1iWLL8ZkntaUMT 6i3mhPiZNUeT8rHU5sFw==; 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 1h0l1d-0005Xz-F5; Mon, 04 Mar 2019 10:40:49 +0000 Received: from mail-wm1-x342.google.com ([2a00:1450:4864:20::342]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1h0kzs-000233-JB for linux-arm-kernel@lists.infradead.org; Mon, 04 Mar 2019 10:39:15 +0000 Received: by mail-wm1-x342.google.com with SMTP id x7so4078136wmj.0 for ; Mon, 04 Mar 2019 02:39:00 -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=ml5vlyaCTJkDRI3uHR4Qlz9TvSRkxPULUVun9aFSzZU=; b=gmWBZCo4JqDDaoPQIlK3lQ9amN1ELcwxPHSeQg3IA9HZ8UiKI/HqKYRXJ+WTvc2lBM u1Lbf9ppDvEGzNRky24v3+mjTmYKorQC8+b8zLeBU+llqe3u0XDCi6kcorFT0YJ81eDV WrBcv8SA8j8r6FOBtyYz75XfrFCB+E7juLcW9bv4pJbe07H2yE7gIt79BmcMsOj2E72T taCq7VNx7G+AiRgCC6lcN7W9k+BXB4IU2x2liIL3NGF9/baUG9LhApkuSt/Zi6zHuSgN HrIbkAdNe1rIWM7c32AOtdA4PoWhhySzVakANPxduRX1rhat8v9qmYkVSr1pn0jzLllf 6v3Q== 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=ml5vlyaCTJkDRI3uHR4Qlz9TvSRkxPULUVun9aFSzZU=; b=pJmnPsQF4hf49txEmGTwrFsMVmqm9qH40VzvwU4x/bB4JHUEBkyMFtkkbdwej/xT4/ dVR4QriJkyQpmgJlZYGK2t6cVM4zRuuRCXDaDKUkR7VWeti8jedv18XFEla5QkffYTLl e0ZXHObLosK3A5inEBcrwlkxXLqZb/adUswKAEW/qAf/VjBc2uXW3F58f2M7eCrYCws1 oGYNCx0SgJs1+cFwe8G5ouJc7Bwo67NrLG4tYPoy9FScm13Grbineum7d1ZCrLVIfFRR pqkgXfeuHGaDK7av8U8avXmuwAcYcsKaUURBTWjFOvHWtnMx73omjl6l44oFYZWjvV2/ 4f2A== X-Gm-Message-State: APjAAAXotDDoscanHv/Xt32Gt0mKAHL8frtNELLxGhrTN2YUArOJdXB9 Nb+i+i4N8DN+FlIsI3U46DFxqA== X-Google-Smtp-Source: AHgI3Iax/u9i0KQCYJyr3mdF7C2SUkZ9vB1RjaHe49SMCnUVz0fvHxpGTMIwN1K1lULoRz8w+kDWzQ== X-Received: by 2002:a1c:ab88:: with SMTP id u130mr10640966wme.148.1551695938936; Mon, 04 Mar 2019 02:38:58 -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 g24sm5505676wmh.45.2019.03.04.02.38.57 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 04 Mar 2019 02:38:58 -0800 (PST) From: Neil Armstrong To: gregkh@linuxfoundation.org, hminas@synopsys.com, balbi@kernel.org, kishon@ti.com Subject: [PATCH v2 8/8] usb: dwc3: Add Amlogic G12A DWC3 glue Date: Mon, 4 Mar 2019 11:38:46 +0100 Message-Id: <20190304103846.2060-9-narmstrong@baylibre.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190304103846.2060-1-narmstrong@baylibre.com> References: <20190304103846.2060-1-narmstrong@baylibre.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190304_023901_292754_BE02E323 X-CRM114-Status: GOOD ( 19.51 ) 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. 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 | 10 + drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-meson-g12a.c | 601 +++++++++++++++++++++++++++++ 3 files changed, 612 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..21ce7368d325 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -93,6 +93,16 @@ 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 + select USB_ROLE_SWITCH + 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..75942614a034 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -0,0 +1,601 @@ +// 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 + +/* USB2 Ports Control Registers */ + +#define U2P_REG_SIZE 0x20 + +#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) + +/* USB Glue Control Registers */ + +#define USB_R0 0x80 + #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 0x84 + #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 0x88 + #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 0x8c + #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 0x90 + #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 0x94 + #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) + +enum { + USB2_HOST_PHY = 0, + USB2_OTG_PHY, + USB3_HOST_PHY, + PHY_COUNT, +}; + +static const char *phy_names[PHY_COUNT] = { + "usb2-phy0", "usb2-phy1", "usb3-phy0", +}; + +struct dwc3_meson_g12a { + struct device *dev; + struct regmap *regmap; + struct clk *clk; + struct reset_control *reset; + struct phy *phys[PHY_COUNT]; + enum usb_dr_mode otg_mode; + enum phy_mode otg_phy_mode; + unsigned int usb2_ports; + unsigned int usb3_ports; + struct regulator *vbus; + struct usb_role_switch_desc switch_desc; + struct usb_role_switch *role_switch; +}; + +static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv, + int i, enum phy_mode mode) +{ + if (mode == PHY_MODE_USB_HOST) + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, + U2P_R0_HOST_DEVICE); + else + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, 0); +} + +static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv) +{ + int i; + + if (priv->otg_mode == USB_DR_MODE_PERIPHERAL) + priv->otg_phy_mode = PHY_MODE_USB_DEVICE; + else + priv->otg_phy_mode = PHY_MODE_USB_HOST; + + for (i = 0 ; i < USB3_HOST_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 (i == USB2_OTG_PHY) { + regmap_update_bits(priv->regmap, + U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS, + U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS); + + dwc3_meson_g12a_usb2_set_mode(priv, i, + priv->otg_phy_mode); + } else + dwc3_meson_g12a_usb2_set_mode(priv, i, + 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_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_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_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_R1, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT); + + regmap_update_bits(priv->regmap, 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) +{ + if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) { + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, USB_R0_U2D_ACT); + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0); + regmap_update_bits(priv->regmap, USB_R4, + USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0); + } else { + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, 0); + regmap_update_bits(priv->regmap, 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_R1, + USB_R1_U3H_FLADJ_30MHZ_REG_MASK, + FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20)); + + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_0, + USB_R5_ID_DIG_EN_0); + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_1, + USB_R5_ID_DIG_EN_1); + regmap_update_bits(priv->regmap, 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); + + 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_R5, +}; + +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv) +{ + int i; + + for (i = 0 ; i < PHY_COUNT ; ++i) { + priv->phys[i] = devm_phy_optional_get(priv->dev, phy_names[i]); + if (!priv->phys[i]) + continue; + + if (IS_ERR(priv->phys[i])) + return PTR_ERR(priv->phys[i]); + + if (i == USB3_HOST_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; +} + +static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv) +{ + u32 reg; + + regmap_read(priv->regmap, USB_R5, ®); + + if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG)) + return PHY_MODE_USB_DEVICE; + + return PHY_MODE_USB_HOST; +} + +static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv, + enum phy_mode mode) +{ + int ret; + + if (!priv->phys[USB2_OTG_PHY]) + return -EINVAL; + + if (mode == PHY_MODE_USB_HOST) + dev_info(priv->dev, "switching to Host Mode\n"); + else + dev_info(priv->dev, "switching to Device Mode\n"); + + if (priv->vbus) { + if (mode == PHY_MODE_USB_DEVICE) + ret = regulator_disable(priv->vbus); + else + ret = regulator_enable(priv->vbus); + if (ret) + return ret; + } + + priv->otg_phy_mode = mode; + + dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode); + + dwc3_meson_g12a_usb_init_mode(priv); + + return phy_set_mode(priv->phys[USB2_OTG_PHY], mode); +} + +static int dwc3_meson_g12a_role_set(struct device *dev, enum usb_role role) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + enum phy_mode mode; + + if (role == USB_ROLE_NONE) + return 0; + + mode = role == USB_ROLE_HOST ? PHY_MODE_USB_HOST : PHY_MODE_USB_DEVICE; + + if (mode == priv->otg_phy_mode) + return 0; + + return dwc3_meson_g12a_otg_mode_set(priv, mode); +} + +static enum usb_role dwc3_meson_g12a_role_get(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + + return priv->otg_phy_mode == PHY_MODE_USB_HOST ? + USB_ROLE_HOST : USB_ROLE_DEVICE; +} + +static struct device *dwc3_meson_g12_find_child(struct device *dev, + const char *compatible) +{ + struct platform_device *pdev; + struct device_node *np; + + np = of_find_compatible_node(dev->of_node, NULL, compatible); + if (!np) + return NULL; + + pdev = of_find_device_by_node(np); + of_node_put(np); + if (!pdev) + return NULL; + + return &pdev->dev; +} + +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; + enum phy_mode otg_id; + 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->vbus = devm_regulator_get_optional(dev, "vbus"); + if (IS_ERR(priv->vbus)) { + if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) + return PTR_ERR(priv->vbus); + priv->vbus = NULL; + } + + priv->clk = devm_clk_get(dev, NULL); + 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, NULL); + 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; + + ret = regulator_enable(priv->vbus); + if (ret) + return ret; + + /* Get dr_mode */ + priv->otg_mode = usb_get_dr_mode(dev); + + dwc3_meson_g12a_usb_init(priv); + + /* Set PHY Power */ + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = phy_power_on(priv->phys[i]); + if (ret) + goto err_phys_put; + } + + /* Init PHYs */ + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = phy_init(priv->phys[i]); + if (ret) + goto err_phys_power; + } + + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) { + clk_disable_unprepare(priv->clk); + clk_put(priv->clk); + + goto err_phys_exit; + } + + /* Setup OTG mode corresponding to the ID pin */ + if (priv->otg_mode == USB_DR_MODE_OTG) { + /* TOFIX Handle ID mode toggling via IRQ */ + otg_id = dwc3_meson_g12a_get_id(priv); + if (otg_id != priv->otg_phy_mode) { + if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) + dev_warn(dev, "Failed to switch OTG mode\n"); + } + } + + /* Setup role switcher */ + priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev, + "snps,dwc3"); + priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2"); + priv->switch_desc.allow_userspace_control = true; + priv->switch_desc.set = dwc3_meson_g12a_role_set; + priv->switch_desc.get = dwc3_meson_g12a_role_get; + + priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc); + if (IS_ERR(priv->role_switch)) + dev_warn(dev, "Unable to register Role Switch\n"); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + return 0; + +err_phys_exit: + for (i = 0 ; i < PHY_COUNT ; ++i) + phy_exit(priv->phys[i]); + +err_phys_power: + for (i = 0 ; i < PHY_COUNT ; ++i) + phy_power_off(priv->phys[i]); + +err_phys_put: + for (i = 0 ; i < PHY_COUNT ; ++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; + + usb_role_switch_unregister(priv->role_switch); + + of_platform_depopulate(dev); + + for (i = 0 ; i < PHY_COUNT ; ++i) { + phy_power_off(priv->phys[i]); + phy_exit(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 < PHY_COUNT ; ++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 < PHY_COUNT ; ++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 932C1C43381 for ; Mon, 4 Mar 2019 10:41:04 +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 4D91020663 for ; Mon, 4 Mar 2019 10:41:04 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="WNFwmpMY"; 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="gmWBZCo4" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 4D91020663 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=FhdL/D40c3LemqvtyuKr1VX1JeuugIrgfXKPXWj6+bE=; b=WNFwmpMYaEAj7s 4WdQo9aFj0iV8RIcKLnbKkI4A8onThg/FPMjCXPYp0e1ErMmsFuf5SGbyQWmj8xdNiINAusRmWDcT 1arixSUvpnI2LqGmiFrTqW0Ur18Fjj5/Dpf6cIiH1yoNdwiLcsnWPZwgx0oFZbaIJ1wOH1X1Ick0r EhzCmZnl/SPWki46Yqvy040UUmydAFjEHjg7eMqnrj7lbzUM8/eQBKkv7L7ulqmC/zageG8LqJags +i1OtnCm7hGev8UAbuRETQG7XWpx8mrn0TaxORQ7nqElcecZlbqGWTmpAnAitTfnCecayZEG2molO 0NAU8Trohxlohhm0OluA==; 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 1h0l1m-0005kc-ML; Mon, 04 Mar 2019 10:40:58 +0000 Received: from mail-wm1-x344.google.com ([2a00:1450:4864:20::344]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1h0kzs-000232-J7 for linux-amlogic@lists.infradead.org; Mon, 04 Mar 2019 10:39:19 +0000 Received: by mail-wm1-x344.google.com with SMTP id y15so4165044wma.0 for ; Mon, 04 Mar 2019 02:39:00 -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=ml5vlyaCTJkDRI3uHR4Qlz9TvSRkxPULUVun9aFSzZU=; b=gmWBZCo4JqDDaoPQIlK3lQ9amN1ELcwxPHSeQg3IA9HZ8UiKI/HqKYRXJ+WTvc2lBM u1Lbf9ppDvEGzNRky24v3+mjTmYKorQC8+b8zLeBU+llqe3u0XDCi6kcorFT0YJ81eDV WrBcv8SA8j8r6FOBtyYz75XfrFCB+E7juLcW9bv4pJbe07H2yE7gIt79BmcMsOj2E72T taCq7VNx7G+AiRgCC6lcN7W9k+BXB4IU2x2liIL3NGF9/baUG9LhApkuSt/Zi6zHuSgN HrIbkAdNe1rIWM7c32AOtdA4PoWhhySzVakANPxduRX1rhat8v9qmYkVSr1pn0jzLllf 6v3Q== 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=ml5vlyaCTJkDRI3uHR4Qlz9TvSRkxPULUVun9aFSzZU=; b=Q6GJvFIYlX845v70vG1NTCDzm2GM+JQbuNRLTH5J43aPLdCRJA+V+D5rmE6sra4Mhb bD3P/559xPPonjRJj4C55TAt8X6nxaUryJlyMWnpek0Wa4xfibgGwW3LN9+Kf7S3oTPm 8nO77aiDYzpZf8R2gCeIwPxHXrzsIwO6EjwbRiGn9LWMv82ZF8MhwZ1NzDdnc/YSyHJj IwVo2NCjRJHiEKcddYtyCIiWYgI7LLpnHGUH7Hi3fm0ieZIWd+zd4H6cAWLjEyNirtT0 QGXJoch9n97awFKAJeVGzlw4NqWEFzB3dJADhsim4Z6RgQRb5y3mjbXj4BWIuEC5C4Al tQ+w== X-Gm-Message-State: APjAAAWh74YTC/b778NoS5/jMjSOYXnzV5HQuUSYZl1LQcHYNduClCww WxnG1tlsLKysG/lUEtdhqsESCg== X-Google-Smtp-Source: AHgI3Iax/u9i0KQCYJyr3mdF7C2SUkZ9vB1RjaHe49SMCnUVz0fvHxpGTMIwN1K1lULoRz8w+kDWzQ== X-Received: by 2002:a1c:ab88:: with SMTP id u130mr10640966wme.148.1551695938936; Mon, 04 Mar 2019 02:38:58 -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 g24sm5505676wmh.45.2019.03.04.02.38.57 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 04 Mar 2019 02:38:58 -0800 (PST) From: Neil Armstrong To: gregkh@linuxfoundation.org, hminas@synopsys.com, balbi@kernel.org, kishon@ti.com Subject: [PATCH v2 8/8] usb: dwc3: Add Amlogic G12A DWC3 glue Date: Mon, 4 Mar 2019 11:38:46 +0100 Message-Id: <20190304103846.2060-9-narmstrong@baylibre.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190304103846.2060-1-narmstrong@baylibre.com> References: <20190304103846.2060-1-narmstrong@baylibre.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190304_023901_194102_E4F65C97 X-CRM114-Status: GOOD ( 18.05 ) 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. 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 | 10 + drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-meson-g12a.c | 601 +++++++++++++++++++++++++++++ 3 files changed, 612 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..21ce7368d325 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -93,6 +93,16 @@ 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 + select USB_ROLE_SWITCH + 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..75942614a034 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -0,0 +1,601 @@ +// 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 + +/* USB2 Ports Control Registers */ + +#define U2P_REG_SIZE 0x20 + +#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) + +/* USB Glue Control Registers */ + +#define USB_R0 0x80 + #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 0x84 + #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 0x88 + #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 0x8c + #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 0x90 + #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 0x94 + #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) + +enum { + USB2_HOST_PHY = 0, + USB2_OTG_PHY, + USB3_HOST_PHY, + PHY_COUNT, +}; + +static const char *phy_names[PHY_COUNT] = { + "usb2-phy0", "usb2-phy1", "usb3-phy0", +}; + +struct dwc3_meson_g12a { + struct device *dev; + struct regmap *regmap; + struct clk *clk; + struct reset_control *reset; + struct phy *phys[PHY_COUNT]; + enum usb_dr_mode otg_mode; + enum phy_mode otg_phy_mode; + unsigned int usb2_ports; + unsigned int usb3_ports; + struct regulator *vbus; + struct usb_role_switch_desc switch_desc; + struct usb_role_switch *role_switch; +}; + +static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv, + int i, enum phy_mode mode) +{ + if (mode == PHY_MODE_USB_HOST) + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, + U2P_R0_HOST_DEVICE); + else + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, 0); +} + +static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv) +{ + int i; + + if (priv->otg_mode == USB_DR_MODE_PERIPHERAL) + priv->otg_phy_mode = PHY_MODE_USB_DEVICE; + else + priv->otg_phy_mode = PHY_MODE_USB_HOST; + + for (i = 0 ; i < USB3_HOST_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 (i == USB2_OTG_PHY) { + regmap_update_bits(priv->regmap, + U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS, + U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS); + + dwc3_meson_g12a_usb2_set_mode(priv, i, + priv->otg_phy_mode); + } else + dwc3_meson_g12a_usb2_set_mode(priv, i, + 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_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_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_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_R1, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT); + + regmap_update_bits(priv->regmap, 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) +{ + if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) { + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, USB_R0_U2D_ACT); + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0); + regmap_update_bits(priv->regmap, USB_R4, + USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0); + } else { + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, 0); + regmap_update_bits(priv->regmap, 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_R1, + USB_R1_U3H_FLADJ_30MHZ_REG_MASK, + FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20)); + + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_0, + USB_R5_ID_DIG_EN_0); + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_1, + USB_R5_ID_DIG_EN_1); + regmap_update_bits(priv->regmap, 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); + + 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_R5, +}; + +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv) +{ + int i; + + for (i = 0 ; i < PHY_COUNT ; ++i) { + priv->phys[i] = devm_phy_optional_get(priv->dev, phy_names[i]); + if (!priv->phys[i]) + continue; + + if (IS_ERR(priv->phys[i])) + return PTR_ERR(priv->phys[i]); + + if (i == USB3_HOST_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; +} + +static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv) +{ + u32 reg; + + regmap_read(priv->regmap, USB_R5, ®); + + if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG)) + return PHY_MODE_USB_DEVICE; + + return PHY_MODE_USB_HOST; +} + +static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv, + enum phy_mode mode) +{ + int ret; + + if (!priv->phys[USB2_OTG_PHY]) + return -EINVAL; + + if (mode == PHY_MODE_USB_HOST) + dev_info(priv->dev, "switching to Host Mode\n"); + else + dev_info(priv->dev, "switching to Device Mode\n"); + + if (priv->vbus) { + if (mode == PHY_MODE_USB_DEVICE) + ret = regulator_disable(priv->vbus); + else + ret = regulator_enable(priv->vbus); + if (ret) + return ret; + } + + priv->otg_phy_mode = mode; + + dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode); + + dwc3_meson_g12a_usb_init_mode(priv); + + return phy_set_mode(priv->phys[USB2_OTG_PHY], mode); +} + +static int dwc3_meson_g12a_role_set(struct device *dev, enum usb_role role) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + enum phy_mode mode; + + if (role == USB_ROLE_NONE) + return 0; + + mode = role == USB_ROLE_HOST ? PHY_MODE_USB_HOST : PHY_MODE_USB_DEVICE; + + if (mode == priv->otg_phy_mode) + return 0; + + return dwc3_meson_g12a_otg_mode_set(priv, mode); +} + +static enum usb_role dwc3_meson_g12a_role_get(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + + return priv->otg_phy_mode == PHY_MODE_USB_HOST ? + USB_ROLE_HOST : USB_ROLE_DEVICE; +} + +static struct device *dwc3_meson_g12_find_child(struct device *dev, + const char *compatible) +{ + struct platform_device *pdev; + struct device_node *np; + + np = of_find_compatible_node(dev->of_node, NULL, compatible); + if (!np) + return NULL; + + pdev = of_find_device_by_node(np); + of_node_put(np); + if (!pdev) + return NULL; + + return &pdev->dev; +} + +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; + enum phy_mode otg_id; + 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->vbus = devm_regulator_get_optional(dev, "vbus"); + if (IS_ERR(priv->vbus)) { + if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) + return PTR_ERR(priv->vbus); + priv->vbus = NULL; + } + + priv->clk = devm_clk_get(dev, NULL); + 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, NULL); + 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; + + ret = regulator_enable(priv->vbus); + if (ret) + return ret; + + /* Get dr_mode */ + priv->otg_mode = usb_get_dr_mode(dev); + + dwc3_meson_g12a_usb_init(priv); + + /* Set PHY Power */ + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = phy_power_on(priv->phys[i]); + if (ret) + goto err_phys_put; + } + + /* Init PHYs */ + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = phy_init(priv->phys[i]); + if (ret) + goto err_phys_power; + } + + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) { + clk_disable_unprepare(priv->clk); + clk_put(priv->clk); + + goto err_phys_exit; + } + + /* Setup OTG mode corresponding to the ID pin */ + if (priv->otg_mode == USB_DR_MODE_OTG) { + /* TOFIX Handle ID mode toggling via IRQ */ + otg_id = dwc3_meson_g12a_get_id(priv); + if (otg_id != priv->otg_phy_mode) { + if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) + dev_warn(dev, "Failed to switch OTG mode\n"); + } + } + + /* Setup role switcher */ + priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev, + "snps,dwc3"); + priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2"); + priv->switch_desc.allow_userspace_control = true; + priv->switch_desc.set = dwc3_meson_g12a_role_set; + priv->switch_desc.get = dwc3_meson_g12a_role_get; + + priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc); + if (IS_ERR(priv->role_switch)) + dev_warn(dev, "Unable to register Role Switch\n"); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + return 0; + +err_phys_exit: + for (i = 0 ; i < PHY_COUNT ; ++i) + phy_exit(priv->phys[i]); + +err_phys_power: + for (i = 0 ; i < PHY_COUNT ; ++i) + phy_power_off(priv->phys[i]); + +err_phys_put: + for (i = 0 ; i < PHY_COUNT ; ++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; + + usb_role_switch_unregister(priv->role_switch); + + of_platform_depopulate(dev); + + for (i = 0 ; i < PHY_COUNT ; ++i) { + phy_power_off(priv->phys[i]); + phy_exit(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 < PHY_COUNT ; ++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 < PHY_COUNT ; ++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