Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.19 143 lines 4.0 kB view raw
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 24struct ma35_usb_phy { 25 struct clk *clk; 26 struct device *dev; 27 struct regmap *sysreg; 28}; 29 30static 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 76static 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 84static 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 90static 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 126static const struct of_device_id ma35_usb_phy_of_match[] = { 127 { .compatible = "nuvoton,ma35d1-usb2-phy", }, 128 { }, 129}; 130MODULE_DEVICE_TABLE(of, ma35_usb_phy_of_match); 131 132static 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}; 139module_platform_driver(ma35_usb_phy_driver); 140 141MODULE_DESCRIPTION("Nuvoton ma35d1 USB2.0 PHY driver"); 142MODULE_AUTHOR("Hui-Ping Chen <hpchen0nvt@gmail.com>"); 143MODULE_LICENSE("GPL");