1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Copyright (c) 2017 NXP. */ 3 4 #include <linux/bitfield.h> 5 #include <linux/clk.h> 6 #include <linux/delay.h> 7 #include <linux/io.h> 8 #include <linux/module.h> 9 #include <linux/of_platform.h> 10 #include <linux/phy/phy.h> 11 #include <linux/platform_device.h> 12 #include <linux/regulator/consumer.h> 13 14 #define PHY_CTRL0 0x0 15 #define PHY_CTRL0_REF_SSP_EN BIT(2) 16 #define PHY_CTRL0_FSEL_MASK GENMASK(10, 5) 17 #define PHY_CTRL0_FSEL_24M 0x2a 18 19 #define PHY_CTRL1 0x4 20 #define PHY_CTRL1_RESET BIT(0) 21 #define PHY_CTRL1_COMMONONN BIT(1) 22 #define PHY_CTRL1_ATERESET BIT(3) 23 #define PHY_CTRL1_VDATSRCENB0 BIT(19) 24 #define PHY_CTRL1_VDATDETENB0 BIT(20) 25 26 #define PHY_CTRL2 0x8 27 #define PHY_CTRL2_TXENABLEN0 BIT(8) 28 #define PHY_CTRL2_OTG_DISABLE BIT(9) 29 30 #define PHY_CTRL6 0x18 31 #define PHY_CTRL6_ALT_CLK_EN BIT(1) 32 #define PHY_CTRL6_ALT_CLK_SEL BIT(0) 33 34 struct imx8mq_usb_phy { 35 struct phy *phy; 36 struct clk *clk; 37 void __iomem *base; 38 struct regulator *vbus; 39 }; 40 41 static int imx8mq_usb_phy_init(struct phy *phy) 42 { 43 struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); 44 u32 value; 45 46 value = readl(imx_phy->base + PHY_CTRL1); 47 value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0 | 48 PHY_CTRL1_COMMONONN); 49 value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET; 50 writel(value, imx_phy->base + PHY_CTRL1); 51 52 value = readl(imx_phy->base + PHY_CTRL0); 53 value |= PHY_CTRL0_REF_SSP_EN; 54 writel(value, imx_phy->base + PHY_CTRL0); 55 56 value = readl(imx_phy->base + PHY_CTRL2); 57 value |= PHY_CTRL2_TXENABLEN0; 58 writel(value, imx_phy->base + PHY_CTRL2); 59 60 value = readl(imx_phy->base + PHY_CTRL1); 61 value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET); 62 writel(value, imx_phy->base + PHY_CTRL1); 63 64 return 0; 65 } 66 67 static int imx8mp_usb_phy_init(struct phy *phy) 68 { 69 struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); 70 u32 value; 71 72 /* USB3.0 PHY signal fsel for 24M ref */ 73 value = readl(imx_phy->base + PHY_CTRL0); 74 value &= ~PHY_CTRL0_FSEL_MASK; 75 value |= FIELD_PREP(PHY_CTRL0_FSEL_MASK, PHY_CTRL0_FSEL_24M); 76 writel(value, imx_phy->base + PHY_CTRL0); 77 78 /* Disable alt_clk_en and use internal MPLL clocks */ 79 value = readl(imx_phy->base + PHY_CTRL6); 80 value &= ~(PHY_CTRL6_ALT_CLK_SEL | PHY_CTRL6_ALT_CLK_EN); 81 writel(value, imx_phy->base + PHY_CTRL6); 82 83 value = readl(imx_phy->base + PHY_CTRL1); 84 value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0); 85 value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET; 86 writel(value, imx_phy->base + PHY_CTRL1); 87 88 value = readl(imx_phy->base + PHY_CTRL0); 89 value |= PHY_CTRL0_REF_SSP_EN; 90 writel(value, imx_phy->base + PHY_CTRL0); 91 92 value = readl(imx_phy->base + PHY_CTRL2); 93 value |= PHY_CTRL2_TXENABLEN0 | PHY_CTRL2_OTG_DISABLE; 94 writel(value, imx_phy->base + PHY_CTRL2); 95 96 udelay(10); 97 98 value = readl(imx_phy->base + PHY_CTRL1); 99 value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET); 100 writel(value, imx_phy->base + PHY_CTRL1); 101 102 return 0; 103 } 104 105 static int imx8mq_phy_power_on(struct phy *phy) 106 { 107 struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); 108 int ret; 109 110 ret = regulator_enable(imx_phy->vbus); 111 if (ret) 112 return ret; 113 114 return clk_prepare_enable(imx_phy->clk); 115 } 116 117 static int imx8mq_phy_power_off(struct phy *phy) 118 { 119 struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); 120 121 clk_disable_unprepare(imx_phy->clk); 122 regulator_disable(imx_phy->vbus); 123 124 return 0; 125 } 126 127 static const struct phy_ops imx8mq_usb_phy_ops = { 128 .init = imx8mq_usb_phy_init, 129 .power_on = imx8mq_phy_power_on, 130 .power_off = imx8mq_phy_power_off, 131 .owner = THIS_MODULE, 132 }; 133 134 static struct phy_ops imx8mp_usb_phy_ops = { 135 .init = imx8mp_usb_phy_init, 136 .power_on = imx8mq_phy_power_on, 137 .power_off = imx8mq_phy_power_off, 138 .owner = THIS_MODULE, 139 }; 140 141 static const struct of_device_id imx8mq_usb_phy_of_match[] = { 142 {.compatible = "fsl,imx8mq-usb-phy", 143 .data = &imx8mq_usb_phy_ops,}, 144 {.compatible = "fsl,imx8mp-usb-phy", 145 .data = &imx8mp_usb_phy_ops,}, 146 { } 147 }; 148 MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match); 149 150 static int imx8mq_usb_phy_probe(struct platform_device *pdev) 151 { 152 struct phy_provider *phy_provider; 153 struct device *dev = &pdev->dev; 154 struct imx8mq_usb_phy *imx_phy; 155 struct resource *res; 156 const struct phy_ops *phy_ops; 157 158 imx_phy = devm_kzalloc(dev, sizeof(*imx_phy), GFP_KERNEL); 159 if (!imx_phy) 160 return -ENOMEM; 161 162 imx_phy->clk = devm_clk_get(dev, "phy"); 163 if (IS_ERR(imx_phy->clk)) { 164 dev_err(dev, "failed to get imx8mq usb phy clock\n"); 165 return PTR_ERR(imx_phy->clk); 166 } 167 168 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 169 imx_phy->base = devm_ioremap_resource(dev, res); 170 if (IS_ERR(imx_phy->base)) 171 return PTR_ERR(imx_phy->base); 172 173 phy_ops = of_device_get_match_data(dev); 174 if (!phy_ops) 175 return -EINVAL; 176 177 imx_phy->phy = devm_phy_create(dev, NULL, phy_ops); 178 if (IS_ERR(imx_phy->phy)) 179 return PTR_ERR(imx_phy->phy); 180 181 imx_phy->vbus = devm_regulator_get(dev, "vbus"); 182 if (IS_ERR(imx_phy->vbus)) 183 return PTR_ERR(imx_phy->vbus); 184 185 phy_set_drvdata(imx_phy->phy, imx_phy); 186 187 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 188 189 return PTR_ERR_OR_ZERO(phy_provider); 190 } 191 192 static struct platform_driver imx8mq_usb_phy_driver = { 193 .probe = imx8mq_usb_phy_probe, 194 .driver = { 195 .name = "imx8mq-usb-phy", 196 .of_match_table = imx8mq_usb_phy_of_match, 197 } 198 }; 199 module_platform_driver(imx8mq_usb_phy_driver); 200 201 MODULE_DESCRIPTION("FSL IMX8MQ USB PHY driver"); 202 MODULE_LICENSE("GPL"); 203