From fdd08f896bcfc513a4cb7799d0094e4fabc73531 Mon Sep 17 00:00:00 2001 From: Jim Liu Date: Tue, 21 Jun 2022 17:03:38 +0800 Subject: [PATCH] phy: nuvoton: add NPCM7xx phy control driver add BMC NPCM750 phy control driver Signed-off-by: Jim Liu --- 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 c01d9e0..4a3856d 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 bf9b409..d95439c 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 0000000..24eba66 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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.7.4