Blame view
drivers/phy/phy-fsl-imx8mq-usb.c
3.44 KB
81f7e3824
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
/* * Copyright (c) 2017 NXP. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include <linux/clk.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/phy/phy.h> #include <linux/io.h> #define PHY_CTRL0 0x0 #define PHY_CTRL0_REF_SSP_EN BIT(2) #define PHY_CTRL0_SSC_RANGE_MASK (7 << 21) #define PHY_CTRL0_SSC_RANGE_4003PPM (0x2 << 21) #define PHY_CTRL1 0x4 #define PHY_CTRL1_RESET BIT(0) #define PHY_CTRL1_COMMONONN BIT(1) #define PHY_CTRL1_ATERESET BIT(3) #define PHY_CTRL1_VDATSRCENB0 BIT(19) #define PHY_CTRL1_VDATDETENB0 BIT(20) #define PHY_CTRL2 0x8 #define PHY_CTRL2_TXENABLEN0 BIT(8) struct imx8mq_usb_phy { struct phy *phy; struct clk *clk; void __iomem *base; }; static int imx8mq_phy_start(struct phy *_phy) { struct imx8mq_usb_phy *phy = phy_get_drvdata(_phy); return clk_prepare_enable(phy->clk); } static int imx8mq_phy_exit(struct phy *_phy) { struct imx8mq_usb_phy *phy = phy_get_drvdata(_phy); clk_disable_unprepare(phy->clk); return 0; } static struct phy_ops imx8mq_usb_phy_ops = { .init = imx8mq_phy_start, .exit = imx8mq_phy_exit, .owner = THIS_MODULE, }; static void imx8mq_usb_phy_init(struct imx8mq_usb_phy *phy) { u32 value; value = readl(phy->base + PHY_CTRL1); value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0 | PHY_CTRL1_COMMONONN); value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET; writel(value, phy->base + PHY_CTRL1); value = readl(phy->base + PHY_CTRL0); value |= PHY_CTRL0_REF_SSP_EN; value &= ~PHY_CTRL0_SSC_RANGE_MASK; value |= PHY_CTRL0_SSC_RANGE_4003PPM; writel(value, phy->base + PHY_CTRL0); value = readl(phy->base + PHY_CTRL2); value |= PHY_CTRL2_TXENABLEN0; writel(value, phy->base + PHY_CTRL2); value = readl(phy->base + PHY_CTRL1); value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET); writel(value, phy->base + PHY_CTRL1); } static int imx8mq_usb_phy_probe(struct platform_device *pdev) { struct phy_provider *phy_provider; struct device *dev = &pdev->dev; struct imx8mq_usb_phy *imx_phy; struct resource *res; imx_phy = devm_kzalloc(dev, sizeof(*imx_phy), GFP_KERNEL); if (!imx_phy) return -ENOMEM; imx_phy->clk = devm_clk_get(dev, "usb_phy_root_clk"); if (IS_ERR(imx_phy->clk)) { dev_err(dev, "failed to get imx8mq usb phy clock "); return PTR_ERR(imx_phy->clk); } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); imx_phy->base = devm_ioremap_resource(dev, res); if (IS_ERR(imx_phy->base)) return PTR_ERR(imx_phy->base); imx_phy->phy = devm_phy_create(dev, NULL, &imx8mq_usb_phy_ops); if (IS_ERR(imx_phy->phy)) return PTR_ERR(imx_phy->phy); phy_set_drvdata(imx_phy->phy, imx_phy); imx8mq_usb_phy_init(imx_phy); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); return PTR_ERR_OR_ZERO(phy_provider); } static const struct of_device_id imx8mq_usb_phy_of_match[] = { {.compatible = "fsl,imx8mq-usb-phy",}, { }, }; MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match); static struct platform_driver imx8mq_usb_phy_driver = { .probe = imx8mq_usb_phy_probe, .driver = { .name = "imx8mq-usb-phy", .of_match_table = imx8mq_usb_phy_of_match, } }; module_platform_driver(imx8mq_usb_phy_driver); MODULE_DESCRIPTION("FSL IMX8MQ USB PHY driver"); MODULE_ALIAS("platform:imx8mq-usb-phy"); MODULE_LICENSE("GPL"); |