Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
01658f0f | 2 | /* |
14da699b | 3 | * omap-control-phy.c - The PHY part of control module. |
01658f0f KVA |
4 | * |
5 | * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com | |
01658f0f | 6 | * Author: Kishon Vijay Abraham I <kishon@ti.com> |
01658f0f KVA |
7 | */ |
8 | ||
9 | #include <linux/module.h> | |
10 | #include <linux/platform_device.h> | |
11 | #include <linux/slab.h> | |
12 | #include <linux/of.h> | |
6cb9310a | 13 | #include <linux/of_device.h> |
01658f0f KVA |
14 | #include <linux/err.h> |
15 | #include <linux/io.h> | |
16 | #include <linux/clk.h> | |
14da699b | 17 | #include <linux/phy/omap_control_phy.h> |
01658f0f | 18 | |
f0e2cf7b KVA |
19 | /** |
20 | * omap_control_pcie_pcs - set the PCS delay count | |
21 | * @dev: the control module device | |
f0e2cf7b KVA |
22 | * @delay: 8 bit delay value |
23 | */ | |
0bc09f9c | 24 | void omap_control_pcie_pcs(struct device *dev, u8 delay) |
f0e2cf7b KVA |
25 | { |
26 | u32 val; | |
27 | struct omap_control_phy *control_phy; | |
28 | ||
16c57fff | 29 | if (IS_ERR_OR_NULL(dev)) { |
f0e2cf7b KVA |
30 | pr_err("%s: invalid device\n", __func__); |
31 | return; | |
32 | } | |
33 | ||
34 | control_phy = dev_get_drvdata(dev); | |
35 | if (!control_phy) { | |
36 | dev_err(dev, "%s: invalid control phy device\n", __func__); | |
37 | return; | |
38 | } | |
39 | ||
40 | if (control_phy->type != OMAP_CTRL_TYPE_PCIE) { | |
41 | dev_err(dev, "%s: unsupported operation\n", __func__); | |
42 | return; | |
43 | } | |
44 | ||
45 | val = readl(control_phy->pcie_pcs); | |
46 | val &= ~(OMAP_CTRL_PCIE_PCS_MASK << | |
0bc09f9c V |
47 | OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); |
48 | val |= (delay << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); | |
f0e2cf7b KVA |
49 | writel(val, control_phy->pcie_pcs); |
50 | } | |
51 | EXPORT_SYMBOL_GPL(omap_control_pcie_pcs); | |
52 | ||
01658f0f | 53 | /** |
14da699b | 54 | * omap_control_phy_power - power on/off the phy using control module reg |
01658f0f | 55 | * @dev: the control module device |
6cb9310a | 56 | * @on: 0 or 1, based on powering on or off the PHY |
01658f0f | 57 | */ |
14da699b | 58 | void omap_control_phy_power(struct device *dev, int on) |
01658f0f KVA |
59 | { |
60 | u32 val; | |
61 | unsigned long rate; | |
14da699b | 62 | struct omap_control_phy *control_phy; |
01658f0f | 63 | |
16c57fff | 64 | if (IS_ERR_OR_NULL(dev)) { |
6cb9310a | 65 | pr_err("%s: invalid device\n", __func__); |
01658f0f | 66 | return; |
6cb9310a | 67 | } |
01658f0f | 68 | |
14da699b KVA |
69 | control_phy = dev_get_drvdata(dev); |
70 | if (!control_phy) { | |
71 | dev_err(dev, "%s: invalid control phy device\n", __func__); | |
6cb9310a | 72 | return; |
01658f0f KVA |
73 | } |
74 | ||
14da699b | 75 | if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) |
6cb9310a | 76 | return; |
01658f0f | 77 | |
14da699b | 78 | val = readl(control_phy->power); |
6cb9310a | 79 | |
14da699b | 80 | switch (control_phy->type) { |
6cb9310a RQ |
81 | case OMAP_CTRL_TYPE_USB2: |
82 | if (on) | |
83 | val &= ~OMAP_CTRL_DEV_PHY_PD; | |
84 | else | |
85 | val |= OMAP_CTRL_DEV_PHY_PD; | |
86 | break; | |
01658f0f | 87 | |
f0e2cf7b | 88 | case OMAP_CTRL_TYPE_PCIE: |
6cb9310a | 89 | case OMAP_CTRL_TYPE_PIPE3: |
14da699b | 90 | rate = clk_get_rate(control_phy->sys_clk); |
6cb9310a RQ |
91 | rate = rate/1000000; |
92 | ||
93 | if (on) { | |
14da699b KVA |
94 | val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | |
95 | OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK); | |
96 | val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON << | |
97 | OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; | |
98 | val |= rate << | |
99 | OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; | |
6cb9310a | 100 | } else { |
14da699b KVA |
101 | val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK; |
102 | val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF << | |
103 | OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; | |
6cb9310a RQ |
104 | } |
105 | break; | |
01658f0f | 106 | |
6cb9310a RQ |
107 | case OMAP_CTRL_TYPE_DRA7USB2: |
108 | if (on) | |
109 | val &= ~OMAP_CTRL_USB2_PHY_PD; | |
110 | else | |
111 | val |= OMAP_CTRL_USB2_PHY_PD; | |
112 | break; | |
c4b34a3b GC |
113 | |
114 | case OMAP_CTRL_TYPE_AM437USB2: | |
115 | if (on) { | |
116 | val &= ~(AM437X_CTRL_USB2_PHY_PD | | |
117 | AM437X_CTRL_USB2_OTG_PD); | |
118 | val |= (AM437X_CTRL_USB2_OTGVDET_EN | | |
119 | AM437X_CTRL_USB2_OTGSESSEND_EN); | |
120 | } else { | |
121 | val &= ~(AM437X_CTRL_USB2_OTGVDET_EN | | |
122 | AM437X_CTRL_USB2_OTGSESSEND_EN); | |
123 | val |= (AM437X_CTRL_USB2_PHY_PD | | |
124 | AM437X_CTRL_USB2_OTG_PD); | |
125 | } | |
126 | break; | |
6cb9310a RQ |
127 | default: |
128 | dev_err(dev, "%s: type %d not recognized\n", | |
14da699b | 129 | __func__, control_phy->type); |
6cb9310a RQ |
130 | break; |
131 | } | |
01658f0f | 132 | |
14da699b | 133 | writel(val, control_phy->power); |
01658f0f | 134 | } |
14da699b | 135 | EXPORT_SYMBOL_GPL(omap_control_phy_power); |
01658f0f KVA |
136 | |
137 | /** | |
138 | * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded | |
14da699b | 139 | * @ctrl_phy: struct omap_control_phy * |
01658f0f KVA |
140 | * |
141 | * Writes to the mailbox register to notify the usb core that a usb | |
142 | * device has been connected. | |
143 | */ | |
14da699b | 144 | static void omap_control_usb_host_mode(struct omap_control_phy *ctrl_phy) |
01658f0f KVA |
145 | { |
146 | u32 val; | |
147 | ||
14da699b | 148 | val = readl(ctrl_phy->otghs_control); |
01658f0f KVA |
149 | val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND); |
150 | val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID; | |
14da699b | 151 | writel(val, ctrl_phy->otghs_control); |
01658f0f KVA |
152 | } |
153 | ||
154 | /** | |
155 | * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high | |
156 | * impedance | |
14da699b | 157 | * @ctrl_phy: struct omap_control_phy * |
01658f0f KVA |
158 | * |
159 | * Writes to the mailbox register to notify the usb core that it has been | |
160 | * connected to a usb host. | |
161 | */ | |
14da699b | 162 | static void omap_control_usb_device_mode(struct omap_control_phy *ctrl_phy) |
01658f0f KVA |
163 | { |
164 | u32 val; | |
165 | ||
14da699b | 166 | val = readl(ctrl_phy->otghs_control); |
01658f0f KVA |
167 | val &= ~OMAP_CTRL_DEV_SESSEND; |
168 | val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID | | |
169 | OMAP_CTRL_DEV_VBUSVALID; | |
14da699b | 170 | writel(val, ctrl_phy->otghs_control); |
01658f0f KVA |
171 | } |
172 | ||
173 | /** | |
174 | * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high | |
175 | * impedance | |
14da699b | 176 | * @ctrl_phy: struct omap_control_phy * |
01658f0f KVA |
177 | * |
178 | * Writes to the mailbox register to notify the usb core it's now in | |
179 | * disconnected state. | |
180 | */ | |
14da699b | 181 | static void omap_control_usb_set_sessionend(struct omap_control_phy *ctrl_phy) |
01658f0f KVA |
182 | { |
183 | u32 val; | |
184 | ||
14da699b | 185 | val = readl(ctrl_phy->otghs_control); |
01658f0f KVA |
186 | val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID); |
187 | val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND; | |
14da699b | 188 | writel(val, ctrl_phy->otghs_control); |
01658f0f KVA |
189 | } |
190 | ||
191 | /** | |
192 | * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode | |
193 | * or device mode or to denote disconnected state | |
194 | * @dev: the control module device | |
195 | * @mode: The mode to which usb should be configured | |
196 | * | |
197 | * This is an API to write to the mailbox register to notify the usb core that | |
198 | * a usb device has been connected. | |
199 | */ | |
200 | void omap_control_usb_set_mode(struct device *dev, | |
201 | enum omap_control_usb_mode mode) | |
202 | { | |
14da699b | 203 | struct omap_control_phy *ctrl_phy; |
01658f0f | 204 | |
16c57fff | 205 | if (IS_ERR_OR_NULL(dev)) |
01658f0f KVA |
206 | return; |
207 | ||
14da699b | 208 | ctrl_phy = dev_get_drvdata(dev); |
14da699b KVA |
209 | if (!ctrl_phy) { |
210 | dev_err(dev, "Invalid control phy device\n"); | |
0bb85dc2 RQ |
211 | return; |
212 | } | |
213 | ||
14da699b | 214 | if (ctrl_phy->type != OMAP_CTRL_TYPE_OTGHS) |
0bb85dc2 RQ |
215 | return; |
216 | ||
01658f0f KVA |
217 | switch (mode) { |
218 | case USB_MODE_HOST: | |
14da699b | 219 | omap_control_usb_host_mode(ctrl_phy); |
01658f0f KVA |
220 | break; |
221 | case USB_MODE_DEVICE: | |
14da699b | 222 | omap_control_usb_device_mode(ctrl_phy); |
01658f0f KVA |
223 | break; |
224 | case USB_MODE_DISCONNECT: | |
14da699b | 225 | omap_control_usb_set_sessionend(ctrl_phy); |
01658f0f KVA |
226 | break; |
227 | default: | |
228 | dev_vdbg(dev, "invalid omap control usb mode\n"); | |
229 | } | |
230 | } | |
231 | EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); | |
232 | ||
14da699b KVA |
233 | static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS; |
234 | static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2; | |
235 | static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3; | |
f0e2cf7b | 236 | static const enum omap_control_phy_type pcie_data = OMAP_CTRL_TYPE_PCIE; |
14da699b KVA |
237 | static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2; |
238 | static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2; | |
6cb9310a | 239 | |
14da699b | 240 | static const struct of_device_id omap_control_phy_id_table[] = { |
6cb9310a RQ |
241 | { |
242 | .compatible = "ti,control-phy-otghs", | |
243 | .data = &otghs_data, | |
244 | }, | |
245 | { | |
246 | .compatible = "ti,control-phy-usb2", | |
247 | .data = &usb2_data, | |
248 | }, | |
249 | { | |
250 | .compatible = "ti,control-phy-pipe3", | |
251 | .data = &pipe3_data, | |
252 | }, | |
f0e2cf7b KVA |
253 | { |
254 | .compatible = "ti,control-phy-pcie", | |
255 | .data = &pcie_data, | |
256 | }, | |
6cb9310a | 257 | { |
51c9f4ad | 258 | .compatible = "ti,control-phy-usb2-dra7", |
6cb9310a RQ |
259 | .data = &dra7usb2_data, |
260 | }, | |
c4b34a3b | 261 | { |
51c9f4ad | 262 | .compatible = "ti,control-phy-usb2-am437", |
c4b34a3b GC |
263 | .data = &am437usb2_data, |
264 | }, | |
6cb9310a RQ |
265 | {}, |
266 | }; | |
14da699b | 267 | MODULE_DEVICE_TABLE(of, omap_control_phy_id_table); |
6cb9310a | 268 | |
14da699b | 269 | static int omap_control_phy_probe(struct platform_device *pdev) |
01658f0f | 270 | { |
6cb9310a | 271 | const struct of_device_id *of_id; |
14da699b | 272 | struct omap_control_phy *control_phy; |
6cb9310a | 273 | |
1f9ba767 | 274 | of_id = of_match_device(omap_control_phy_id_table, &pdev->dev); |
6cb9310a RQ |
275 | if (!of_id) |
276 | return -EINVAL; | |
01658f0f | 277 | |
14da699b | 278 | control_phy = devm_kzalloc(&pdev->dev, sizeof(*control_phy), |
01658f0f | 279 | GFP_KERNEL); |
437a6bc4 | 280 | if (!control_phy) |
01658f0f | 281 | return -ENOMEM; |
01658f0f | 282 | |
14da699b KVA |
283 | control_phy->dev = &pdev->dev; |
284 | control_phy->type = *(enum omap_control_phy_type *)of_id->data; | |
01658f0f | 285 | |
14da699b | 286 | if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) { |
79caf207 CY |
287 | control_phy->otghs_control = |
288 | devm_platform_ioremap_resource_byname(pdev, "otghs_control"); | |
14da699b KVA |
289 | if (IS_ERR(control_phy->otghs_control)) |
290 | return PTR_ERR(control_phy->otghs_control); | |
6cb9310a | 291 | } else { |
79caf207 CY |
292 | control_phy->power = |
293 | devm_platform_ioremap_resource_byname(pdev, "power"); | |
14da699b | 294 | if (IS_ERR(control_phy->power)) { |
6cb9310a | 295 | dev_err(&pdev->dev, "Couldn't get power register\n"); |
14da699b | 296 | return PTR_ERR(control_phy->power); |
6cb9310a RQ |
297 | } |
298 | } | |
01658f0f | 299 | |
f0e2cf7b KVA |
300 | if (control_phy->type == OMAP_CTRL_TYPE_PIPE3 || |
301 | control_phy->type == OMAP_CTRL_TYPE_PCIE) { | |
14da699b | 302 | control_phy->sys_clk = devm_clk_get(control_phy->dev, |
01658f0f | 303 | "sys_clkin"); |
14da699b | 304 | if (IS_ERR(control_phy->sys_clk)) { |
01658f0f KVA |
305 | pr_err("%s: unable to get sys_clkin\n", __func__); |
306 | return -EINVAL; | |
307 | } | |
308 | } | |
309 | ||
f0e2cf7b | 310 | if (control_phy->type == OMAP_CTRL_TYPE_PCIE) { |
79caf207 CY |
311 | control_phy->pcie_pcs = |
312 | devm_platform_ioremap_resource_byname(pdev, "pcie_pcs"); | |
f0e2cf7b KVA |
313 | if (IS_ERR(control_phy->pcie_pcs)) |
314 | return PTR_ERR(control_phy->pcie_pcs); | |
315 | } | |
316 | ||
14da699b | 317 | dev_set_drvdata(control_phy->dev, control_phy); |
01658f0f KVA |
318 | |
319 | return 0; | |
320 | } | |
321 | ||
14da699b KVA |
322 | static struct platform_driver omap_control_phy_driver = { |
323 | .probe = omap_control_phy_probe, | |
01658f0f | 324 | .driver = { |
14da699b | 325 | .name = "omap-control-phy", |
1f9ba767 | 326 | .of_match_table = omap_control_phy_id_table, |
01658f0f KVA |
327 | }, |
328 | }; | |
329 | ||
14da699b | 330 | static int __init omap_control_phy_init(void) |
01658f0f | 331 | { |
14da699b | 332 | return platform_driver_register(&omap_control_phy_driver); |
01658f0f | 333 | } |
14da699b | 334 | subsys_initcall(omap_control_phy_init); |
01658f0f | 335 | |
14da699b | 336 | static void __exit omap_control_phy_exit(void) |
01658f0f | 337 | { |
14da699b | 338 | platform_driver_unregister(&omap_control_phy_driver); |
01658f0f | 339 | } |
14da699b | 340 | module_exit(omap_control_phy_exit); |
01658f0f | 341 | |
dd64ad38 | 342 | MODULE_ALIAS("platform:omap_control_phy"); |
01658f0f | 343 | MODULE_AUTHOR("Texas Instruments Inc."); |
14da699b | 344 | MODULE_DESCRIPTION("OMAP Control Module PHY Driver"); |
01658f0f | 345 | MODULE_LICENSE("GPL v2"); |