diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-14 17:57:16 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-14 17:57:16 -0500 |
commit | e7cf773d431a63a2417902696fcc9e0ebdc83bbe (patch) | |
tree | 86dbdceb7d91226507a3af0d57e03b0ca664b22e /drivers/phy/phy-armada375-usb2.c | |
parent | 7a02d089695a1217992434f03a78aa32bad85b5c (diff) | |
parent | 81e1dadfb5b2d47aa513ad60b1c9cf0ea17b6514 (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.c | 158 |
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 | |||
28 | struct armada375_cluster_phy { | ||
29 | struct phy *phy; | ||
30 | void __iomem *reg; | ||
31 | bool use_usb3; | ||
32 | int phy_provided; | ||
33 | }; | ||
34 | |||
35 | static 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 | |||
54 | static 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 | */ | ||
67 | static 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 | |||
106 | static 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 | |||
140 | static const struct of_device_id of_usb_cluster_table[] = { | ||
141 | { .compatible = "marvell,armada-375-usb-cluster", }, | ||
142 | { /* end of list */ }, | ||
143 | }; | ||
144 | MODULE_DEVICE_TABLE(of, of_usb_cluster_table); | ||
145 | |||
146 | static 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 | }; | ||
154 | module_platform_driver(armada375_usb_phy_driver); | ||
155 | |||
156 | MODULE_DESCRIPTION("Armada 375 USB cluster driver"); | ||
157 | MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>"); | ||
158 | MODULE_LICENSE("GPL"); | ||