1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2024 Nuvoton Technology Corp. 4 */ 5 #include <linux/bitfield.h> 6 #include <linux/clk.h> 7 #include <linux/delay.h> 8 #include <linux/io.h> 9 #include <linux/kernel.h> 10 #include <linux/mfd/syscon.h> 11 #include <linux/module.h> 12 #include <linux/of.h> 13 #include <linux/phy/phy.h> 14 #include <linux/platform_device.h> 15 #include <linux/regmap.h> 16 17 /* USB PHY Miscellaneous Control Register */ 18 #define MA35_SYS_REG_USBPMISCR 0x60 19 #define PHY0POR BIT(0) /* PHY Power-On Reset Control Bit */ 20 #define PHY0SUSPEND BIT(1) /* PHY Suspend; 0: suspend, 1: operaion */ 21 #define PHY0COMN BIT(2) /* PHY Common Block Power-Down Control */ 22 #define PHY0DEVCKSTB BIT(10) /* PHY 60 MHz UTMI clock stable bit */ 23 24 struct ma35_usb_phy { 25 struct clk *clk; 26 struct device *dev; 27 struct regmap *sysreg; 28 }; 29 30 static int ma35_usb_phy_power_on(struct phy *phy) 31 { 32 struct ma35_usb_phy *p_phy = phy_get_drvdata(phy); 33 unsigned int val; 34 int ret; 35 36 ret = clk_prepare_enable(p_phy->clk); 37 if (ret < 0) { 38 dev_err(p_phy->dev, "Failed to enable PHY clock: %d\n", ret); 39 return ret; 40 } 41 42 regmap_read(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, &val); 43 if (val & PHY0SUSPEND) { 44 /* 45 * USB PHY0 is in operation mode already 46 * make sure USB PHY 60 MHz UTMI Interface Clock ready 47 */ 48 ret = regmap_read_poll_timeout(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, val, 49 val & PHY0DEVCKSTB, 10, 1000); 50 if (ret == 0) 51 return 0; 52 } 53 54 /* 55 * reset USB PHY0. 56 * wait until USB PHY0 60 MHz UTMI Interface Clock ready 57 */ 58 regmap_update_bits(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, 0x7, (PHY0POR | PHY0SUSPEND)); 59 udelay(20); 60 61 /* make USB PHY0 enter operation mode */ 62 regmap_update_bits(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, 0x7, PHY0SUSPEND); 63 64 /* make sure USB PHY 60 MHz UTMI Interface Clock ready */ 65 ret = regmap_read_poll_timeout(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, val, 66 val & PHY0DEVCKSTB, 10, 1000); 67 if (ret == -ETIMEDOUT) { 68 dev_err(p_phy->dev, "Check PHY clock, Timeout: %d\n", ret); 69 clk_disable_unprepare(p_phy->clk); 70 return ret; 71 } 72 73 return 0; 74 } 75 76 static int ma35_usb_phy_power_off(struct phy *phy) 77 { 78 struct ma35_usb_phy *p_phy = phy_get_drvdata(phy); 79 80 clk_disable_unprepare(p_phy->clk); 81 return 0; 82 } 83 84 static const struct phy_ops ma35_usb_phy_ops = { 85 .power_on = ma35_usb_phy_power_on, 86 .power_off = ma35_usb_phy_power_off, 87 .owner = THIS_MODULE, 88 }; 89 90 static int ma35_usb_phy_probe(struct platform_device *pdev) 91 { 92 struct phy_provider *provider; 93 struct ma35_usb_phy *p_phy; 94 struct phy *phy; 95 96 p_phy = devm_kzalloc(&pdev->dev, sizeof(*p_phy), GFP_KERNEL); 97 if (!p_phy) 98 return -ENOMEM; 99 100 p_phy->dev = &pdev->dev; 101 platform_set_drvdata(pdev, p_phy); 102 103 p_phy->sysreg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "nuvoton,sys"); 104 if (IS_ERR(p_phy->sysreg)) 105 return dev_err_probe(&pdev->dev, PTR_ERR(p_phy->sysreg), 106 "Failed to get SYS registers\n"); 107 108 p_phy->clk = of_clk_get(pdev->dev.of_node, 0); 109 if (IS_ERR(p_phy->clk)) 110 return dev_err_probe(&pdev->dev, PTR_ERR(p_phy->clk), 111 "failed to find usb_phy clock\n"); 112 113 phy = devm_phy_create(&pdev->dev, NULL, &ma35_usb_phy_ops); 114 if (IS_ERR(phy)) 115 return dev_err_probe(&pdev->dev, PTR_ERR(phy), "Failed to create PHY\n"); 116 117 phy_set_drvdata(phy, p_phy); 118 119 provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); 120 if (IS_ERR(provider)) 121 return dev_err_probe(&pdev->dev, PTR_ERR(provider), 122 "Failed to register PHY provider\n"); 123 return 0; 124 } 125 126 static const struct of_device_id ma35_usb_phy_of_match[] = { 127 { .compatible = "nuvoton,ma35d1-usb2-phy", }, 128 { }, 129 }; 130 MODULE_DEVICE_TABLE(of, ma35_usb_phy_of_match); 131 132 static struct platform_driver ma35_usb_phy_driver = { 133 .probe = ma35_usb_phy_probe, 134 .driver = { 135 .name = "ma35d1-usb2-phy", 136 .of_match_table = ma35_usb_phy_of_match, 137 }, 138 }; 139 module_platform_driver(ma35_usb_phy_driver); 140 141 MODULE_DESCRIPTION("Nuvoton ma35d1 USB2.0 PHY driver"); 142 MODULE_AUTHOR("Hui-Ping Chen <hpchen0nvt@gmail.com>"); 143 MODULE_LICENSE("GPL"); 144