aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/phy
diff options
context:
space:
mode:
authorGregory CLEMENT <gregory.clement@free-electrons.com>2014-11-13 06:47:46 -0500
committerKishon Vijay Abraham I <kishon@ti.com>2014-11-26 00:37:14 -0500
commiteee47538ec1f26198cf5da675975b61d7f16135b (patch)
tree84b97f801f796ddb012526390e7c378b372de51a /drivers/phy
parent1a6ab1c0e844041524f8edd1acf0de94df991aaa (diff)
phy: add support for USB cluster on the Armada 375 SoC
The Armada 375 SoC comes with an USB2 host and device controller and an USB3 controller. The USB cluster control register allows to manage common features of both USB controllers. This commit adds a driver integrated in the generic PHY framework to control this USB cluster feature. Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> [ kishon@ti.com : Made it to use the updated devm_phy_create API and soem cosmentic changes in Kconfig file.] Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-by: Jason Cooper <jason@lakedaemon.net>
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/Kconfig6
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-armada375-usb2.c158
3 files changed, 165 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 96d43d5acc57..ccad8809ecb1 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -29,6 +29,12 @@ config PHY_BERLIN_SATA
29 help 29 help
30 Enable this to support the SATA PHY on Marvell Berlin SoCs. 30 Enable this to support the SATA PHY on Marvell Berlin SoCs.
31 31
32config ARMADA375_USBCLUSTER_PHY
33 def_bool y
34 depends on MACH_ARMADA_375 || COMPILE_TEST
35 depends on OF
36 select GENERIC_PHY
37
32config PHY_EXYNOS_MIPI_VIDEO 38config PHY_EXYNOS_MIPI_VIDEO
33 tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" 39 tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
34 depends on HAS_IOMEM 40 depends on HAS_IOMEM
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 68022f698f51..aa74f961e44e 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -5,6 +5,7 @@
5obj-$(CONFIG_GENERIC_PHY) += phy-core.o 5obj-$(CONFIG_GENERIC_PHY) += phy-core.o
6obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o 6obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
7obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o 7obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
8obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
8obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o 9obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
9obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o 10obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
10obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o 11obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
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");