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 v5.4 139 lines 3.5 kB view raw
1// SPDX-License-Identifier: GPL-2.0+ 2/* Copyright (c) 2017 NXP. */ 3 4#include <linux/clk.h> 5#include <linux/io.h> 6#include <linux/module.h> 7#include <linux/phy/phy.h> 8#include <linux/platform_device.h> 9#include <linux/regulator/consumer.h> 10 11#define PHY_CTRL0 0x0 12#define PHY_CTRL0_REF_SSP_EN BIT(2) 13 14#define PHY_CTRL1 0x4 15#define PHY_CTRL1_RESET BIT(0) 16#define PHY_CTRL1_COMMONONN BIT(1) 17#define PHY_CTRL1_ATERESET BIT(3) 18#define PHY_CTRL1_VDATSRCENB0 BIT(19) 19#define PHY_CTRL1_VDATDETENB0 BIT(20) 20 21#define PHY_CTRL2 0x8 22#define PHY_CTRL2_TXENABLEN0 BIT(8) 23 24struct imx8mq_usb_phy { 25 struct phy *phy; 26 struct clk *clk; 27 void __iomem *base; 28 struct regulator *vbus; 29}; 30 31static int imx8mq_usb_phy_init(struct phy *phy) 32{ 33 struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); 34 u32 value; 35 36 value = readl(imx_phy->base + PHY_CTRL1); 37 value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0 | 38 PHY_CTRL1_COMMONONN); 39 value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET; 40 writel(value, imx_phy->base + PHY_CTRL1); 41 42 value = readl(imx_phy->base + PHY_CTRL0); 43 value |= PHY_CTRL0_REF_SSP_EN; 44 writel(value, imx_phy->base + PHY_CTRL0); 45 46 value = readl(imx_phy->base + PHY_CTRL2); 47 value |= PHY_CTRL2_TXENABLEN0; 48 writel(value, imx_phy->base + PHY_CTRL2); 49 50 value = readl(imx_phy->base + PHY_CTRL1); 51 value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET); 52 writel(value, imx_phy->base + PHY_CTRL1); 53 54 return 0; 55} 56 57static int imx8mq_phy_power_on(struct phy *phy) 58{ 59 struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); 60 int ret; 61 62 ret = regulator_enable(imx_phy->vbus); 63 if (ret) 64 return ret; 65 66 return clk_prepare_enable(imx_phy->clk); 67} 68 69static int imx8mq_phy_power_off(struct phy *phy) 70{ 71 struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); 72 73 clk_disable_unprepare(imx_phy->clk); 74 regulator_disable(imx_phy->vbus); 75 76 return 0; 77} 78 79static struct phy_ops imx8mq_usb_phy_ops = { 80 .init = imx8mq_usb_phy_init, 81 .power_on = imx8mq_phy_power_on, 82 .power_off = imx8mq_phy_power_off, 83 .owner = THIS_MODULE, 84}; 85 86static int imx8mq_usb_phy_probe(struct platform_device *pdev) 87{ 88 struct phy_provider *phy_provider; 89 struct device *dev = &pdev->dev; 90 struct imx8mq_usb_phy *imx_phy; 91 struct resource *res; 92 93 imx_phy = devm_kzalloc(dev, sizeof(*imx_phy), GFP_KERNEL); 94 if (!imx_phy) 95 return -ENOMEM; 96 97 imx_phy->clk = devm_clk_get(dev, "phy"); 98 if (IS_ERR(imx_phy->clk)) { 99 dev_err(dev, "failed to get imx8mq usb phy clock\n"); 100 return PTR_ERR(imx_phy->clk); 101 } 102 103 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 104 imx_phy->base = devm_ioremap_resource(dev, res); 105 if (IS_ERR(imx_phy->base)) 106 return PTR_ERR(imx_phy->base); 107 108 imx_phy->phy = devm_phy_create(dev, NULL, &imx8mq_usb_phy_ops); 109 if (IS_ERR(imx_phy->phy)) 110 return PTR_ERR(imx_phy->phy); 111 112 imx_phy->vbus = devm_regulator_get(dev, "vbus"); 113 if (IS_ERR(imx_phy->vbus)) 114 return PTR_ERR(imx_phy->vbus); 115 116 phy_set_drvdata(imx_phy->phy, imx_phy); 117 118 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 119 120 return PTR_ERR_OR_ZERO(phy_provider); 121} 122 123static const struct of_device_id imx8mq_usb_phy_of_match[] = { 124 {.compatible = "fsl,imx8mq-usb-phy",}, 125 { }, 126}; 127MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match); 128 129static struct platform_driver imx8mq_usb_phy_driver = { 130 .probe = imx8mq_usb_phy_probe, 131 .driver = { 132 .name = "imx8mq-usb-phy", 133 .of_match_table = imx8mq_usb_phy_of_match, 134 } 135}; 136module_platform_driver(imx8mq_usb_phy_driver); 137 138MODULE_DESCRIPTION("FSL IMX8MQ USB PHY driver"); 139MODULE_LICENSE("GPL");