1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | |
3 | #include <linux/module.h> |
4 | #include <linux/platform_device.h> |
5 | #include <linux/regmap.h> |
6 | |
7 | #include <linux/slab.h> |
8 | #include <linux/of.h> |
9 | #include <linux/io.h> |
10 | #include <linux/usb/phy_companion.h> |
11 | #include <linux/clk.h> |
12 | #include <linux/err.h> |
13 | #include <linux/pm_runtime.h> |
14 | #include <linux/delay.h> |
15 | #include <linux/phy/phy.h> |
16 | |
17 | #include <linux/mfd/syscon.h> |
18 | |
19 | /* |
20 | * TRM has two sets of USB_CTRL registers.. The correct register bits |
21 | * are in TRM section 24.9.8.2 USB_CTRL Register. The TRM documents the |
22 | * phy as being SR70LX Synopsys USB 2.0 OTG nanoPHY. It also seems at |
23 | * least dm816x rev c ignores writes to USB_CTRL register, but the TI |
24 | * kernel is writing to those so it's possible that later revisions |
25 | * have worknig USB_CTRL register. |
26 | * |
27 | * Also note that At least USB_CTRL register seems to be dm816x specific |
28 | * according to the TRM. It's possible that USBPHY_CTRL is more generic, |
29 | * but that would have to be checked against the SR70LX documentation |
30 | * which does not seem to be publicly available. |
31 | * |
32 | * Finally, the phy on dm814x and am335x is different from dm816x. |
33 | */ |
34 | #define DM816X_USB_CTRL_PHYCLKSRC BIT(8) /* 1 = PLL ref clock */ |
35 | #define DM816X_USB_CTRL_PHYSLEEP1 BIT(1) /* Enable the first phy */ |
36 | #define DM816X_USB_CTRL_PHYSLEEP0 BIT(0) /* Enable the second phy */ |
37 | |
38 | #define DM816X_USBPHY_CTRL_TXRISETUNE 1 |
39 | #define DM816X_USBPHY_CTRL_TXVREFTUNE 0xc |
40 | #define DM816X_USBPHY_CTRL_TXPREEMTUNE 0x2 |
41 | |
42 | struct dm816x_usb_phy { |
43 | struct regmap *syscon; |
44 | struct device *dev; |
45 | unsigned int instance; |
46 | struct clk *refclk; |
47 | struct usb_phy phy; |
48 | unsigned int usb_ctrl; /* Shared between phy0 and phy1 */ |
49 | unsigned int usbphy_ctrl; |
50 | }; |
51 | |
52 | static int dm816x_usb_phy_set_host(struct usb_otg *otg, struct usb_bus *host) |
53 | { |
54 | otg->host = host; |
55 | if (!host) |
56 | otg->state = OTG_STATE_UNDEFINED; |
57 | |
58 | return 0; |
59 | } |
60 | |
61 | static int dm816x_usb_phy_set_peripheral(struct usb_otg *otg, |
62 | struct usb_gadget *gadget) |
63 | { |
64 | otg->gadget = gadget; |
65 | if (!gadget) |
66 | otg->state = OTG_STATE_UNDEFINED; |
67 | |
68 | return 0; |
69 | } |
70 | |
71 | static int dm816x_usb_phy_init(struct phy *x) |
72 | { |
73 | struct dm816x_usb_phy *phy = phy_get_drvdata(phy: x); |
74 | unsigned int val; |
75 | |
76 | if (clk_get_rate(clk: phy->refclk) != 24000000) |
77 | dev_warn(phy->dev, "nonstandard phy refclk\n" ); |
78 | |
79 | /* Set PLL ref clock and put phys to sleep */ |
80 | regmap_update_bits(map: phy->syscon, reg: phy->usb_ctrl, |
81 | DM816X_USB_CTRL_PHYCLKSRC | |
82 | DM816X_USB_CTRL_PHYSLEEP1 | |
83 | DM816X_USB_CTRL_PHYSLEEP0, |
84 | val: 0); |
85 | regmap_read(map: phy->syscon, reg: phy->usb_ctrl, val: &val); |
86 | if ((val & 3) != 0) |
87 | dev_info(phy->dev, |
88 | "Working dm816x USB_CTRL! (0x%08x)\n" , |
89 | val); |
90 | |
91 | /* |
92 | * TI kernel sets these values for "symmetrical eye diagram and |
93 | * better signal quality" so let's assume somebody checked the |
94 | * values with a scope and set them here too. |
95 | */ |
96 | regmap_read(map: phy->syscon, reg: phy->usbphy_ctrl, val: &val); |
97 | val |= DM816X_USBPHY_CTRL_TXRISETUNE | |
98 | DM816X_USBPHY_CTRL_TXVREFTUNE | |
99 | DM816X_USBPHY_CTRL_TXPREEMTUNE; |
100 | regmap_write(map: phy->syscon, reg: phy->usbphy_ctrl, val); |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | static const struct phy_ops ops = { |
106 | .init = dm816x_usb_phy_init, |
107 | .owner = THIS_MODULE, |
108 | }; |
109 | |
110 | static int __maybe_unused dm816x_usb_phy_runtime_suspend(struct device *dev) |
111 | { |
112 | struct dm816x_usb_phy *phy = dev_get_drvdata(dev); |
113 | unsigned int mask, val; |
114 | int error = 0; |
115 | |
116 | mask = BIT(phy->instance); |
117 | val = ~BIT(phy->instance); |
118 | error = regmap_update_bits(map: phy->syscon, reg: phy->usb_ctrl, |
119 | mask, val); |
120 | if (error) |
121 | dev_err(phy->dev, "phy%i failed to power off\n" , |
122 | phy->instance); |
123 | clk_disable(clk: phy->refclk); |
124 | |
125 | return 0; |
126 | } |
127 | |
128 | static int __maybe_unused dm816x_usb_phy_runtime_resume(struct device *dev) |
129 | { |
130 | struct dm816x_usb_phy *phy = dev_get_drvdata(dev); |
131 | unsigned int mask, val; |
132 | int error; |
133 | |
134 | error = clk_enable(clk: phy->refclk); |
135 | if (error) |
136 | return error; |
137 | |
138 | /* |
139 | * Note that at least dm816x rev c does not seem to do |
140 | * anything with the USB_CTRL register. But let's follow |
141 | * what the TI tree is doing in case later revisions use |
142 | * USB_CTRL. |
143 | */ |
144 | mask = BIT(phy->instance); |
145 | val = BIT(phy->instance); |
146 | error = regmap_update_bits(map: phy->syscon, reg: phy->usb_ctrl, |
147 | mask, val); |
148 | if (error) { |
149 | dev_err(phy->dev, "phy%i failed to power on\n" , |
150 | phy->instance); |
151 | clk_disable(clk: phy->refclk); |
152 | return error; |
153 | } |
154 | |
155 | return 0; |
156 | } |
157 | |
158 | static UNIVERSAL_DEV_PM_OPS(dm816x_usb_phy_pm_ops, |
159 | dm816x_usb_phy_runtime_suspend, |
160 | dm816x_usb_phy_runtime_resume, |
161 | NULL); |
162 | |
163 | static const struct of_device_id dm816x_usb_phy_id_table[] = { |
164 | { |
165 | .compatible = "ti,dm8168-usb-phy" , |
166 | }, |
167 | {}, |
168 | }; |
169 | MODULE_DEVICE_TABLE(of, dm816x_usb_phy_id_table); |
170 | |
171 | static int dm816x_usb_phy_probe(struct platform_device *pdev) |
172 | { |
173 | struct dm816x_usb_phy *phy; |
174 | struct resource *res; |
175 | struct phy *generic_phy; |
176 | struct phy_provider *phy_provider; |
177 | struct usb_otg *otg; |
178 | int error; |
179 | |
180 | phy = devm_kzalloc(dev: &pdev->dev, size: sizeof(*phy), GFP_KERNEL); |
181 | if (!phy) |
182 | return -ENOMEM; |
183 | |
184 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
185 | if (!res) |
186 | return -ENOENT; |
187 | |
188 | phy->syscon = syscon_regmap_lookup_by_phandle(np: pdev->dev.of_node, |
189 | property: "syscon" ); |
190 | if (IS_ERR(ptr: phy->syscon)) |
191 | return PTR_ERR(ptr: phy->syscon); |
192 | |
193 | /* |
194 | * According to sprs614e.pdf, the first usb_ctrl is shared and |
195 | * the second instance for usb_ctrl is reserved.. Also the |
196 | * register bits are different from earlier TRMs. |
197 | */ |
198 | phy->usb_ctrl = 0x20; |
199 | phy->usbphy_ctrl = (res->start & 0xff) + 4; |
200 | if (phy->usbphy_ctrl == 0x2c) |
201 | phy->instance = 1; |
202 | |
203 | otg = devm_kzalloc(dev: &pdev->dev, size: sizeof(*otg), GFP_KERNEL); |
204 | if (!otg) |
205 | return -ENOMEM; |
206 | |
207 | phy->dev = &pdev->dev; |
208 | phy->phy.dev = phy->dev; |
209 | phy->phy.label = "dm8168_usb_phy" ; |
210 | phy->phy.otg = otg; |
211 | phy->phy.type = USB_PHY_TYPE_USB2; |
212 | otg->set_host = dm816x_usb_phy_set_host; |
213 | otg->set_peripheral = dm816x_usb_phy_set_peripheral; |
214 | otg->usb_phy = &phy->phy; |
215 | |
216 | platform_set_drvdata(pdev, data: phy); |
217 | |
218 | phy->refclk = devm_clk_get(dev: phy->dev, id: "refclk" ); |
219 | if (IS_ERR(ptr: phy->refclk)) |
220 | return PTR_ERR(ptr: phy->refclk); |
221 | error = clk_prepare(clk: phy->refclk); |
222 | if (error) |
223 | return error; |
224 | |
225 | pm_runtime_enable(dev: phy->dev); |
226 | generic_phy = devm_phy_create(dev: phy->dev, NULL, ops: &ops); |
227 | if (IS_ERR(ptr: generic_phy)) { |
228 | error = PTR_ERR(ptr: generic_phy); |
229 | goto clk_unprepare; |
230 | } |
231 | |
232 | phy_set_drvdata(phy: generic_phy, data: phy); |
233 | |
234 | phy_provider = devm_of_phy_provider_register(phy->dev, |
235 | of_phy_simple_xlate); |
236 | if (IS_ERR(ptr: phy_provider)) { |
237 | error = PTR_ERR(ptr: phy_provider); |
238 | goto clk_unprepare; |
239 | } |
240 | |
241 | usb_add_phy_dev(&phy->phy); |
242 | |
243 | return 0; |
244 | |
245 | clk_unprepare: |
246 | pm_runtime_disable(dev: phy->dev); |
247 | clk_unprepare(clk: phy->refclk); |
248 | return error; |
249 | } |
250 | |
251 | static void dm816x_usb_phy_remove(struct platform_device *pdev) |
252 | { |
253 | struct dm816x_usb_phy *phy = platform_get_drvdata(pdev); |
254 | |
255 | usb_remove_phy(&phy->phy); |
256 | pm_runtime_disable(dev: phy->dev); |
257 | clk_unprepare(clk: phy->refclk); |
258 | } |
259 | |
260 | static struct platform_driver dm816x_usb_phy_driver = { |
261 | .probe = dm816x_usb_phy_probe, |
262 | .remove_new = dm816x_usb_phy_remove, |
263 | .driver = { |
264 | .name = "dm816x-usb-phy" , |
265 | .pm = &dm816x_usb_phy_pm_ops, |
266 | .of_match_table = dm816x_usb_phy_id_table, |
267 | }, |
268 | }; |
269 | |
270 | module_platform_driver(dm816x_usb_phy_driver); |
271 | |
272 | MODULE_ALIAS("platform:dm816x_usb" ); |
273 | MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>" ); |
274 | MODULE_DESCRIPTION("dm816x usb phy driver" ); |
275 | MODULE_LICENSE("GPL v2" ); |
276 | |