diff options
author | Tony Lindgren <tony@atomide.com> | 2015-03-19 20:08:07 -0400 |
---|---|---|
committer | Kishon Vijay Abraham I <kishon@ti.com> | 2015-04-03 08:46:19 -0400 |
commit | 609adde838f4557f9d209b0432f4bac5c5eb5e86 (patch) | |
tree | d820b3b38807e33479f9b9491bc55cdd074baa4f /drivers/phy | |
parent | 52ea796b9161b1b81242e6831d9e128ee207ad2d (diff) |
phy: Add a driver for dm816x USB PHY
Add a minimal driver for dm816x USB. This makes USB work on dm816x
without any other changes needed as it can use the existing musb_dsps
glue layer for the USB controller.
Note that this phy is different from dm814x and am335x.
Cc: Bin Liu <binmlist@gmail.com>
Cc: Brian Hutchinson <b.hutchman@gmail.com>
Cc: Felipe Balbi <balbi@ti.com>
Cc: Matthijs van Duin <matthijsvanduin@gmail.com>
Cc: Paul Bolle <pebolle@tiscali.nl>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Diffstat (limited to 'drivers/phy')
-rw-r--r-- | drivers/phy/Kconfig | 7 | ||||
-rw-r--r-- | drivers/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/phy-dm816x-usb.c | 290 |
3 files changed, 298 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 2962de205ba7..885c39c28a8b 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig | |||
@@ -35,6 +35,13 @@ config ARMADA375_USBCLUSTER_PHY | |||
35 | depends on OF | 35 | depends on OF |
36 | select GENERIC_PHY | 36 | select GENERIC_PHY |
37 | 37 | ||
38 | config PHY_DM816X_USB | ||
39 | tristate "TI dm816x USB PHY driver" | ||
40 | depends on ARCH_OMAP2PLUS | ||
41 | select GENERIC_PHY | ||
42 | help | ||
43 | Enable this for dm816x USB to work. | ||
44 | |||
38 | config PHY_EXYNOS_MIPI_VIDEO | 45 | config PHY_EXYNOS_MIPI_VIDEO |
39 | tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" | 46 | tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" |
40 | depends on HAS_IOMEM | 47 | depends on HAS_IOMEM |
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index f080e1bb2a74..dab6665ad9c1 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile | |||
@@ -5,6 +5,7 @@ | |||
5 | obj-$(CONFIG_GENERIC_PHY) += phy-core.o | 5 | obj-$(CONFIG_GENERIC_PHY) += phy-core.o |
6 | obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o | 6 | obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o |
7 | obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o | 7 | obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o |
8 | obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o | ||
8 | obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o | 9 | obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o |
9 | obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o | 10 | obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o |
10 | obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o | 11 | obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o |
diff --git a/drivers/phy/phy-dm816x-usb.c b/drivers/phy/phy-dm816x-usb.c new file mode 100644 index 000000000000..7b42555ddd51 --- /dev/null +++ b/drivers/phy/phy-dm816x-usb.c | |||
@@ -0,0 +1,290 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or | ||
3 | * modify it under the terms of the GNU General Public License as | ||
4 | * published by the Free Software Foundation version 2. | ||
5 | * | ||
6 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
7 | * kind, whether express or implied; without even the implied warranty | ||
8 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
9 | * GNU General Public License for more details. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/regmap.h> | ||
15 | |||
16 | #include <linux/slab.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/io.h> | ||
19 | #include <linux/usb/phy_companion.h> | ||
20 | #include <linux/clk.h> | ||
21 | #include <linux/err.h> | ||
22 | #include <linux/pm_runtime.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/phy/phy.h> | ||
25 | #include <linux/of_platform.h> | ||
26 | |||
27 | #include <linux/mfd/syscon.h> | ||
28 | |||
29 | /* | ||
30 | * TRM has two sets of USB_CTRL registers.. The correct register bits | ||
31 | * are in TRM section 24.9.8.2 USB_CTRL Register. The TRM documents the | ||
32 | * phy as being SR70LX Synopsys USB 2.0 OTG nanoPHY. It also seems at | ||
33 | * least dm816x rev c ignores writes to USB_CTRL register, but the TI | ||
34 | * kernel is writing to those so it's possible that later revisions | ||
35 | * have worknig USB_CTRL register. | ||
36 | * | ||
37 | * Also note that At least USB_CTRL register seems to be dm816x specific | ||
38 | * according to the TRM. It's possible that USBPHY_CTRL is more generic, | ||
39 | * but that would have to be checked against the SR70LX documentation | ||
40 | * which does not seem to be publicly available. | ||
41 | * | ||
42 | * Finally, the phy on dm814x and am335x is different from dm816x. | ||
43 | */ | ||
44 | #define DM816X_USB_CTRL_PHYCLKSRC BIT(8) /* 1 = PLL ref clock */ | ||
45 | #define DM816X_USB_CTRL_PHYSLEEP1 BIT(1) /* Enable the first phy */ | ||
46 | #define DM816X_USB_CTRL_PHYSLEEP0 BIT(0) /* Enable the second phy */ | ||
47 | |||
48 | #define DM816X_USBPHY_CTRL_TXRISETUNE 1 | ||
49 | #define DM816X_USBPHY_CTRL_TXVREFTUNE 0xc | ||
50 | #define DM816X_USBPHY_CTRL_TXPREEMTUNE 0x2 | ||
51 | |||
52 | struct dm816x_usb_phy { | ||
53 | struct regmap *syscon; | ||
54 | struct device *dev; | ||
55 | unsigned int instance; | ||
56 | struct clk *refclk; | ||
57 | struct usb_phy phy; | ||
58 | unsigned int usb_ctrl; /* Shared between phy0 and phy1 */ | ||
59 | unsigned int usbphy_ctrl; | ||
60 | }; | ||
61 | |||
62 | static int dm816x_usb_phy_set_host(struct usb_otg *otg, struct usb_bus *host) | ||
63 | { | ||
64 | otg->host = host; | ||
65 | if (!host) | ||
66 | otg->state = OTG_STATE_UNDEFINED; | ||
67 | |||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | static int dm816x_usb_phy_set_peripheral(struct usb_otg *otg, | ||
72 | struct usb_gadget *gadget) | ||
73 | { | ||
74 | otg->gadget = gadget; | ||
75 | if (!gadget) | ||
76 | otg->state = OTG_STATE_UNDEFINED; | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | static int dm816x_usb_phy_init(struct phy *x) | ||
82 | { | ||
83 | struct dm816x_usb_phy *phy = phy_get_drvdata(x); | ||
84 | unsigned int val; | ||
85 | int error; | ||
86 | |||
87 | if (clk_get_rate(phy->refclk) != 24000000) | ||
88 | dev_warn(phy->dev, "nonstandard phy refclk\n"); | ||
89 | |||
90 | /* Set PLL ref clock and put phys to sleep */ | ||
91 | error = regmap_update_bits(phy->syscon, phy->usb_ctrl, | ||
92 | DM816X_USB_CTRL_PHYCLKSRC | | ||
93 | DM816X_USB_CTRL_PHYSLEEP1 | | ||
94 | DM816X_USB_CTRL_PHYSLEEP0, | ||
95 | 0); | ||
96 | regmap_read(phy->syscon, phy->usb_ctrl, &val); | ||
97 | if ((val & 3) != 0) | ||
98 | dev_info(phy->dev, | ||
99 | "Working dm816x USB_CTRL! (0x%08x)\n", | ||
100 | val); | ||
101 | |||
102 | /* | ||
103 | * TI kernel sets these values for "symmetrical eye diagram and | ||
104 | * better signal quality" so let's assume somebody checked the | ||
105 | * values with a scope and set them here too. | ||
106 | */ | ||
107 | regmap_read(phy->syscon, phy->usbphy_ctrl, &val); | ||
108 | val |= DM816X_USBPHY_CTRL_TXRISETUNE | | ||
109 | DM816X_USBPHY_CTRL_TXVREFTUNE | | ||
110 | DM816X_USBPHY_CTRL_TXPREEMTUNE; | ||
111 | regmap_write(phy->syscon, phy->usbphy_ctrl, val); | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | static struct phy_ops ops = { | ||
117 | .init = dm816x_usb_phy_init, | ||
118 | .owner = THIS_MODULE, | ||
119 | }; | ||
120 | |||
121 | static int dm816x_usb_phy_runtime_suspend(struct device *dev) | ||
122 | { | ||
123 | struct dm816x_usb_phy *phy = dev_get_drvdata(dev); | ||
124 | unsigned int mask, val; | ||
125 | int error = 0; | ||
126 | |||
127 | mask = BIT(phy->instance); | ||
128 | val = ~BIT(phy->instance); | ||
129 | error = regmap_update_bits(phy->syscon, phy->usb_ctrl, | ||
130 | mask, val); | ||
131 | if (error) | ||
132 | dev_err(phy->dev, "phy%i failed to power off\n", | ||
133 | phy->instance); | ||
134 | clk_disable(phy->refclk); | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | static int dm816x_usb_phy_runtime_resume(struct device *dev) | ||
140 | { | ||
141 | struct dm816x_usb_phy *phy = dev_get_drvdata(dev); | ||
142 | unsigned int mask, val; | ||
143 | int error; | ||
144 | |||
145 | error = clk_enable(phy->refclk); | ||
146 | if (error) | ||
147 | return error; | ||
148 | |||
149 | /* | ||
150 | * Note that at least dm816x rev c does not seem to do | ||
151 | * anything with the USB_CTRL register. But let's follow | ||
152 | * what the TI tree is doing in case later revisions use | ||
153 | * USB_CTRL. | ||
154 | */ | ||
155 | mask = BIT(phy->instance); | ||
156 | val = BIT(phy->instance); | ||
157 | error = regmap_update_bits(phy->syscon, phy->usb_ctrl, | ||
158 | mask, val); | ||
159 | if (error) { | ||
160 | dev_err(phy->dev, "phy%i failed to power on\n", | ||
161 | phy->instance); | ||
162 | clk_disable(phy->refclk); | ||
163 | return error; | ||
164 | } | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static UNIVERSAL_DEV_PM_OPS(dm816x_usb_phy_pm_ops, | ||
170 | dm816x_usb_phy_runtime_suspend, | ||
171 | dm816x_usb_phy_runtime_resume, | ||
172 | NULL); | ||
173 | |||
174 | #ifdef CONFIG_OF | ||
175 | static const struct of_device_id dm816x_usb_phy_id_table[] = { | ||
176 | { | ||
177 | .compatible = "ti,dm8168-usb-phy", | ||
178 | }, | ||
179 | {}, | ||
180 | }; | ||
181 | MODULE_DEVICE_TABLE(of, dm816x_usb_phy_id_table); | ||
182 | #endif | ||
183 | |||
184 | static int dm816x_usb_phy_probe(struct platform_device *pdev) | ||
185 | { | ||
186 | struct dm816x_usb_phy *phy; | ||
187 | struct resource *res; | ||
188 | struct phy *generic_phy; | ||
189 | struct phy_provider *phy_provider; | ||
190 | struct usb_otg *otg; | ||
191 | const struct of_device_id *of_id; | ||
192 | const struct usb_phy_data *phy_data; | ||
193 | int error; | ||
194 | |||
195 | of_id = of_match_device(of_match_ptr(dm816x_usb_phy_id_table), | ||
196 | &pdev->dev); | ||
197 | if (!of_id) | ||
198 | return -EINVAL; | ||
199 | |||
200 | phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); | ||
201 | if (!phy) | ||
202 | return -ENOMEM; | ||
203 | |||
204 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
205 | if (!res) | ||
206 | return -ENOENT; | ||
207 | |||
208 | phy->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, | ||
209 | "syscon"); | ||
210 | if (IS_ERR(phy->syscon)) | ||
211 | return PTR_ERR(phy->syscon); | ||
212 | |||
213 | /* | ||
214 | * According to sprs614e.pdf, the first usb_ctrl is shared and | ||
215 | * the second instance for usb_ctrl is reserved.. Also the | ||
216 | * register bits are different from earlier TRMs. | ||
217 | */ | ||
218 | phy->usb_ctrl = 0x20; | ||
219 | phy->usbphy_ctrl = (res->start & 0xff) + 4; | ||
220 | if (phy->usbphy_ctrl == 0x2c) | ||
221 | phy->instance = 1; | ||
222 | |||
223 | phy_data = of_id->data; | ||
224 | |||
225 | otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); | ||
226 | if (!otg) | ||
227 | return -ENOMEM; | ||
228 | |||
229 | phy->dev = &pdev->dev; | ||
230 | phy->phy.dev = phy->dev; | ||
231 | phy->phy.label = "dm8168_usb_phy"; | ||
232 | phy->phy.otg = otg; | ||
233 | phy->phy.type = USB_PHY_TYPE_USB2; | ||
234 | otg->set_host = dm816x_usb_phy_set_host; | ||
235 | otg->set_peripheral = dm816x_usb_phy_set_peripheral; | ||
236 | otg->usb_phy = &phy->phy; | ||
237 | |||
238 | platform_set_drvdata(pdev, phy); | ||
239 | |||
240 | phy->refclk = devm_clk_get(phy->dev, "refclk"); | ||
241 | if (IS_ERR(phy->refclk)) | ||
242 | return PTR_ERR(phy->refclk); | ||
243 | error = clk_prepare(phy->refclk); | ||
244 | if (error) | ||
245 | return error; | ||
246 | |||
247 | pm_runtime_enable(phy->dev); | ||
248 | generic_phy = devm_phy_create(phy->dev, NULL, &ops); | ||
249 | if (IS_ERR(generic_phy)) | ||
250 | return PTR_ERR(generic_phy); | ||
251 | |||
252 | phy_set_drvdata(generic_phy, phy); | ||
253 | |||
254 | phy_provider = devm_of_phy_provider_register(phy->dev, | ||
255 | of_phy_simple_xlate); | ||
256 | if (IS_ERR(phy_provider)) | ||
257 | return PTR_ERR(phy_provider); | ||
258 | |||
259 | usb_add_phy_dev(&phy->phy); | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int dm816x_usb_phy_remove(struct platform_device *pdev) | ||
265 | { | ||
266 | struct dm816x_usb_phy *phy = platform_get_drvdata(pdev); | ||
267 | |||
268 | usb_remove_phy(&phy->phy); | ||
269 | pm_runtime_disable(phy->dev); | ||
270 | clk_unprepare(phy->refclk); | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static struct platform_driver dm816x_usb_phy_driver = { | ||
276 | .probe = dm816x_usb_phy_probe, | ||
277 | .remove = dm816x_usb_phy_remove, | ||
278 | .driver = { | ||
279 | .name = "dm816x-usb-phy", | ||
280 | .pm = &dm816x_usb_phy_pm_ops, | ||
281 | .of_match_table = of_match_ptr(dm816x_usb_phy_id_table), | ||
282 | }, | ||
283 | }; | ||
284 | |||
285 | module_platform_driver(dm816x_usb_phy_driver); | ||
286 | |||
287 | MODULE_ALIAS("platform:dm816x_usb"); | ||
288 | MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); | ||
289 | MODULE_DESCRIPTION("dm816x usb phy driver"); | ||
290 | MODULE_LICENSE("GPL v2"); | ||