]>
Commit | Line | Data |
---|---|---|
c339d3e0 KH |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * phy-uniphier-usb2.c - PHY driver for UniPhier USB2 controller | |
4 | * Copyright 2015-2018 Socionext Inc. | |
5 | * Author: | |
6 | * Kunihiko Hayashi <[email protected]> | |
7 | */ | |
8 | ||
9 | #include <linux/mfd/syscon.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/of.h> | |
12 | #include <linux/of_platform.h> | |
13 | #include <linux/phy/phy.h> | |
14 | #include <linux/platform_device.h> | |
15 | #include <linux/regmap.h> | |
16 | #include <linux/regulator/consumer.h> | |
17 | ||
18 | #define SG_USBPHY1CTRL 0x500 | |
19 | #define SG_USBPHY1CTRL2 0x504 | |
20 | #define SG_USBPHY2CTRL 0x508 | |
21 | #define SG_USBPHY2CTRL2 0x50c /* LD11 */ | |
22 | #define SG_USBPHY12PLL 0x50c /* Pro4 */ | |
23 | #define SG_USBPHY3CTRL 0x510 | |
24 | #define SG_USBPHY3CTRL2 0x514 | |
25 | #define SG_USBPHY4CTRL 0x518 /* Pro4 */ | |
26 | #define SG_USBPHY4CTRL2 0x51c /* Pro4 */ | |
27 | #define SG_USBPHY34PLL 0x51c /* Pro4 */ | |
28 | ||
29 | struct uniphier_u2phy_param { | |
30 | u32 offset; | |
31 | u32 value; | |
32 | }; | |
33 | ||
34 | struct uniphier_u2phy_soc_data { | |
35 | struct uniphier_u2phy_param config0; | |
36 | struct uniphier_u2phy_param config1; | |
37 | }; | |
38 | ||
39 | struct uniphier_u2phy_priv { | |
40 | struct regmap *regmap; | |
41 | struct phy *phy; | |
42 | struct regulator *vbus; | |
43 | const struct uniphier_u2phy_soc_data *data; | |
44 | struct uniphier_u2phy_priv *next; | |
45 | }; | |
46 | ||
47 | static int uniphier_u2phy_power_on(struct phy *phy) | |
48 | { | |
49 | struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy); | |
50 | int ret = 0; | |
51 | ||
52 | if (priv->vbus) | |
53 | ret = regulator_enable(priv->vbus); | |
54 | ||
55 | return ret; | |
56 | } | |
57 | ||
58 | static int uniphier_u2phy_power_off(struct phy *phy) | |
59 | { | |
60 | struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy); | |
61 | ||
62 | if (priv->vbus) | |
63 | regulator_disable(priv->vbus); | |
64 | ||
65 | return 0; | |
66 | } | |
67 | ||
68 | static int uniphier_u2phy_init(struct phy *phy) | |
69 | { | |
70 | struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy); | |
71 | ||
72 | if (!priv->data) | |
73 | return 0; | |
74 | ||
75 | regmap_write(priv->regmap, priv->data->config0.offset, | |
76 | priv->data->config0.value); | |
77 | regmap_write(priv->regmap, priv->data->config1.offset, | |
78 | priv->data->config1.value); | |
79 | ||
80 | return 0; | |
81 | } | |
82 | ||
83 | static struct phy *uniphier_u2phy_xlate(struct device *dev, | |
84 | struct of_phandle_args *args) | |
85 | { | |
86 | struct uniphier_u2phy_priv *priv = dev_get_drvdata(dev); | |
87 | ||
88 | while (priv && args->np != priv->phy->dev.of_node) | |
89 | priv = priv->next; | |
90 | ||
91 | if (!priv) { | |
92 | dev_err(dev, "Failed to find appropriate phy\n"); | |
93 | return ERR_PTR(-EINVAL); | |
94 | } | |
95 | ||
96 | return priv->phy; | |
97 | } | |
98 | ||
99 | static const struct phy_ops uniphier_u2phy_ops = { | |
100 | .init = uniphier_u2phy_init, | |
101 | .power_on = uniphier_u2phy_power_on, | |
102 | .power_off = uniphier_u2phy_power_off, | |
103 | .owner = THIS_MODULE, | |
104 | }; | |
105 | ||
106 | static int uniphier_u2phy_probe(struct platform_device *pdev) | |
107 | { | |
108 | struct device *dev = &pdev->dev; | |
109 | struct device_node *parent, *child; | |
110 | struct uniphier_u2phy_priv *priv = NULL, *next = NULL; | |
111 | struct phy_provider *phy_provider; | |
112 | struct regmap *regmap; | |
113 | const struct uniphier_u2phy_soc_data *data; | |
114 | int ret, data_idx, ndatas; | |
115 | ||
116 | data = of_device_get_match_data(dev); | |
117 | if (WARN_ON(!data)) | |
118 | return -EINVAL; | |
119 | ||
120 | /* get number of data */ | |
121 | for (ndatas = 0; data[ndatas].config0.offset; ndatas++) | |
122 | ; | |
123 | ||
124 | parent = of_get_parent(dev->of_node); | |
125 | regmap = syscon_node_to_regmap(parent); | |
126 | of_node_put(parent); | |
127 | if (IS_ERR(regmap)) { | |
128 | dev_err(dev, "Failed to get regmap\n"); | |
129 | return PTR_ERR(regmap); | |
130 | } | |
131 | ||
132 | for_each_child_of_node(dev->of_node, child) { | |
133 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
134 | if (!priv) { | |
135 | ret = -ENOMEM; | |
136 | goto out_put_child; | |
137 | } | |
138 | priv->regmap = regmap; | |
139 | ||
140 | priv->vbus = devm_regulator_get_optional(dev, "vbus"); | |
141 | if (IS_ERR(priv->vbus)) { | |
142 | if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) { | |
143 | ret = PTR_ERR(priv->vbus); | |
144 | goto out_put_child; | |
145 | } | |
146 | priv->vbus = NULL; | |
147 | } | |
148 | ||
149 | priv->phy = devm_phy_create(dev, child, &uniphier_u2phy_ops); | |
150 | if (IS_ERR(priv->phy)) { | |
151 | dev_err(dev, "Failed to create phy\n"); | |
152 | ret = PTR_ERR(priv->phy); | |
153 | goto out_put_child; | |
154 | } | |
155 | ||
156 | ret = of_property_read_u32(child, "reg", &data_idx); | |
157 | if (ret) { | |
158 | dev_err(dev, "Failed to get reg property\n"); | |
159 | goto out_put_child; | |
160 | } | |
161 | ||
162 | if (data_idx < ndatas) | |
163 | priv->data = &data[data_idx]; | |
164 | else | |
165 | dev_warn(dev, "No phy configuration: %s\n", | |
166 | child->full_name); | |
167 | ||
168 | phy_set_drvdata(priv->phy, priv); | |
169 | priv->next = next; | |
170 | next = priv; | |
171 | } | |
172 | ||
173 | dev_set_drvdata(dev, priv); | |
174 | phy_provider = devm_of_phy_provider_register(dev, | |
175 | uniphier_u2phy_xlate); | |
176 | return PTR_ERR_OR_ZERO(phy_provider); | |
177 | ||
178 | out_put_child: | |
179 | of_node_put(child); | |
180 | ||
181 | return ret; | |
182 | } | |
183 | ||
184 | static const struct uniphier_u2phy_soc_data uniphier_pro4_data[] = { | |
185 | { | |
186 | .config0 = { SG_USBPHY1CTRL, 0x05142400 }, | |
187 | .config1 = { SG_USBPHY12PLL, 0x00010010 }, | |
188 | }, | |
189 | { | |
190 | .config0 = { SG_USBPHY2CTRL, 0x05142400 }, | |
191 | .config1 = { SG_USBPHY12PLL, 0x00010010 }, | |
192 | }, | |
193 | { | |
194 | .config0 = { SG_USBPHY3CTRL, 0x05142400 }, | |
195 | .config1 = { SG_USBPHY34PLL, 0x00010010 }, | |
196 | }, | |
197 | { | |
198 | .config0 = { SG_USBPHY4CTRL, 0x05142400 }, | |
199 | .config1 = { SG_USBPHY34PLL, 0x00010010 }, | |
200 | }, | |
201 | { /* sentinel */ } | |
202 | }; | |
203 | ||
204 | static const struct uniphier_u2phy_soc_data uniphier_ld11_data[] = { | |
205 | { | |
206 | .config0 = { SG_USBPHY1CTRL, 0x82280000 }, | |
207 | .config1 = { SG_USBPHY1CTRL2, 0x00000106 }, | |
208 | }, | |
209 | { | |
210 | .config0 = { SG_USBPHY2CTRL, 0x82280000 }, | |
211 | .config1 = { SG_USBPHY2CTRL2, 0x00000106 }, | |
212 | }, | |
213 | { | |
214 | .config0 = { SG_USBPHY3CTRL, 0x82280000 }, | |
215 | .config1 = { SG_USBPHY3CTRL2, 0x00000106 }, | |
216 | }, | |
217 | { /* sentinel */ } | |
218 | }; | |
219 | ||
220 | static const struct of_device_id uniphier_u2phy_match[] = { | |
221 | { | |
222 | .compatible = "socionext,uniphier-pro4-usb2-phy", | |
223 | .data = &uniphier_pro4_data, | |
224 | }, | |
225 | { | |
226 | .compatible = "socionext,uniphier-ld11-usb2-phy", | |
227 | .data = &uniphier_ld11_data, | |
228 | }, | |
229 | { /* sentinel */ } | |
230 | }; | |
231 | MODULE_DEVICE_TABLE(of, uniphier_u2phy_match); | |
232 | ||
233 | static struct platform_driver uniphier_u2phy_driver = { | |
234 | .probe = uniphier_u2phy_probe, | |
235 | .driver = { | |
236 | .name = "uniphier-usb2-phy", | |
237 | .of_match_table = uniphier_u2phy_match, | |
238 | }, | |
239 | }; | |
240 | module_platform_driver(uniphier_u2phy_driver); | |
241 | ||
242 | MODULE_AUTHOR("Kunihiko Hayashi <[email protected]>"); | |
243 | MODULE_DESCRIPTION("UniPhier PHY driver for USB2 controller"); | |
244 | MODULE_LICENSE("GPL v2"); |