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 | |
24 | struct armada375_cluster_phy { |
25 | struct phy *phy; |
26 | void __iomem *reg; |
27 | bool use_usb3; |
28 | int phy_provided; |
29 | }; |
30 | |
31 | static 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(addr: 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(val: reg, addr: cluster_phy->reg); |
46 | |
47 | return 0; |
48 | } |
49 | |
50 | static 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 | */ |
63 | static struct phy *armada375_usb_phy_xlate(struct device *dev, |
64 | const 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(error: -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(error: -EBUSY); |
83 | else |
84 | return ERR_PTR(error: -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(error: -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 | |
102 | static 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, size: sizeof(*cluster_phy), GFP_KERNEL); |
111 | if (!cluster_phy) |
112 | return -ENOMEM; |
113 | |
114 | usb_cluster_base = devm_platform_ioremap_resource(pdev, index: 0); |
115 | if (IS_ERR(ptr: usb_cluster_base)) |
116 | return PTR_ERR(ptr: usb_cluster_base); |
117 | |
118 | phy = devm_phy_create(dev, NULL, ops: &armada375_usb_phy_ops); |
119 | if (IS_ERR(ptr: phy)) { |
120 | dev_err(dev, "failed to create PHY\n" ); |
121 | return PTR_ERR(ptr: phy); |
122 | } |
123 | |
124 | cluster_phy->phy = phy; |
125 | cluster_phy->reg = usb_cluster_base; |
126 | |
127 | dev_set_drvdata(dev, data: cluster_phy); |
128 | phy_set_drvdata(phy, data: cluster_phy); |
129 | |
130 | phy_provider = devm_of_phy_provider_register(&pdev->dev, |
131 | armada375_usb_phy_xlate); |
132 | return PTR_ERR_OR_ZERO(ptr: phy_provider); |
133 | } |
134 | |
135 | static const struct of_device_id of_usb_cluster_table[] = { |
136 | { .compatible = "marvell,armada-375-usb-cluster" , }, |
137 | { /* end of list */ }, |
138 | }; |
139 | |
140 | static 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 | }; |
147 | builtin_platform_driver(armada375_usb_phy_driver); |
148 | |