add BMC NPCM750 phy control driver

Signed-off-by: Jim Liu <jjl...@nuvoton.com>
---
 drivers/phy/Kconfig        |   7 ++
 drivers/phy/Makefile       |   1 +
 drivers/phy/phy-npcm-usb.c | 215 +++++++++++++++++++++++++++++++++++++
 3 files changed, 223 insertions(+)
 create mode 100644 drivers/phy/phy-npcm-usb.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index c01d9e09b9..4a3856d3c2 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -274,6 +274,13 @@ config PHY_MTK_TPHY
          multi-ports is first version, otherwise is second veriosn,
          so you can easily distinguish them by banks layout.
 
+config PHY_NPCM_USB
+       bool "Nuvoton NPCM USB PHY support"
+       depends on PHY
+       depends on ARCH_NPCM
+       help
+         Support the USB PHY in NPCM SoCs
+
 config PHY_IMX8MQ_USB
        bool "NXP i.MX8MQ/i.MX8MP USB PHY Driver"
        depends on PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index bf9b40932f..d95439c425 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_MT7620_USB_PHY) += mt7620-usb-phy.o
 obj-$(CONFIG_MT76X8_USB_PHY) += mt76x8-usb-phy.o
 obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o
 obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o
+obj-$(CONFIG_PHY_NPCM_USB) += phy-npcm-usb.o
 obj-$(CONFIG_PHY_IMX8MQ_USB) += phy-imx8mq-usb.o
 obj-$(CONFIG_PHY_XILINX_ZYNQMP) += phy-zynqmp.o
 obj-y += cadence/
diff --git a/drivers/phy/phy-npcm-usb.c b/drivers/phy/phy-npcm-usb.c
new file mode 100644
index 0000000000..24eba66554
--- /dev/null
+++ b/drivers/phy/phy-npcm-usb.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2021 Nuvoton Technology Corp.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <generic-phy.h>
+#include <regmap.h>
+#include <reset.h>
+#include <syscon.h>
+#include <dm/device_compat.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+
+/* GCR Register Offsets */
+#define GCR_INTCR3     0x9C
+#define GCR_USB1PHYCTL 0x140
+#define GCR_USB2PHYCTL 0x144
+#define GCR_USB3PHYCTL 0x148
+
+/* USBnPHYCTL bit fields */
+#define PHYCTL_RS      BIT(28)
+
+#define USBPHY2SW      GENMASK(13, 12)
+#define USBPHY3SW      GENMASK(15, 14)
+
+#define USBPHY2SW_DEV9_PHY1    FIELD_PREP(USBPHY2SW, 0)
+#define USBPHY2SW_HOST1                FIELD_PREP(USBPHY2SW, 1)
+#define USBPHY2SW_DEV9_PHY2    FIELD_PREP(USBPHY2SW, 3)
+#define USBPHY3SW_DEV8_PHY1    FIELD_PREP(USBPHY3SW, 0)
+#define USBPHY3SW_HOST2                FIELD_PREP(USBPHY3SW, 1)
+#define USBPHY3SW_DEV8_PHY3    FIELD_PREP(USBPHY3SW, 3)
+
+enum controller_id {
+       UDC0_7,
+       UDC8,
+       UDC9,
+       USBH1,
+       USBH2,
+};
+
+enum phy_id {
+       PHY1 = 1,
+       PHY2,
+       PHY3,
+};
+
+/* Phy Switch Settings */
+#define USBDPHY1       ((PHY1 << 8) | UDC0_7)  /* Connect UDC0~7 to PHY1 */
+#define USBD8PHY1      ((PHY1 << 8) | UDC8)    /* Connect UDC8 to PHY1 */
+#define USBD9PHY1      ((PHY1 << 8) | UDC9)    /* Connect UDC9 to PHY1 */
+#define USBD9PHY2      ((PHY2 << 8) | UDC9)    /* Connect UDC9 to PHY2 */
+#define USBH1PHY2      ((PHY2 << 8) | USBH1)   /* Connect USBH1 to PHY2 */
+#define USBD8PHY3      ((PHY3 << 8) | UDC8)    /* Connect UDC8 to PHY3 */
+#define USBH2PHY3      ((PHY3 << 8) | USBH2)   /* Connect USBH2 to PHY3 */
+
+struct npcm_usbphy {
+       struct regmap *syscon;
+       u8 id;
+       u16 phy_switch; /* (phy_id << 8) | controller_id */
+};
+
+static int npcm_usb_phy_init(struct phy *phy)
+{
+       struct npcm_usbphy *priv = dev_get_priv(phy->dev);
+       struct reset_ctl reset;
+       int ret;
+
+       ret = reset_get_by_index(phy->dev, 0, &reset);
+       if (ret && ret != -ENOENT && ret != -ENOTSUPP) {
+               dev_err(phy->dev, "can't get phy reset ctrl (err %d)", ret);
+               return ret;
+       }
+
+       /* setup PHY switch */
+       switch (priv->phy_switch) {
+       case USBD8PHY1:
+               regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY3SW,
+                                  USBPHY3SW_DEV8_PHY1);
+               break;
+       case USBD8PHY3:
+               regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY3SW,
+                                  USBPHY3SW_DEV8_PHY3);
+               break;
+       case USBD9PHY1:
+               regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY2SW,
+                                  USBPHY2SW_DEV9_PHY1);
+               break;
+       case USBD9PHY2:
+               regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY2SW,
+                                  USBPHY2SW_DEV9_PHY2);
+               break;
+       case USBH1PHY2:
+               regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY2SW,
+                                  USBPHY2SW_HOST1);
+               break;
+       case USBH2PHY3:
+               regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY3SW,
+                                  USBPHY3SW_HOST2);
+               break;
+       default:
+               break;
+       }
+       /* reset phy */
+       if (reset_valid(&reset))
+               reset_assert(&reset);
+
+       /* Wait for PHY clocks to stablize for 50us or more */
+       udelay(100);
+
+       /* release phy from reset */
+       if (reset_valid(&reset))
+               reset_deassert(&reset);
+
+       /* PHY RS bit should be set after reset */
+       switch (priv->id) {
+       case PHY1:
+               regmap_update_bits(priv->syscon, GCR_USB1PHYCTL, PHYCTL_RS, 
PHYCTL_RS);
+               break;
+       case PHY2:
+               regmap_update_bits(priv->syscon, GCR_USB2PHYCTL, PHYCTL_RS, 
PHYCTL_RS);
+               break;
+       case PHY3:
+               regmap_update_bits(priv->syscon, GCR_USB3PHYCTL, PHYCTL_RS, 
PHYCTL_RS);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int npcm_usb_phy_exit(struct phy *phy)
+{
+       struct npcm_usbphy *priv = dev_get_priv(phy->dev);
+
+       /* set PHY switch to default state */
+       switch (priv->phy_switch) {
+       case USBD8PHY1:
+       case USBD8PHY3:
+               regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY3SW,
+                                  USBPHY3SW_HOST2);
+               break;
+       case USBD9PHY1:
+       case USBD9PHY2:
+               regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY2SW,
+                                  USBPHY2SW_HOST1);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int  npcm_usb_phy_xlate(struct phy *phy, struct ofnode_phandle_args 
*args)
+{
+       struct npcm_usbphy *priv = dev_get_priv(phy->dev);
+       u16 phy_switch;
+
+       if (args->args_count < 1 || args->args[0] > USBH2)
+               return -EINVAL;
+
+       phy_switch = (priv->id << 8) | args->args[0];
+       switch (phy_switch) {
+       case USBD9PHY1:
+       case USBH2PHY3:
+       case USBD8PHY3:
+               if (!IS_ENABLED(CONFIG_ARCH_NPCM8XX))
+                       return -EINVAL;
+       case USBDPHY1:
+       case USBD8PHY1:
+       case USBD9PHY2:
+       case USBH1PHY2:
+               priv->phy_switch = phy_switch;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int npcm_usb_phy_probe(struct udevice *dev)
+{
+       struct npcm_usbphy *priv = dev_get_priv(dev);
+
+       priv->syscon = syscon_regmap_lookup_by_phandle(dev->parent, "syscon");
+       if (IS_ERR(priv->syscon)) {
+               dev_err(dev, "%s: unable to get syscon\n", __func__);
+               return PTR_ERR(priv->syscon);
+       }
+       priv->id = dev_read_u32_default(dev, "reg", -1);
+
+       return 0;
+}
+
+static const struct udevice_id npcm_phy_ids[] = {
+       { .compatible = "nuvoton,npcm845-usb-phy",},
+       { .compatible = "nuvoton,npcm750-usb-phy",},
+       { }
+};
+
+static struct phy_ops npcm_phy_ops = {
+       .init = npcm_usb_phy_init,
+       .exit = npcm_usb_phy_exit,
+       .of_xlate = npcm_usb_phy_xlate,
+};
+
+U_BOOT_DRIVER(npcm_phy) = {
+       .name   = "npcm-usb-phy",
+       .id     = UCLASS_PHY,
+       .of_match = npcm_phy_ids,
+       .ops = &npcm_phy_ops,
+       .probe          = npcm_usb_phy_probe,
+       .priv_auto      = sizeof(struct npcm_usbphy),
+};
-- 
2.17.1

Reply via email to