From patchwork Fri Nov 18 13:28:26 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kishon Vijay Abraham I X-Patchwork-Id: 82903 Delivered-To: patch@linaro.org Received: by 10.140.97.165 with SMTP id m34csp78093qge; Fri, 18 Nov 2016 05:32:56 -0800 (PST) X-Received: by 10.37.1.198 with SMTP id 189mr4372771ybb.20.1479475976485; Fri, 18 Nov 2016 05:32:56 -0800 (PST) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id m70si1692080ywd.129.2016.11.18.05.32.55; Fri, 18 Nov 2016 05:32:56 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE dis=NONE) header.from=ti.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753997AbcKRNc0 (ORCPT + 26 others); Fri, 18 Nov 2016 08:32:26 -0500 Received: from lelnx194.ext.ti.com ([198.47.27.80]:53673 "EHLO lelnx194.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753033AbcKRN2v (ORCPT ); Fri, 18 Nov 2016 08:28:51 -0500 Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by lelnx194.ext.ti.com (8.15.1/8.15.1) with ESMTP id uAIDSorD018375; Fri, 18 Nov 2016 07:28:50 -0600 Received: from DFLE73.ent.ti.com (dfle73.ent.ti.com [128.247.5.110]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id uAIDSosU029976; Fri, 18 Nov 2016 07:28:50 -0600 Received: from dflp32.itg.ti.com (10.64.6.15) by DFLE73.ent.ti.com (128.247.5.110) with Microsoft SMTP Server id 14.3.294.0; Fri, 18 Nov 2016 07:28:50 -0600 Received: from a0393678ub.india.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by dflp32.itg.ti.com (8.14.3/8.13.8) with ESMTP id uAIDShMk020893; Fri, 18 Nov 2016 07:28:49 -0600 From: Kishon Vijay Abraham I To: CC: , Subject: [PATCH 03/20] phy: meson: add USB2 PHY support for Meson8b and GXBB Date: Fri, 18 Nov 2016 18:58:26 +0530 Message-ID: <1479475723-4857-4-git-send-email-kishon@ti.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1479475723-4857-1-git-send-email-kishon@ti.com> References: <1479475723-4857-1-git-send-email-kishon@ti.com> MIME-Version: 1.0 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Martin Blumenstingl This is a new driver for the USB PHY found in Meson8b and GXBB SoCs. Signed-off-by: Martin Blumenstingl Signed-off-by: Jerome Brunet Tested-by: Kevin Hilman Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 13 ++ drivers/phy/Makefile | 1 + drivers/phy/phy-meson8b-usb2.c | 284 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 drivers/phy/phy-meson8b-usb2.c -- 1.7.9.5 diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index fe00f91..728e03f 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -489,4 +489,17 @@ config PHY_NS2_PCIE help Enable this to support the Broadcom Northstar2 PCIe PHY. If unsure, say N. + +config PHY_MESON8B_USB2 + tristate "Meson8b and GXBB USB2 PHY driver" + default ARCH_MESON + depends on OF && (ARCH_MESON || COMPILE_TEST) + depends on USB_SUPPORT + select USB_COMMON + select GENERIC_PHY + help + Enable this to support the Meson USB2 PHYs found in Meson8b + and GXBB SoCs. + If unsure, say N. + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index a534cf5..0c7fdae 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -60,3 +60,4 @@ obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.o +obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o diff --git a/drivers/phy/phy-meson8b-usb2.c b/drivers/phy/phy-meson8b-usb2.c new file mode 100644 index 0000000..73bf632 --- /dev/null +++ b/drivers/phy/phy-meson8b-usb2.c @@ -0,0 +1,284 @@ +/* + * Meson8b and GXBB USB2 PHY driver + * + * Copyright (C) 2016 Martin Blumenstingl + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_CONFIG 0x00 + #define REG_CONFIG_CLK_EN BIT(0) + #define REG_CONFIG_CLK_SEL_MASK GENMASK(3, 1) + #define REG_CONFIG_CLK_DIV_MASK GENMASK(10, 4) + #define REG_CONFIG_CLK_32k_ALTSEL BIT(15) + #define REG_CONFIG_TEST_TRIG BIT(31) + +#define REG_CTRL 0x04 + #define REG_CTRL_SOFT_PRST BIT(0) + #define REG_CTRL_SOFT_HRESET BIT(1) + #define REG_CTRL_SS_SCALEDOWN_MODE_MASK GENMASK(3, 2) + #define REG_CTRL_CLK_DET_RST BIT(4) + #define REG_CTRL_INTR_SEL BIT(5) + #define REG_CTRL_CLK_DETECTED BIT(8) + #define REG_CTRL_SOF_SENT_RCVD_TGL BIT(9) + #define REG_CTRL_SOF_TOGGLE_OUT BIT(10) + #define REG_CTRL_POWER_ON_RESET BIT(15) + #define REG_CTRL_SLEEPM BIT(16) + #define REG_CTRL_TX_BITSTUFF_ENN_H BIT(17) + #define REG_CTRL_TX_BITSTUFF_ENN BIT(18) + #define REG_CTRL_COMMON_ON BIT(19) + #define REG_CTRL_REF_CLK_SEL_MASK GENMASK(21, 20) + #define REG_CTRL_REF_CLK_SEL_SHIFT 20 + #define REG_CTRL_FSEL_MASK GENMASK(24, 22) + #define REG_CTRL_FSEL_SHIFT 22 + #define REG_CTRL_PORT_RESET BIT(25) + #define REG_CTRL_THREAD_ID_MASK GENMASK(31, 26) + +#define REG_ENDP_INTR 0x08 + +/* bits [31:26], [24:21] and [15:3] seem to be read-only */ +#define REG_ADP_BC 0x0c + #define REG_ADP_BC_VBUS_VLD_EXT_SEL BIT(0) + #define REG_ADP_BC_VBUS_VLD_EXT BIT(1) + #define REG_ADP_BC_OTG_DISABLE BIT(2) + #define REG_ADP_BC_ID_PULLUP BIT(3) + #define REG_ADP_BC_DRV_VBUS BIT(4) + #define REG_ADP_BC_ADP_PRB_EN BIT(5) + #define REG_ADP_BC_ADP_DISCHARGE BIT(6) + #define REG_ADP_BC_ADP_CHARGE BIT(7) + #define REG_ADP_BC_SESS_END BIT(8) + #define REG_ADP_BC_DEVICE_SESS_VLD BIT(9) + #define REG_ADP_BC_B_VALID BIT(10) + #define REG_ADP_BC_A_VALID BIT(11) + #define REG_ADP_BC_ID_DIG BIT(12) + #define REG_ADP_BC_VBUS_VALID BIT(13) + #define REG_ADP_BC_ADP_PROBE BIT(14) + #define REG_ADP_BC_ADP_SENSE BIT(15) + #define REG_ADP_BC_ACA_ENABLE BIT(16) + #define REG_ADP_BC_DCD_ENABLE BIT(17) + #define REG_ADP_BC_VDAT_DET_EN_B BIT(18) + #define REG_ADP_BC_VDAT_SRC_EN_B BIT(19) + #define REG_ADP_BC_CHARGE_SEL BIT(20) + #define REG_ADP_BC_CHARGE_DETECT BIT(21) + #define REG_ADP_BC_ACA_PIN_RANGE_C BIT(22) + #define REG_ADP_BC_ACA_PIN_RANGE_B BIT(23) + #define REG_ADP_BC_ACA_PIN_RANGE_A BIT(24) + #define REG_ADP_BC_ACA_PIN_GND BIT(25) + #define REG_ADP_BC_ACA_PIN_FLOAT BIT(26) + +#define REG_DBG_UART 0x14 + +#define REG_TEST 0x18 + #define REG_TEST_DATA_IN_MASK GENMASK(3, 0) + #define REG_TEST_EN_MASK GENMASK(7, 4) + #define REG_TEST_ADDR_MASK GENMASK(11, 8) + #define REG_TEST_DATA_OUT_SEL BIT(12) + #define REG_TEST_CLK BIT(13) + #define REG_TEST_VA_TEST_EN_B_MASK GENMASK(15, 14) + #define REG_TEST_DATA_OUT_MASK GENMASK(19, 16) + #define REG_TEST_DISABLE_ID_PULLUP BIT(20) + +#define REG_TUNE 0x1c + #define REG_TUNE_TX_RES_TUNE_MASK GENMASK(1, 0) + #define REG_TUNE_TX_HSXV_TUNE_MASK GENMASK(3, 2) + #define REG_TUNE_TX_VREF_TUNE_MASK GENMASK(7, 4) + #define REG_TUNE_TX_RISE_TUNE_MASK GENMASK(9, 8) + #define REG_TUNE_TX_PREEMP_PULSE_TUNE BIT(10) + #define REG_TUNE_TX_PREEMP_AMP_TUNE_MASK GENMASK(12, 11) + #define REG_TUNE_TX_FSLS_TUNE_MASK GENMASK(16, 13) + #define REG_TUNE_SQRX_TUNE_MASK GENMASK(19, 17) + #define REG_TUNE_OTG_TUNE GENMASK(22, 20) + #define REG_TUNE_COMP_DIS_TUNE GENMASK(25, 23) + #define REG_TUNE_HOST_DM_PULLDOWN BIT(26) + #define REG_TUNE_HOST_DP_PULLDOWN BIT(27) + +#define RESET_COMPLETE_TIME 500 +#define ACA_ENABLE_COMPLETE_TIME 50 + +struct phy_meson8b_usb2_priv { + void __iomem *regs; + enum usb_dr_mode dr_mode; + struct clk *clk_usb_general; + struct clk *clk_usb; + struct reset_control *reset; +}; + +static u32 phy_meson8b_usb2_read(struct phy_meson8b_usb2_priv *phy_priv, + u32 reg) +{ + return readl(phy_priv->regs + reg); +} + +static void phy_meson8b_usb2_mask_bits(struct phy_meson8b_usb2_priv *phy_priv, + u32 reg, u32 mask, u32 value) +{ + u32 data; + + data = phy_meson8b_usb2_read(phy_priv, reg); + data &= ~mask; + data |= (value & mask); + + writel(data, phy_priv->regs + reg); +} + +static int phy_meson8b_usb2_power_on(struct phy *phy) +{ + struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); + int ret; + + if (!IS_ERR_OR_NULL(priv->reset)) { + ret = reset_control_reset(priv->reset); + if (ret) { + dev_err(&phy->dev, "Failed to trigger USB reset\n"); + return ret; + } + } + + ret = clk_prepare_enable(priv->clk_usb_general); + if (ret) { + dev_err(&phy->dev, "Failed to enable USB general clock\n"); + return ret; + } + + ret = clk_prepare_enable(priv->clk_usb); + if (ret) { + dev_err(&phy->dev, "Failed to enable USB DDR clock\n"); + return ret; + } + + phy_meson8b_usb2_mask_bits(priv, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL, + REG_CONFIG_CLK_32k_ALTSEL); + + phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK, + 0x2 << REG_CTRL_REF_CLK_SEL_SHIFT); + + phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_FSEL_MASK, + 0x5 << REG_CTRL_FSEL_SHIFT); + + /* reset the PHY */ + phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, + REG_CTRL_POWER_ON_RESET); + udelay(RESET_COMPLETE_TIME); + phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0); + udelay(RESET_COMPLETE_TIME); + + phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT, + REG_CTRL_SOF_TOGGLE_OUT); + + if (priv->dr_mode == USB_DR_MODE_HOST) { + phy_meson8b_usb2_mask_bits(priv, REG_ADP_BC, + REG_ADP_BC_ACA_ENABLE, + REG_ADP_BC_ACA_ENABLE); + + udelay(ACA_ENABLE_COMPLETE_TIME); + + if (phy_meson8b_usb2_read(priv, REG_ADP_BC) & + REG_ADP_BC_ACA_PIN_FLOAT) { + dev_warn(&phy->dev, "USB ID detect failed!\n"); + return -EINVAL; + } + } + + return 0; +} + +static int phy_meson8b_usb2_power_off(struct phy *phy) +{ + struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); + + clk_disable_unprepare(priv->clk_usb); + clk_disable_unprepare(priv->clk_usb_general); + + return 0; +} + +static const struct phy_ops phy_meson8b_usb2_ops = { + .power_on = phy_meson8b_usb2_power_on, + .power_off = phy_meson8b_usb2_power_off, + .owner = THIS_MODULE, +}; + +static int phy_meson8b_usb2_probe(struct platform_device *pdev) +{ + struct phy_meson8b_usb2_priv *priv; + struct resource *res; + struct phy *phy; + struct phy_provider *phy_provider; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + priv->clk_usb_general = devm_clk_get(&pdev->dev, "usb_general"); + if (IS_ERR(priv->clk_usb_general)) + return PTR_ERR(priv->clk_usb_general); + + priv->clk_usb = devm_clk_get(&pdev->dev, "usb"); + if (IS_ERR(priv->clk_usb)) + return PTR_ERR(priv->clk_usb); + + priv->reset = devm_reset_control_get_optional_exclusive(&pdev->dev, + NULL); + if (PTR_ERR(priv->reset) == -EPROBE_DEFER) + return PTR_ERR(priv->reset); + + priv->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1); + if (priv->dr_mode == USB_DR_MODE_UNKNOWN) { + dev_err(&pdev->dev, + "missing dual role configuration of the controller\n"); + return -EINVAL; + } + + phy = devm_phy_create(&pdev->dev, NULL, &phy_meson8b_usb2_ops); + if (IS_ERR(phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, priv); + + phy_provider = + devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id phy_meson8b_usb2_of_match[] = { + { .compatible = "amlogic,meson8b-usb2-phy", }, + { .compatible = "amlogic,meson-gxbb-usb2-phy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, phy_meson8b_usb2_of_match); + +static struct platform_driver phy_meson8b_usb2_driver = { + .probe = phy_meson8b_usb2_probe, + .driver = { + .name = "phy-meson-usb2", + .of_match_table = phy_meson8b_usb2_of_match, + }, +}; +module_platform_driver(phy_meson8b_usb2_driver); + +MODULE_AUTHOR("Martin Blumenstingl "); +MODULE_DESCRIPTION("Meson8b and GXBB USB2 PHY driver"); +MODULE_LICENSE("GPL");