diff options
Diffstat (limited to 'drivers/usb/phy/phy-keystone.c')
-rw-r--r-- | drivers/usb/phy/phy-keystone.c | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c new file mode 100644 index 00000000000..b9a9c796270 --- /dev/null +++ b/drivers/usb/phy/phy-keystone.c | |||
@@ -0,0 +1,151 @@ | |||
1 | /* | ||
2 | * phy-keystone - USB PHY, talking to dwc3 controller in Keystone. | ||
3 | * | ||
4 | * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * Author: WingMan Kwok <w-kwok2@ti.com> | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/usb/otg.h> | ||
22 | #include <linux/io.h> | ||
23 | #include <linux/of.h> | ||
24 | |||
25 | /* USB PHY control register offsets */ | ||
26 | #define USB_PHY_CTL_UTMI 0x0000 | ||
27 | #define USB_PHY_CTL_PIPE 0x0004 | ||
28 | #define USB_PHY_CTL_PARAM_1 0x0008 | ||
29 | #define USB_PHY_CTL_PARAM_2 0x000c | ||
30 | #define USB_PHY_CTL_CLOCK 0x0010 | ||
31 | #define USB_PHY_CTL_PLL 0x0014 | ||
32 | |||
33 | #define PHY_REF_SSP_EN BIT(29) | ||
34 | |||
35 | struct keystone_usbphy { | ||
36 | struct usb_phy phy; | ||
37 | struct device *dev; | ||
38 | void __iomem *phy_ctrl; | ||
39 | }; | ||
40 | |||
41 | static inline u32 k_usbphy_readl(void __iomem *base, u32 offset) | ||
42 | { | ||
43 | return readl(base + offset); | ||
44 | } | ||
45 | |||
46 | static inline void k_usbphy_writel(void __iomem *base, | ||
47 | u32 offset, u32 value) | ||
48 | { | ||
49 | writel(value, base + offset); | ||
50 | } | ||
51 | |||
52 | static int k_usbphy_set_suspend(struct usb_phy *x, int suspend) | ||
53 | { | ||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | static int k_usbphy_init(struct usb_phy *phy) | ||
58 | { | ||
59 | struct keystone_usbphy *k_phy = dev_get_drvdata(phy->dev); | ||
60 | u32 val; | ||
61 | |||
62 | val = k_usbphy_readl(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK); | ||
63 | k_usbphy_writel(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK, | ||
64 | val | PHY_REF_SSP_EN); | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static void k_usbphy_shutdown(struct usb_phy *phy) | ||
69 | { | ||
70 | struct keystone_usbphy *k_phy = dev_get_drvdata(phy->dev); | ||
71 | u32 val; | ||
72 | |||
73 | val = k_usbphy_readl(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK); | ||
74 | k_usbphy_writel(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK, | ||
75 | val &= ~PHY_REF_SSP_EN); | ||
76 | } | ||
77 | |||
78 | static int k_usbphy_create_phy(struct device *dev, struct keystone_usbphy *kphy) | ||
79 | { | ||
80 | enum usb_phy_type type = USB_PHY_TYPE_USB2; | ||
81 | |||
82 | kphy->dev = dev; | ||
83 | kphy->phy.dev = kphy->dev; | ||
84 | kphy->phy.label = "k_usbphy-xceiv"; | ||
85 | kphy->phy.set_suspend = k_usbphy_set_suspend; | ||
86 | kphy->phy.init = k_usbphy_init; | ||
87 | kphy->phy.shutdown = k_usbphy_shutdown; | ||
88 | |||
89 | kphy->phy.state = OTG_STATE_UNDEFINED; | ||
90 | kphy->phy.type = type; | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static int k_usbphy_probe(struct platform_device *pdev) | ||
96 | { | ||
97 | struct device *dev = &pdev->dev; | ||
98 | struct keystone_usbphy *k_phy; | ||
99 | struct resource *res; | ||
100 | int ret; | ||
101 | |||
102 | k_phy = devm_kzalloc(dev, sizeof(*k_phy), GFP_KERNEL); | ||
103 | if (!k_phy) | ||
104 | return -ENOMEM; | ||
105 | |||
106 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
107 | k_phy->phy_ctrl = devm_ioremap_resource(dev, res); | ||
108 | if (IS_ERR(k_phy->phy_ctrl)) | ||
109 | return PTR_ERR(k_phy->phy_ctrl); | ||
110 | |||
111 | ret = k_usbphy_create_phy(dev, k_phy); | ||
112 | if (ret) | ||
113 | return ret; | ||
114 | |||
115 | platform_set_drvdata(pdev, k_phy); | ||
116 | |||
117 | ret = usb_add_phy_dev(&k_phy->phy); | ||
118 | return ret; | ||
119 | } | ||
120 | |||
121 | static int k_usbphy_remove(struct platform_device *pdev) | ||
122 | { | ||
123 | struct keystone_usbphy *k_phy = platform_get_drvdata(pdev); | ||
124 | |||
125 | usb_remove_phy(&k_phy->phy); | ||
126 | |||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static const struct of_device_id keystone_usbphy_ids[] = { | ||
131 | { .compatible = "ti,keystone-usbphy" }, | ||
132 | { } | ||
133 | }; | ||
134 | MODULE_DEVICE_TABLE(of, keystone_usbphy_ids); | ||
135 | |||
136 | static struct platform_driver keystone_usbphy_driver = { | ||
137 | .probe = k_usbphy_probe, | ||
138 | .remove = k_usbphy_remove, | ||
139 | .driver = { | ||
140 | .name = "keystone-usbphy", | ||
141 | .owner = THIS_MODULE, | ||
142 | .of_match_table = keystone_usbphy_ids, | ||
143 | }, | ||
144 | }; | ||
145 | |||
146 | module_platform_driver(keystone_usbphy_driver); | ||
147 | |||
148 | MODULE_ALIAS("platform:keystone-usbphy"); | ||
149 | MODULE_AUTHOR("WingMan Kwok"); | ||
150 | MODULE_DESCRIPTION("Keystone USB phy driver"); | ||
151 | MODULE_LICENSE("GPL v2"); | ||