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

phy: add driver for Qualcomm IPQ40xx USB PHY

Add a driver to setup the USB PHY-s on Qualcom m IPQ40xx series SoCs.
The driver sets up HS and SS phys.

Signed-off-by: John Crispin <john@phrozen.org>
Signed-off-by: Robert Marko <robert.marko@sartura.hr>
Cc: Luka Perkov <luka.perkov@sartura.hr>
Link: https://lore.kernel.org/r/20200503201823.531757-1-robert.marko@sartura.hr
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Robert Marko and committed by
Vinod Koul
3c9d8f6c 2f29298b

+156
+7
drivers/phy/qualcomm/Kconfig
··· 18 18 depends on OF 19 19 select GENERIC_PHY 20 20 21 + config PHY_QCOM_IPQ4019_USB 22 + tristate "Qualcomm IPQ4019 USB PHY driver" 23 + depends on OF && (ARCH_QCOM || COMPILE_TEST) 24 + select GENERIC_PHY 25 + help 26 + Support for the USB PHY-s on Qualcomm IPQ40xx SoC-s. 27 + 21 28 config PHY_QCOM_IPQ806X_SATA 22 29 tristate "Qualcomm IPQ806x SATA SerDes/PHY driver" 23 30 depends on ARCH_QCOM
+1
drivers/phy/qualcomm/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 obj-$(CONFIG_PHY_ATH79_USB) += phy-ath79-usb.o 3 3 obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o 4 + obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o 4 5 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o 5 6 obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o 6 7 obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o
+148
drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Copyright (C) 2018 John Crispin <john@phrozen.org> 4 + * 5 + * Based on code from 6 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> 7 + * 8 + */ 9 + 10 + #include <linux/delay.h> 11 + #include <linux/err.h> 12 + #include <linux/io.h> 13 + #include <linux/kernel.h> 14 + #include <linux/module.h> 15 + #include <linux/mutex.h> 16 + #include <linux/of_platform.h> 17 + #include <linux/of_device.h> 18 + #include <linux/phy/phy.h> 19 + #include <linux/platform_device.h> 20 + #include <linux/reset.h> 21 + 22 + struct ipq4019_usb_phy { 23 + struct device *dev; 24 + struct phy *phy; 25 + void __iomem *base; 26 + struct reset_control *por_rst; 27 + struct reset_control *srif_rst; 28 + }; 29 + 30 + static int ipq4019_ss_phy_power_off(struct phy *_phy) 31 + { 32 + struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy); 33 + 34 + reset_control_assert(phy->por_rst); 35 + msleep(10); 36 + 37 + return 0; 38 + } 39 + 40 + static int ipq4019_ss_phy_power_on(struct phy *_phy) 41 + { 42 + struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy); 43 + 44 + ipq4019_ss_phy_power_off(_phy); 45 + 46 + reset_control_deassert(phy->por_rst); 47 + 48 + return 0; 49 + } 50 + 51 + static struct phy_ops ipq4019_usb_ss_phy_ops = { 52 + .power_on = ipq4019_ss_phy_power_on, 53 + .power_off = ipq4019_ss_phy_power_off, 54 + }; 55 + 56 + static int ipq4019_hs_phy_power_off(struct phy *_phy) 57 + { 58 + struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy); 59 + 60 + reset_control_assert(phy->por_rst); 61 + msleep(10); 62 + 63 + reset_control_assert(phy->srif_rst); 64 + msleep(10); 65 + 66 + return 0; 67 + } 68 + 69 + static int ipq4019_hs_phy_power_on(struct phy *_phy) 70 + { 71 + struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy); 72 + 73 + ipq4019_hs_phy_power_off(_phy); 74 + 75 + reset_control_deassert(phy->srif_rst); 76 + msleep(10); 77 + 78 + reset_control_deassert(phy->por_rst); 79 + 80 + return 0; 81 + } 82 + 83 + static struct phy_ops ipq4019_usb_hs_phy_ops = { 84 + .power_on = ipq4019_hs_phy_power_on, 85 + .power_off = ipq4019_hs_phy_power_off, 86 + }; 87 + 88 + static const struct of_device_id ipq4019_usb_phy_of_match[] = { 89 + { .compatible = "qcom,usb-hs-ipq4019-phy", .data = &ipq4019_usb_hs_phy_ops}, 90 + { .compatible = "qcom,usb-ss-ipq4019-phy", .data = &ipq4019_usb_ss_phy_ops}, 91 + { }, 92 + }; 93 + MODULE_DEVICE_TABLE(of, ipq4019_usb_phy_of_match); 94 + 95 + static int ipq4019_usb_phy_probe(struct platform_device *pdev) 96 + { 97 + struct device *dev = &pdev->dev; 98 + struct resource *res; 99 + struct phy_provider *phy_provider; 100 + struct ipq4019_usb_phy *phy; 101 + 102 + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); 103 + if (!phy) 104 + return -ENOMEM; 105 + 106 + phy->dev = &pdev->dev; 107 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 108 + phy->base = devm_ioremap_resource(&pdev->dev, res); 109 + if (IS_ERR(phy->base)) { 110 + dev_err(dev, "failed to remap register memory\n"); 111 + return PTR_ERR(phy->base); 112 + } 113 + 114 + phy->por_rst = devm_reset_control_get(phy->dev, "por_rst"); 115 + if (IS_ERR(phy->por_rst)) { 116 + if (PTR_ERR(phy->por_rst) != -EPROBE_DEFER) 117 + dev_err(dev, "POR reset is missing\n"); 118 + return PTR_ERR(phy->por_rst); 119 + } 120 + 121 + phy->srif_rst = devm_reset_control_get_optional(phy->dev, "srif_rst"); 122 + if (IS_ERR(phy->srif_rst)) 123 + return PTR_ERR(phy->srif_rst); 124 + 125 + phy->phy = devm_phy_create(dev, NULL, of_device_get_match_data(dev)); 126 + if (IS_ERR(phy->phy)) { 127 + dev_err(dev, "failed to create PHY\n"); 128 + return PTR_ERR(phy->phy); 129 + } 130 + phy_set_drvdata(phy->phy, phy); 131 + 132 + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 133 + 134 + return PTR_ERR_OR_ZERO(phy_provider); 135 + } 136 + 137 + static struct platform_driver ipq4019_usb_phy_driver = { 138 + .probe = ipq4019_usb_phy_probe, 139 + .driver = { 140 + .of_match_table = ipq4019_usb_phy_of_match, 141 + .name = "ipq4019-usb-phy", 142 + } 143 + }; 144 + module_platform_driver(ipq4019_usb_phy_driver); 145 + 146 + MODULE_DESCRIPTION("QCOM/IPQ4019 USB phy driver"); 147 + MODULE_AUTHOR("John Crispin <john@phrozen.org>"); 148 + MODULE_LICENSE("GPL v2");