phy-armada375-usb2.c (3983B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * USB cluster support for Armada 375 platform. 4 * 5 * Copyright (C) 2014 Marvell 6 * 7 * Gregory CLEMENT <gregory.clement@free-electrons.com> 8 * 9 * Armada 375 comes with an USB2 host and device controller and an 10 * USB3 controller. The USB cluster control register allows to manage 11 * common features of both USB controllers. 12 */ 13 14#include <dt-bindings/phy/phy.h> 15#include <linux/init.h> 16#include <linux/io.h> 17#include <linux/kernel.h> 18#include <linux/of_address.h> 19#include <linux/phy/phy.h> 20#include <linux/platform_device.h> 21 22#define USB2_PHY_CONFIG_DISABLE BIT(0) 23 24struct armada375_cluster_phy { 25 struct phy *phy; 26 void __iomem *reg; 27 bool use_usb3; 28 int phy_provided; 29}; 30 31static int armada375_usb_phy_init(struct phy *phy) 32{ 33 struct armada375_cluster_phy *cluster_phy; 34 u32 reg; 35 36 cluster_phy = phy_get_drvdata(phy); 37 if (!cluster_phy) 38 return -ENODEV; 39 40 reg = readl(cluster_phy->reg); 41 if (cluster_phy->use_usb3) 42 reg |= USB2_PHY_CONFIG_DISABLE; 43 else 44 reg &= ~USB2_PHY_CONFIG_DISABLE; 45 writel(reg, cluster_phy->reg); 46 47 return 0; 48} 49 50static const struct phy_ops armada375_usb_phy_ops = { 51 .init = armada375_usb_phy_init, 52 .owner = THIS_MODULE, 53}; 54 55/* 56 * Only one controller can use this PHY. We shouldn't have the case 57 * when two controllers want to use this PHY. But if this case occurs 58 * then we provide a phy to the first one and return an error for the 59 * next one. This error has also to be an error returned by 60 * devm_phy_optional_get() so different from ENODEV for USB2. In the 61 * USB3 case it still optional and we use ENODEV. 62 */ 63static struct phy *armada375_usb_phy_xlate(struct device *dev, 64 struct of_phandle_args *args) 65{ 66 struct armada375_cluster_phy *cluster_phy = dev_get_drvdata(dev); 67 68 if (!cluster_phy) 69 return ERR_PTR(-ENODEV); 70 71 /* 72 * Either the phy had never been requested and then the first 73 * usb claiming it can get it, or it had already been 74 * requested in this case, we only allow to use it with the 75 * same configuration. 76 */ 77 if (WARN_ON((cluster_phy->phy_provided != PHY_NONE) && 78 (cluster_phy->phy_provided != args->args[0]))) { 79 dev_err(dev, "This PHY has already been provided!\n"); 80 dev_err(dev, "Check your device tree, only one controller can use it\n."); 81 if (args->args[0] == PHY_TYPE_USB2) 82 return ERR_PTR(-EBUSY); 83 else 84 return ERR_PTR(-ENODEV); 85 } 86 87 if (args->args[0] == PHY_TYPE_USB2) 88 cluster_phy->use_usb3 = false; 89 else if (args->args[0] == PHY_TYPE_USB3) 90 cluster_phy->use_usb3 = true; 91 else { 92 dev_err(dev, "Invalid PHY mode\n"); 93 return ERR_PTR(-ENODEV); 94 } 95 96 /* Store which phy mode is used for next test */ 97 cluster_phy->phy_provided = args->args[0]; 98 99 return cluster_phy->phy; 100} 101 102static int armada375_usb_phy_probe(struct platform_device *pdev) 103{ 104 struct device *dev = &pdev->dev; 105 struct phy *phy; 106 struct phy_provider *phy_provider; 107 void __iomem *usb_cluster_base; 108 struct armada375_cluster_phy *cluster_phy; 109 110 cluster_phy = devm_kzalloc(dev, sizeof(*cluster_phy), GFP_KERNEL); 111 if (!cluster_phy) 112 return -ENOMEM; 113 114 usb_cluster_base = devm_platform_ioremap_resource(pdev, 0); 115 if (IS_ERR(usb_cluster_base)) 116 return PTR_ERR(usb_cluster_base); 117 118 phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops); 119 if (IS_ERR(phy)) { 120 dev_err(dev, "failed to create PHY\n"); 121 return PTR_ERR(phy); 122 } 123 124 cluster_phy->phy = phy; 125 cluster_phy->reg = usb_cluster_base; 126 127 dev_set_drvdata(dev, cluster_phy); 128 phy_set_drvdata(phy, cluster_phy); 129 130 phy_provider = devm_of_phy_provider_register(&pdev->dev, 131 armada375_usb_phy_xlate); 132 return PTR_ERR_OR_ZERO(phy_provider); 133} 134 135static const struct of_device_id of_usb_cluster_table[] = { 136 { .compatible = "marvell,armada-375-usb-cluster", }, 137 { /* end of list */ }, 138}; 139 140static struct platform_driver armada375_usb_phy_driver = { 141 .probe = armada375_usb_phy_probe, 142 .driver = { 143 .of_match_table = of_usb_cluster_table, 144 .name = "armada-375-usb-cluster", 145 } 146}; 147builtin_platform_driver(armada375_usb_phy_driver);