1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * omap-control-phy.c - The PHY part of control module.
5 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
6 * Author: Kishon Vijay Abraham I <kishon@ti.com>
9 #include <linux/module.h>
10 #include <linux/platform_device.h>
11 #include <linux/slab.h>
13 #include <linux/of_device.h>
14 #include <linux/err.h>
16 #include <linux/clk.h>
17 #include <linux/phy/omap_control_phy.h>
20 * omap_control_pcie_pcs - set the PCS delay count
21 * @dev: the control module device
22 * @delay: 8 bit delay value
24 void omap_control_pcie_pcs(struct device
*dev
, u8 delay
)
27 struct omap_control_phy
*control_phy
;
29 if (IS_ERR(dev
) || !dev
) {
30 pr_err("%s: invalid device\n", __func__
);
34 control_phy
= dev_get_drvdata(dev
);
36 dev_err(dev
, "%s: invalid control phy device\n", __func__
);
40 if (control_phy
->type
!= OMAP_CTRL_TYPE_PCIE
) {
41 dev_err(dev
, "%s: unsupported operation\n", __func__
);
45 val
= readl(control_phy
->pcie_pcs
);
46 val
&= ~(OMAP_CTRL_PCIE_PCS_MASK
<<
47 OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT
);
48 val
|= (delay
<< OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT
);
49 writel(val
, control_phy
->pcie_pcs
);
51 EXPORT_SYMBOL_GPL(omap_control_pcie_pcs
);
54 * omap_control_phy_power - power on/off the phy using control module reg
55 * @dev: the control module device
56 * @on: 0 or 1, based on powering on or off the PHY
58 void omap_control_phy_power(struct device
*dev
, int on
)
62 struct omap_control_phy
*control_phy
;
64 if (IS_ERR(dev
) || !dev
) {
65 pr_err("%s: invalid device\n", __func__
);
69 control_phy
= dev_get_drvdata(dev
);
71 dev_err(dev
, "%s: invalid control phy device\n", __func__
);
75 if (control_phy
->type
== OMAP_CTRL_TYPE_OTGHS
)
78 val
= readl(control_phy
->power
);
80 switch (control_phy
->type
) {
81 case OMAP_CTRL_TYPE_USB2
:
83 val
&= ~OMAP_CTRL_DEV_PHY_PD
;
85 val
|= OMAP_CTRL_DEV_PHY_PD
;
88 case OMAP_CTRL_TYPE_PCIE
:
89 case OMAP_CTRL_TYPE_PIPE3
:
90 rate
= clk_get_rate(control_phy
->sys_clk
);
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
;
99 OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT
;
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
;
107 case OMAP_CTRL_TYPE_DRA7USB2
:
109 val
&= ~OMAP_CTRL_USB2_PHY_PD
;
111 val
|= OMAP_CTRL_USB2_PHY_PD
;
114 case OMAP_CTRL_TYPE_AM437USB2
:
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
);
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
);
128 dev_err(dev
, "%s: type %d not recognized\n",
129 __func__
, control_phy
->type
);
133 writel(val
, control_phy
->power
);
135 EXPORT_SYMBOL_GPL(omap_control_phy_power
);
138 * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded
139 * @ctrl_phy: struct omap_control_phy *
141 * Writes to the mailbox register to notify the usb core that a usb
142 * device has been connected.
144 static void omap_control_usb_host_mode(struct omap_control_phy
*ctrl_phy
)
148 val
= readl(ctrl_phy
->otghs_control
);
149 val
&= ~(OMAP_CTRL_DEV_IDDIG
| OMAP_CTRL_DEV_SESSEND
);
150 val
|= OMAP_CTRL_DEV_AVALID
| OMAP_CTRL_DEV_VBUSVALID
;
151 writel(val
, ctrl_phy
->otghs_control
);
155 * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high
157 * @ctrl_phy: struct omap_control_phy *
159 * Writes to the mailbox register to notify the usb core that it has been
160 * connected to a usb host.
162 static void omap_control_usb_device_mode(struct omap_control_phy
*ctrl_phy
)
166 val
= readl(ctrl_phy
->otghs_control
);
167 val
&= ~OMAP_CTRL_DEV_SESSEND
;
168 val
|= OMAP_CTRL_DEV_IDDIG
| OMAP_CTRL_DEV_AVALID
|
169 OMAP_CTRL_DEV_VBUSVALID
;
170 writel(val
, ctrl_phy
->otghs_control
);
174 * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high
176 * @ctrl_phy: struct omap_control_phy *
178 * Writes to the mailbox register to notify the usb core it's now in
179 * disconnected state.
181 static void omap_control_usb_set_sessionend(struct omap_control_phy
*ctrl_phy
)
185 val
= readl(ctrl_phy
->otghs_control
);
186 val
&= ~(OMAP_CTRL_DEV_AVALID
| OMAP_CTRL_DEV_VBUSVALID
);
187 val
|= OMAP_CTRL_DEV_IDDIG
| OMAP_CTRL_DEV_SESSEND
;
188 writel(val
, ctrl_phy
->otghs_control
);
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
197 * This is an API to write to the mailbox register to notify the usb core that
198 * a usb device has been connected.
200 void omap_control_usb_set_mode(struct device
*dev
,
201 enum omap_control_usb_mode mode
)
203 struct omap_control_phy
*ctrl_phy
;
205 if (IS_ERR(dev
) || !dev
)
208 ctrl_phy
= dev_get_drvdata(dev
);
210 dev_err(dev
, "Invalid control phy device\n");
214 if (ctrl_phy
->type
!= OMAP_CTRL_TYPE_OTGHS
)
219 omap_control_usb_host_mode(ctrl_phy
);
221 case USB_MODE_DEVICE
:
222 omap_control_usb_device_mode(ctrl_phy
);
224 case USB_MODE_DISCONNECT
:
225 omap_control_usb_set_sessionend(ctrl_phy
);
228 dev_vdbg(dev
, "invalid omap control usb mode\n");
231 EXPORT_SYMBOL_GPL(omap_control_usb_set_mode
);
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
;
236 static const enum omap_control_phy_type pcie_data
= OMAP_CTRL_TYPE_PCIE
;
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
;
240 static const struct of_device_id omap_control_phy_id_table
[] = {
242 .compatible
= "ti,control-phy-otghs",
246 .compatible
= "ti,control-phy-usb2",
250 .compatible
= "ti,control-phy-pipe3",
254 .compatible
= "ti,control-phy-pcie",
258 .compatible
= "ti,control-phy-usb2-dra7",
259 .data
= &dra7usb2_data
,
262 .compatible
= "ti,control-phy-usb2-am437",
263 .data
= &am437usb2_data
,
267 MODULE_DEVICE_TABLE(of
, omap_control_phy_id_table
);
269 static int omap_control_phy_probe(struct platform_device
*pdev
)
271 const struct of_device_id
*of_id
;
272 struct omap_control_phy
*control_phy
;
274 of_id
= of_match_device(omap_control_phy_id_table
, &pdev
->dev
);
278 control_phy
= devm_kzalloc(&pdev
->dev
, sizeof(*control_phy
),
283 control_phy
->dev
= &pdev
->dev
;
284 control_phy
->type
= *(enum omap_control_phy_type
*)of_id
->data
;
286 if (control_phy
->type
== OMAP_CTRL_TYPE_OTGHS
) {
287 control_phy
->otghs_control
=
288 devm_platform_ioremap_resource_byname(pdev
, "otghs_control");
289 if (IS_ERR(control_phy
->otghs_control
))
290 return PTR_ERR(control_phy
->otghs_control
);
293 devm_platform_ioremap_resource_byname(pdev
, "power");
294 if (IS_ERR(control_phy
->power
)) {
295 dev_err(&pdev
->dev
, "Couldn't get power register\n");
296 return PTR_ERR(control_phy
->power
);
300 if (control_phy
->type
== OMAP_CTRL_TYPE_PIPE3
||
301 control_phy
->type
== OMAP_CTRL_TYPE_PCIE
) {
302 control_phy
->sys_clk
= devm_clk_get(control_phy
->dev
,
304 if (IS_ERR(control_phy
->sys_clk
)) {
305 pr_err("%s: unable to get sys_clkin\n", __func__
);
310 if (control_phy
->type
== OMAP_CTRL_TYPE_PCIE
) {
311 control_phy
->pcie_pcs
=
312 devm_platform_ioremap_resource_byname(pdev
, "pcie_pcs");
313 if (IS_ERR(control_phy
->pcie_pcs
))
314 return PTR_ERR(control_phy
->pcie_pcs
);
317 dev_set_drvdata(control_phy
->dev
, control_phy
);
322 static struct platform_driver omap_control_phy_driver
= {
323 .probe
= omap_control_phy_probe
,
325 .name
= "omap-control-phy",
326 .of_match_table
= omap_control_phy_id_table
,
330 static int __init
omap_control_phy_init(void)
332 return platform_driver_register(&omap_control_phy_driver
);
334 subsys_initcall(omap_control_phy_init
);
336 static void __exit
omap_control_phy_exit(void)
338 platform_driver_unregister(&omap_control_phy_driver
);
340 module_exit(omap_control_phy_exit
);
342 MODULE_ALIAS("platform:omap_control_phy");
343 MODULE_AUTHOR("Texas Instruments Inc.");
344 MODULE_DESCRIPTION("OMAP Control Module PHY Driver");
345 MODULE_LICENSE("GPL v2");