phy: Add USB PHY driver for the MT76x8 (7628/7688) SoC
authorStefan Roese <sr@denx.de>
Fri, 5 Apr 2019 11:44:43 +0000 (13:44 +0200)
committerDaniel Schwierzeck <daniel.schwierzeck@gmail.com>
Fri, 12 Apr 2019 15:32:53 +0000 (17:32 +0200)
This driver is derived from this Linux driver:
linux/drivers/phy/ralink/phy-ralink-usb.c

The driver sets up power and host mode, but also needs to configure PHY
registers for the MT7628 and MT7688.

I removed the reset controller handling for the USB host and device, as
it does not seem to be necessary right now. The soft reset bits for both
devices are enabled by default and testing has shown (with hackish
reset handling added), that USB related commands work identical with
or without the reset handling.

Please note that the resulting USB support is tested only very minimal.
I was able to detect one of my 3 currently available USB sticks.
Perhaps some further work is needed to fully support the EHCI controller
integrated in the MT76x8 SoC.

Signed-off-by: Stefan Roese <sr@denx.de>
Cc: Marek Vasut <marex@denx.de>
Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
Reviewed-by: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
drivers/phy/Kconfig
drivers/phy/Makefile
drivers/phy/mt76x8-usb-phy.c [new file with mode: 0644]

index 32bbf41dd1f5972e6aaa8c25d36d151e14f92f3a..102fb91fffd07cc0f65a8aef150247dae6459a15 100644 (file)
@@ -174,4 +174,12 @@ config KEYSTONE_USB_PHY
 
          This PHY is found on some Keystone (K2) devices supporting USB.
 
+config MT76X8_USB_PHY
+       bool "MediaTek MT76x8 (7628/88) USB PHY support"
+       depends on PHY
+       help
+          Support the USB PHY in MT76x8 SoCs
+
+         This PHY is found on MT76x8 devices supporting USB.
+
 endmenu
index 099551d693082596878f52bbc592962e70429fb3..b55917bce1ae62353fb4ace629c74e790fedf808 100644 (file)
@@ -19,3 +19,4 @@ obj-$(CONFIG_MESON_GXL_USB_PHY) += meson-gxl-usb2.o meson-gxl-usb3.o
 obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o
 obj-$(CONFIG_OMAP_USB2_PHY) += omap-usb2-phy.o
 obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o
+obj-$(CONFIG_MT76X8_USB_PHY) += mt76x8-usb-phy.o
diff --git a/drivers/phy/mt76x8-usb-phy.c b/drivers/phy/mt76x8-usb-phy.c
new file mode 100644 (file)
index 0000000..268da8e
--- /dev/null
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Stefan Roese <sr@denx.de>
+ *
+ * Derived from linux/drivers/phy/ralink/phy-ralink-usb.c
+ *     Copyright (C) 2017 John Crispin <john@phrozen.org>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <generic-phy.h>
+#include <regmap.h>
+#include <reset-uclass.h>
+#include <syscon.h>
+#include <asm/io.h>
+
+#define RT_SYSC_REG_SYSCFG1            0x014
+#define RT_SYSC_REG_CLKCFG1            0x030
+#define RT_SYSC_REG_USB_PHY_CFG                0x05c
+
+#define OFS_U2_PHY_AC0                 0x800
+#define OFS_U2_PHY_AC1                 0x804
+#define OFS_U2_PHY_AC2                 0x808
+#define OFS_U2_PHY_ACR0                        0x810
+#define OFS_U2_PHY_ACR1                        0x814
+#define OFS_U2_PHY_ACR2                        0x818
+#define OFS_U2_PHY_ACR3                        0x81C
+#define OFS_U2_PHY_ACR4                        0x820
+#define OFS_U2_PHY_AMON0               0x824
+#define OFS_U2_PHY_DCR0                        0x860
+#define OFS_U2_PHY_DCR1                        0x864
+#define OFS_U2_PHY_DTM0                        0x868
+#define OFS_U2_PHY_DTM1                        0x86C
+
+#define RT_RSTCTRL_UDEV                        BIT(25)
+#define RT_RSTCTRL_UHST                        BIT(22)
+#define RT_SYSCFG1_USB0_HOST_MODE      BIT(10)
+
+#define MT7620_CLKCFG1_UPHY0_CLK_EN    BIT(25)
+#define MT7620_CLKCFG1_UPHY1_CLK_EN    BIT(22)
+#define RT_CLKCFG1_UPHY1_CLK_EN                BIT(20)
+#define RT_CLKCFG1_UPHY0_CLK_EN                BIT(18)
+
+#define USB_PHY_UTMI_8B60M             BIT(1)
+#define UDEV_WAKEUP                    BIT(0)
+
+struct mt76x8_usb_phy {
+       u32                     clk;
+       void __iomem            *base;
+       struct regmap           *sysctl;
+};
+
+static void u2_phy_w32(struct mt76x8_usb_phy *phy, u32 val, u32 reg)
+{
+       writel(val, phy->base + reg);
+}
+
+static u32 u2_phy_r32(struct mt76x8_usb_phy *phy, u32 reg)
+{
+       return readl(phy->base + reg);
+}
+
+static void mt76x8_usb_phy_init(struct mt76x8_usb_phy *phy)
+{
+       u2_phy_r32(phy, OFS_U2_PHY_AC2);
+       u2_phy_r32(phy, OFS_U2_PHY_ACR0);
+       u2_phy_r32(phy, OFS_U2_PHY_DCR0);
+
+       u2_phy_w32(phy, 0x00ffff02, OFS_U2_PHY_DCR0);
+       u2_phy_r32(phy, OFS_U2_PHY_DCR0);
+       u2_phy_w32(phy, 0x00555502, OFS_U2_PHY_DCR0);
+       u2_phy_r32(phy, OFS_U2_PHY_DCR0);
+       u2_phy_w32(phy, 0x00aaaa02, OFS_U2_PHY_DCR0);
+       u2_phy_r32(phy, OFS_U2_PHY_DCR0);
+       u2_phy_w32(phy, 0x00000402, OFS_U2_PHY_DCR0);
+       u2_phy_r32(phy, OFS_U2_PHY_DCR0);
+       u2_phy_w32(phy, 0x0048086a, OFS_U2_PHY_AC0);
+       u2_phy_w32(phy, 0x4400001c, OFS_U2_PHY_AC1);
+       u2_phy_w32(phy, 0xc0200000, OFS_U2_PHY_ACR3);
+       u2_phy_w32(phy, 0x02000000, OFS_U2_PHY_DTM0);
+}
+
+static int mt76x8_usb_phy_power_on(struct phy *_phy)
+{
+       struct mt76x8_usb_phy *phy = dev_get_priv(_phy->dev);
+       u32 t;
+
+       /* enable the phy */
+       regmap_update_bits(phy->sysctl, RT_SYSC_REG_CLKCFG1,
+                          phy->clk, phy->clk);
+
+       /* setup host mode */
+       regmap_update_bits(phy->sysctl, RT_SYSC_REG_SYSCFG1,
+                          RT_SYSCFG1_USB0_HOST_MODE,
+                          RT_SYSCFG1_USB0_HOST_MODE);
+
+       /*
+        * The SDK kernel had a delay of 100ms. however on device
+        * testing showed that 10ms is enough
+        */
+       mdelay(10);
+
+       if (phy->base)
+               mt76x8_usb_phy_init(phy);
+
+       /* print some status info */
+       regmap_read(phy->sysctl, RT_SYSC_REG_USB_PHY_CFG, &t);
+       printf("remote usb device wakeup %s\n",
+              (t & UDEV_WAKEUP) ? "enabled" : "disabled");
+       if (t & USB_PHY_UTMI_8B60M)
+               printf("UTMI 8bit 60MHz\n");
+       else
+               printf("UTMI 16bit 30MHz\n");
+
+       return 0;
+}
+
+static int mt76x8_usb_phy_power_off(struct phy *_phy)
+{
+       struct mt76x8_usb_phy *phy = dev_get_priv(_phy->dev);
+
+       /* disable the phy */
+       regmap_update_bits(phy->sysctl, RT_SYSC_REG_CLKCFG1,
+                          phy->clk, 0);
+
+       return 0;
+}
+
+static int mt76x8_usb_phy_probe(struct udevice *dev)
+{
+       struct mt76x8_usb_phy *phy = dev_get_priv(dev);
+
+       phy->sysctl = syscon_regmap_lookup_by_phandle(dev, "ralink,sysctl");
+       if (IS_ERR(phy->sysctl))
+               return PTR_ERR(phy->sysctl);
+
+       phy->base = dev_read_addr_ptr(dev);
+       if (!phy->base)
+               return -EINVAL;
+
+       return 0;
+}
+
+static struct phy_ops mt76x8_usb_phy_ops = {
+       .power_on = mt76x8_usb_phy_power_on,
+       .power_off = mt76x8_usb_phy_power_off,
+};
+
+static const struct udevice_id mt76x8_usb_phy_ids[] = {
+       { .compatible = "mediatek,mt7628-usbphy" },
+       { }
+};
+
+U_BOOT_DRIVER(mt76x8_usb_phy) = {
+       .name           = "mt76x8_usb_phy",
+       .id             = UCLASS_PHY,
+       .of_match       = mt76x8_usb_phy_ids,
+       .ops            = &mt76x8_usb_phy_ops,
+       .probe          = mt76x8_usb_phy_probe,
+       .priv_auto_alloc_size = sizeof(struct mt76x8_usb_phy),
+};