Received: by 2002:a05:6a10:a0d1:0:0:0:0 with SMTP id j17csp2016147pxa; Sun, 16 Aug 2020 20:15:33 -0700 (PDT) X-Google-Smtp-Source: ABdhPJx097zzCTJ0VFMwhj/kYsXSEAJmKcM90mS2Z1ZMe4dw4u0QSnGcp5zL4XBYXhuIUulMmsn/ X-Received: by 2002:a05:6402:b26:: with SMTP id bo6mr13256812edb.104.1597634133706; Sun, 16 Aug 2020 20:15:33 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1597634133; cv=none; d=google.com; s=arc-20160816; b=N4/GSIZL7Pn1qWtU52qRaiG8iGrOz3aZxwU7lVRqMH/knF8YMp6VfP4hSf8LEPz1pj /RPh6tsEDpQ9UljE8eHpfGAXs6jMBr9JyihpMPmayiuzcZsrOwpizuU5jZyz9h/LP23s gcltY+e+OgspkMV/3PTAZ8kTv5dO7md8TCrFKed+BlYsPIilZQHvwxpN+k1yPPKzwB6o fXFmbtBCy2B9Z4CP7vUxMhPu51DcbQE0L8gjqmhzsRfIXdrtpeijWcpJb4j6vvQMKjW6 tItiZFmpj5Zc5nddxRyR+z8jxY8zIJmaiAAxmRTsMWFx3Fi7KdtG1ZXmO7Fw0WMbJb/8 YdIw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:ironport-sdr:ironport-sdr; bh=yx8fnoP1Rmlsbk4YpdWs8V7nPEQ0zWUDp+7n751yHEg=; b=skMG5B/Ncrw1JC4RpHUsmiXzCxBZS0fWe+lIXxPzN2ctNaCbmI9JAhKMj5cPNKLrQo 1zCZZ6+OkSmllr442O3qIofsCwZGM3xtWj5X2xp6DoCqsp+1kv8xuqEh13UE0cDmS45P ZwL7ImrR8C+SYriYsiUNKf0DNSWAJn2mSLKiIDtzqJ5SchBaFh4NunStLvYE3rH4j2m4 lKlK8qsqL0whyGLtHzLsby3LUelUSxdsgnmz9TSD9ExST4Ihvk4XhV2FlboOBPNf2xhR o+n5n4z/8C26AubhRcCaY5kmudWF/unQx3arRGMlG0WyTNK07Iv+tiYhK52a/CLK/g+j LxzQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id h11si9672581edr.606.2020.08.16.20.15.09; Sun, 16 Aug 2020 20:15:33 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727062AbgHQDNG (ORCPT + 99 others); Sun, 16 Aug 2020 23:13:06 -0400 Received: from mga02.intel.com ([134.134.136.20]:13567 "EHLO mga02.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726235AbgHQDNF (ORCPT ); Sun, 16 Aug 2020 23:13:05 -0400 IronPort-SDR: uxCdkeZvprbtp8hsiaqZxTnjPuEJhhaVd/8ab/rYe/F2CoYWcZBGzQlosvnDSe/PLMP6ESvOLC 2/IMJMOQPt4Q== X-IronPort-AV: E=McAfee;i="6000,8403,9715"; a="142457332" X-IronPort-AV: E=Sophos;i="5.76,322,1592895600"; d="scan'208";a="142457332" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga101.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 16 Aug 2020 20:13:03 -0700 IronPort-SDR: CVyman69i/zqeUxCRQhObKspAcdVTGcGGKnKx9LuAdeP4tmrou6SRGZ4/DqE4bd2BHyAp2FsRy E672iZkBwItA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.76,322,1592895600"; d="scan'208";a="326281413" Received: from sgsxdev004.isng.intel.com (HELO localhost) ([10.226.88.13]) by orsmga008.jf.intel.com with ESMTP; 16 Aug 2020 20:13:00 -0700 From: "Ramuthevar,Vadivel MuruganX" To: linux-kernel@vger.kernel.org, kishon@ti.com, vkoul@kernel.org Cc: gregkh@linuxfoundation.org, robh+dt@kernel.org, devicetree@vger.kernel.org, p.zabel@pengutronix.de, andriy.shevchenko@intel.com, balbi@kernel.org, cheol.yong.kim@intel.com, qi-ming.wu@intel.com, yin1.li@intel.com, Ramuthevar Vadivel Murugan Subject: [PATCH v8 2/2] phy: Add USB3 PHY support for Intel LGM SoC Date: Mon, 17 Aug 2020 11:12:49 +0800 Message-Id: <20200817031249.36795-3-vadivel.muruganx.ramuthevar@linux.intel.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20200817031249.36795-1-vadivel.muruganx.ramuthevar@linux.intel.com> References: <20200817031249.36795-1-vadivel.muruganx.ramuthevar@linux.intel.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Ramuthevar Vadivel Murugan Add support for USB PHY on Intel LGM SoC. Signed-off-by: Ramuthevar Vadivel Murugan Reviewed-by: Philipp Zabel --- drivers/phy/Kconfig | 11 ++ drivers/phy/Makefile | 3 +- drivers/phy/phy-lgm-usb.c | 278 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 drivers/phy/phy-lgm-usb.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index de9362c25c07..01b53f86004c 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -49,6 +49,17 @@ config PHY_XGENE help This option enables support for APM X-Gene SoC multi-purpose PHY. +config USB_LGM_PHY + tristate "INTEL Lightning Mountain USB PHY Driver" + depends on USB_SUPPORT + select USB_PHY + select REGULATOR + select REGULATOR_FIXED_VOLTAGE + help + Enable this to support Intel DWC3 PHY USB phy. This driver provides + interface to interact with USB GEN-II and USB 3.x PHY that is part + of the Intel network SOC. + source "drivers/phy/allwinner/Kconfig" source "drivers/phy/amlogic/Kconfig" source "drivers/phy/broadcom/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index c27408e4daae..90030ff299eb 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 +PDX-License-Identifier: GPL-2.0 # # Makefile for the phy drivers. # @@ -8,6 +8,7 @@ obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o +obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o obj-y += allwinner/ \ amlogic/ \ broadcom/ \ diff --git a/drivers/phy/phy-lgm-usb.c b/drivers/phy/phy-lgm-usb.c new file mode 100644 index 000000000000..1ec9ab266e08 --- /dev/null +++ b/drivers/phy/phy-lgm-usb.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel LGM USB PHY driver + * + * Copyright (C) 2020 Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CTRL1_OFFSET 0x14 +#define SRAM_EXT_LD_DONE BIT(25) +#define SRAM_INIT_DONE BIT(26) + +#define TCPC_OFFSET 0x1014 +#define TCPC_MUX_CTL GENMASK(1, 0) +#define MUX_NC 0 +#define MUX_USB 1 +#define MUX_DP 2 +#define MUX_USBDP 3 +#define TCPC_FLIPPED BIT(2) +#define TCPC_LOW_POWER_EN BIT(3) +#define TCPC_VALID BIT(4) +#define TCPC_CONN \ + (TCPC_VALID | FIELD_PREP(TCPC_MUX_CTL, MUX_USB)) +#define TCPC_DISCONN \ + (TCPC_VALID | FIELD_PREP(TCPC_MUX_CTL, MUX_NC) | TCPC_LOW_POWER_EN) + +static const char *const PHY_RESETS[] = { "phy31", "phy", }; +static const char *const CTL_RESETS[] = { "apb", "ctrl", }; + +struct tca_apb { + struct reset_control *resets[ARRAY_SIZE(PHY_RESETS)]; + struct regulator *vbus; + struct work_struct wk; + struct usb_phy phy; + + bool phy_initialized; + bool connected; +}; + +static int get_flipped(struct tca_apb *ta, bool *flipped) +{ + union extcon_property_value property; + int ret; + + ret = extcon_get_property(ta->phy.edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_TYPEC_POLARITY, &property); + if (ret) { + dev_err(ta->phy.dev, "no polarity property from extcon\n"); + return ret; + } + + *flipped = property.intval; + + return ret; +} + +static int phy_init(struct usb_phy *phy) +{ + struct tca_apb *ta = container_of(phy, struct tca_apb, phy); + void __iomem *ctrl1 = phy->io_priv + CTRL1_OFFSET; + int val, ret, i; + + if (ta->phy_initialized) + return 0; + + for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++) + reset_control_deassert(ta->resets[i]); + + ret = readl_poll_timeout(ctrl1, val, val & SRAM_INIT_DONE, 10, 10 * 1000); + if (ret) { + dev_err(ta->phy.dev, "SRAM init failed, 0x%x\n", val); + return ret; + } + + writel(readl(ctrl1) | SRAM_EXT_LD_DONE, ctrl1); + + ta->phy_initialized = true; + if (!ta->phy.edev) { + writel(TCPC_CONN, ta->phy.io_priv + TCPC_OFFSET); + return phy->set_vbus(phy, true); + } + + schedule_work(&ta->wk); + + return ret; +} + +static void phy_shutdown(struct usb_phy *phy) +{ + struct tca_apb *ta = container_of(phy, struct tca_apb, phy); + int i; + + if (!ta->phy_initialized) + return; + + ta->phy_initialized = false; + flush_work(&ta->wk); + ta->phy.set_vbus(&ta->phy, false); + + ta->connected = false; + writel(TCPC_DISCONN, ta->phy.io_priv + TCPC_OFFSET); + + for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++) + reset_control_assert(ta->resets[i]); +} + +static int phy_set_vbus(struct usb_phy *phy, int on) +{ + struct tca_apb *ta = container_of(phy, struct tca_apb, phy); + int ret; + + if (on) { + ret = regulator_enable(ta->vbus); + if (ret) + dev_err(ta->phy.dev, "regulator not enabled\n"); + } else { + ret = regulator_disable(ta->vbus); + if (ret) + dev_err(ta->phy.dev, "regulator not disabled\n"); + } + + return ret; +} + +static void tca_work(struct work_struct *work) +{ + struct tca_apb *ta = container_of(work, struct tca_apb, wk); + bool connected; + bool flipped = false; + u32 val; + int ret; + + ret = get_flipped(ta, &flipped); + connected = extcon_get_state(ta->phy.edev, EXTCON_USB_HOST) && !ret; + if (connected == ta->connected) + return; + + ta->connected = connected; + if (connected) { + val = TCPC_CONN; + if (flipped) + val |= TCPC_FLIPPED; + dev_info(ta->phy.dev, "connected%s\n", flipped ? " flipped" : ""); + } else { + val = TCPC_DISCONN; + dev_info(ta->phy.dev, "disconnected\n"); + } + + writel(val, ta->phy.io_priv + TCPC_OFFSET); + + ret = ta->phy.set_vbus(&ta->phy, connected); + if (ret) + dev_err(ta->phy.dev, "failed to set VBUS\n"); +} + +static int id_notifier(struct notifier_block *nb, unsigned long event, void *ptr) +{ + struct tca_apb *ta = container_of(nb, struct tca_apb, phy.id_nb); + + if (ta->phy_initialized) + schedule_work(&ta->wk); + + return NOTIFY_DONE; +} + +static int vbus_notifier(struct notifier_block *nb, unsigned long evnt, void *ptr) +{ + return NOTIFY_DONE; +} + +static int phy_probe(struct platform_device *pdev) +{ + struct reset_control *resets[ARRAY_SIZE(CTL_RESETS)]; + struct device *dev = &pdev->dev; + struct usb_phy *phy; + struct tca_apb *ta; + int i; + + ta = devm_kzalloc(dev, sizeof(*ta), GFP_KERNEL); + if (!ta) + return -ENOMEM; + + platform_set_drvdata(pdev, ta); + INIT_WORK(&ta->wk, tca_work); + + phy = &ta->phy; + phy->dev = dev; + phy->label = dev_name(dev); + phy->type = USB_PHY_TYPE_USB3; + phy->init = phy_init; + phy->shutdown = phy_shutdown; + phy->set_vbus = phy_set_vbus; + phy->id_nb.notifier_call = id_notifier; + phy->vbus_nb.notifier_call = vbus_notifier; + + phy->io_priv = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->io_priv)) + return PTR_ERR(phy->io_priv); + + ta->vbus = devm_regulator_get(dev, "vbus"); + if (IS_ERR(ta->vbus)) + return PTR_ERR(ta->vbus); + + for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++) { + resets[i] = devm_reset_control_get_exclusive(dev, CTL_RESETS[i]); + if (IS_ERR(resets[i])) { + dev_err(dev, "%s reset not found\n", CTL_RESETS[i]); + return PTR_ERR(resets[i]); + } + } + + for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++) { + ta->resets[i] = devm_reset_control_get_exclusive(dev, PHY_RESETS[i]); + if (IS_ERR(ta->resets[i])) { + dev_err(dev, "%s reset not found\n", PHY_RESETS[i]); + return PTR_ERR(ta->resets[i]); + } + } + + for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++) + reset_control_assert(resets[i]); + + for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++) + reset_control_assert(ta->resets[i]); + /* + * Out-of-band reset of the controller after PHY reset will cause + * controller malfunctioning, so we should use in-band controller + * reset only and leave the controller de-asserted here. + */ + for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++) + reset_control_deassert(resets[i]); + + /* Need to wait at least 20us after de-assert the controller */ + usleep_range(20, 100); + + return usb_add_phy_dev(phy); +} + +static int phy_remove(struct platform_device *pdev) +{ + struct tca_apb *ta = platform_get_drvdata(pdev); + + usb_remove_phy(&ta->phy); + + return 0; +} + +static const struct of_device_id intel_usb_phy_dt_ids[] = { + { .compatible = "intel,lgm-usb-phy" }, + { } +}; +MODULE_DEVICE_TABLE(of, intel_usb_phy_dt_ids); + +static struct platform_driver lgm_phy_driver = { + .driver = { + .name = "lgm-usb-phy", + .of_match_table = intel_usb_phy_dt_ids, + }, + .probe = phy_probe, + .remove = phy_remove, +}; + +module_platform_driver(lgm_phy_driver); + +MODULE_DESCRIPTION("Intel LGM USB PHY driver"); +MODULE_AUTHOR("Li Yin "); +MODULE_AUTHOR("Vadivel Murugan R "); +MODULE_LICENSE("GPL v2"); -- 2.11.0