aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/phy/phy-armada375-usb2.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-12-14 17:57:16 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-12-14 17:57:16 -0500
commite7cf773d431a63a2417902696fcc9e0ebdc83bbe (patch)
tree86dbdceb7d91226507a3af0d57e03b0ca664b22e /drivers/phy/phy-armada375-usb2.c
parent7a02d089695a1217992434f03a78aa32bad85b5c (diff)
parent81e1dadfb5b2d47aa513ad60b1c9cf0ea17b6514 (diff)
Merge tag 'usb-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB updates from Greg KH: "Here's the big set of USB and PHY patches for 3.19-rc1. The normal churn in the USB gadget area is in here, as well as xhci and other individual USB driver updates. The PHY tree is also in here, as there were dependancies on the USB tree. All of these have been in linux-next" * tag 'usb-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (351 commits) arm: omap3: twl: remove usb phy init data usbip: fix error handling in stub_probe() usb: gadget: udc: missing curly braces USB: mos7720: delete some unneeded code wusb: replace memset by memzero_explicit usbip: remove unneeded structure usb: xhci: fix comment for PORT_DEV_REMOVE xhci: don't use the same variable for stopped and halted rings current TD xhci: clear extra bits from slot context when setting max exit latency xhci: cleanup finish_td function USB: adutux: NULL dereferences on disconnect usb: chipidea: fix platform_no_drv_owner.cocci warnings usb: chipidea: Fixed a few typos in comments Documentation: bindings: add doc for the USB2 ChipIdea USB driver usb: chipidea: add a usb2 driver for ci13xxx usb: chipidea: fix phy handling usb: chipidea: remove duplicate dev_set_drvdata for host_start usb: chipidea: parameter 'mode' isn't needed for hw_device_reset usb: chipidea: add controller reset API usb: chipidea: remove flag CI_HDRC_REQUIRE_TRANSCEIVER ...
Diffstat (limited to 'drivers/phy/phy-armada375-usb2.c')
-rw-r--r--drivers/phy/phy-armada375-usb2.c158
1 files changed, 158 insertions, 0 deletions
diff --git a/drivers/phy/phy-armada375-usb2.c b/drivers/phy/phy-armada375-usb2.c
new file mode 100644
index 000000000000..ac7d99d01cb3
--- /dev/null
+++ b/drivers/phy/phy-armada375-usb2.c
@@ -0,0 +1,158 @@
1/*
2 * USB cluster support for Armada 375 platform.
3 *
4 * Copyright (C) 2014 Marvell
5 *
6 * Gregory CLEMENT <gregory.clement@free-electrons.com>
7 *
8 * This file is licensed under the terms of the GNU General Public
9 * License version 2 or later. This program is licensed "as is"
10 * without any warranty of any kind, whether express or implied.
11 *
12 * Armada 375 comes with an USB2 host and device controller and an
13 * USB3 controller. The USB cluster control register allows to manage
14 * common features of both USB controllers.
15 */
16
17#include <dt-bindings/phy/phy.h>
18#include <linux/init.h>
19#include <linux/io.h>
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/of_address.h>
23#include <linux/phy/phy.h>
24#include <linux/platform_device.h>
25
26#define USB2_PHY_CONFIG_DISABLE BIT(0)
27
28struct armada375_cluster_phy {
29 struct phy *phy;
30 void __iomem *reg;
31 bool use_usb3;
32 int phy_provided;
33};
34
35static int armada375_usb_phy_init(struct phy *phy)
36{
37 struct armada375_cluster_phy *cluster_phy;
38 u32 reg;
39
40 cluster_phy = dev_get_drvdata(phy->dev.parent);
41 if (!cluster_phy)
42 return -ENODEV;
43
44 reg = readl(cluster_phy->reg);
45 if (cluster_phy->use_usb3)
46 reg |= USB2_PHY_CONFIG_DISABLE;
47 else
48 reg &= ~USB2_PHY_CONFIG_DISABLE;
49 writel(reg, cluster_phy->reg);
50
51 return 0;
52}
53
54static struct phy_ops armada375_usb_phy_ops = {
55 .init = armada375_usb_phy_init,
56 .owner = THIS_MODULE,
57};
58
59/*
60 * Only one controller can use this PHY. We shouldn't have the case
61 * when two controllers want to use this PHY. But if this case occurs
62 * then we provide a phy to the first one and return an error for the
63 * next one. This error has also to be an error returned by
64 * devm_phy_optional_get() so different from ENODEV for USB2. In the
65 * USB3 case it still optional and we use ENODEV.
66 */
67static struct phy *armada375_usb_phy_xlate(struct device *dev,
68 struct of_phandle_args *args)
69{
70 struct armada375_cluster_phy *cluster_phy = dev_get_drvdata(dev);
71
72 if (!cluster_phy)
73 return ERR_PTR(-ENODEV);
74
75 /*
76 * Either the phy had never been requested and then the first
77 * usb claiming it can get it, or it had already been
78 * requested in this case, we only allow to use it with the
79 * same configuration.
80 */
81 if (WARN_ON((cluster_phy->phy_provided != PHY_NONE) &&
82 (cluster_phy->phy_provided != args->args[0]))) {
83 dev_err(dev, "This PHY has already been provided!\n");
84 dev_err(dev, "Check your device tree, only one controller can use it\n.");
85 if (args->args[0] == PHY_TYPE_USB2)
86 return ERR_PTR(-EBUSY);
87 else
88 return ERR_PTR(-ENODEV);
89 }
90
91 if (args->args[0] == PHY_TYPE_USB2)
92 cluster_phy->use_usb3 = false;
93 else if (args->args[0] == PHY_TYPE_USB3)
94 cluster_phy->use_usb3 = true;
95 else {
96 dev_err(dev, "Invalid PHY mode\n");
97 return ERR_PTR(-ENODEV);
98 }
99
100 /* Store which phy mode is used for next test */
101 cluster_phy->phy_provided = args->args[0];
102
103 return cluster_phy->phy;
104}
105
106static int armada375_usb_phy_probe(struct platform_device *pdev)
107{
108 struct device *dev = &pdev->dev;
109 struct phy *phy;
110 struct phy_provider *phy_provider;
111 void __iomem *usb_cluster_base;
112 struct resource *res;
113 struct armada375_cluster_phy *cluster_phy;
114
115 cluster_phy = devm_kzalloc(dev, sizeof(*cluster_phy), GFP_KERNEL);
116 if (!cluster_phy)
117 return -ENOMEM;
118
119 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
120 usb_cluster_base = devm_ioremap_resource(&pdev->dev, res);
121 if (!usb_cluster_base)
122 return -ENOMEM;
123
124 phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops);
125 if (IS_ERR(phy)) {
126 dev_err(dev, "failed to create PHY\n");
127 return PTR_ERR(phy);
128 }
129
130 cluster_phy->phy = phy;
131 cluster_phy->reg = usb_cluster_base;
132
133 dev_set_drvdata(dev, cluster_phy);
134
135 phy_provider = devm_of_phy_provider_register(&pdev->dev,
136 armada375_usb_phy_xlate);
137 return PTR_ERR_OR_ZERO(phy_provider);
138}
139
140static const struct of_device_id of_usb_cluster_table[] = {
141 { .compatible = "marvell,armada-375-usb-cluster", },
142 { /* end of list */ },
143};
144MODULE_DEVICE_TABLE(of, of_usb_cluster_table);
145
146static struct platform_driver armada375_usb_phy_driver = {
147 .probe = armada375_usb_phy_probe,
148 .driver = {
149 .of_match_table = of_usb_cluster_table,
150 .name = "armada-375-usb-cluster",
151 .owner = THIS_MODULE,
152 }
153};
154module_platform_driver(armada375_usb_phy_driver);
155
156MODULE_DESCRIPTION("Armada 375 USB cluster driver");
157MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
158MODULE_LICENSE("GPL");