diff options
author | Lubomir Rintel <lkundrak@v3.sk> | 2018-10-15 10:45:38 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-10-18 13:44:39 -0400 |
commit | 644930cbad32c0a850aaeed11eb2a49b492bf51a (patch) | |
tree | 3fb7855f47388fd6d6070a9e3dac4dbd69621db5 | |
parent | 89303c7ea770b6010943ef4ed73eb92bdc5a7ec8 (diff) |
phy: phy-pxa-usb: add a new driver
Turned from arch/arm/mach-mmp/devices.c into a proper PHY driver, so
that in can be instantiated from a DT.
Acked-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Lubomir Rintel <lkundrak@v3.sk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/phy/marvell/Kconfig | 11 | ||||
-rw-r--r-- | drivers/phy/marvell/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/marvell/phy-pxa-usb.c | 345 |
3 files changed, 357 insertions, 0 deletions
diff --git a/drivers/phy/marvell/Kconfig b/drivers/phy/marvell/Kconfig index 68e321225400..6fb4b56e4c14 100644 --- a/drivers/phy/marvell/Kconfig +++ b/drivers/phy/marvell/Kconfig | |||
@@ -59,3 +59,14 @@ config PHY_PXA_28NM_USB2 | |||
59 | The PHY driver will be used by Marvell udc/ehci/otg driver. | 59 | The PHY driver will be used by Marvell udc/ehci/otg driver. |
60 | 60 | ||
61 | To compile this driver as a module, choose M here. | 61 | To compile this driver as a module, choose M here. |
62 | |||
63 | config PHY_PXA_USB | ||
64 | tristate "Marvell PXA USB PHY Driver" | ||
65 | depends on ARCH_PXA || ARCH_MMP | ||
66 | select GENERIC_PHY | ||
67 | help | ||
68 | Enable this to support Marvell PXA USB PHY driver for Marvell | ||
69 | SoC. This driver will do the PHY initialization and shutdown. | ||
70 | The PHY driver will be used by Marvell udc/ehci/otg driver. | ||
71 | |||
72 | To compile this driver as a module, choose M here. | ||
diff --git a/drivers/phy/marvell/Makefile b/drivers/phy/marvell/Makefile index 5c3ec5d10e0d..3975b144f8ec 100644 --- a/drivers/phy/marvell/Makefile +++ b/drivers/phy/marvell/Makefile | |||
@@ -6,3 +6,4 @@ obj-$(CONFIG_PHY_MVEBU_CP110_COMPHY) += phy-mvebu-cp110-comphy.o | |||
6 | obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o | 6 | obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o |
7 | obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o | 7 | obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o |
8 | obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o | 8 | obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o |
9 | obj-$(CONFIG_PHY_PXA_USB) += phy-pxa-usb.o | ||
diff --git a/drivers/phy/marvell/phy-pxa-usb.c b/drivers/phy/marvell/phy-pxa-usb.c new file mode 100644 index 000000000000..87ff7550b912 --- /dev/null +++ b/drivers/phy/marvell/phy-pxa-usb.c | |||
@@ -0,0 +1,345 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (C) 2011 Marvell International Ltd. All rights reserved. | ||
4 | * Copyright (C) 2018 Lubomir Rintel <lkundrak@v3.sk> | ||
5 | */ | ||
6 | |||
7 | #include <dt-bindings/phy/phy.h> | ||
8 | #include <linux/clk.h> | ||
9 | #include <linux/delay.h> | ||
10 | #include <linux/io.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/of_address.h> | ||
13 | #include <linux/phy/phy.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | |||
16 | /* phy regs */ | ||
17 | #define UTMI_REVISION 0x0 | ||
18 | #define UTMI_CTRL 0x4 | ||
19 | #define UTMI_PLL 0x8 | ||
20 | #define UTMI_TX 0xc | ||
21 | #define UTMI_RX 0x10 | ||
22 | #define UTMI_IVREF 0x14 | ||
23 | #define UTMI_T0 0x18 | ||
24 | #define UTMI_T1 0x1c | ||
25 | #define UTMI_T2 0x20 | ||
26 | #define UTMI_T3 0x24 | ||
27 | #define UTMI_T4 0x28 | ||
28 | #define UTMI_T5 0x2c | ||
29 | #define UTMI_RESERVE 0x30 | ||
30 | #define UTMI_USB_INT 0x34 | ||
31 | #define UTMI_DBG_CTL 0x38 | ||
32 | #define UTMI_OTG_ADDON 0x3c | ||
33 | |||
34 | /* For UTMICTRL Register */ | ||
35 | #define UTMI_CTRL_USB_CLK_EN (1 << 31) | ||
36 | /* pxa168 */ | ||
37 | #define UTMI_CTRL_SUSPEND_SET1 (1 << 30) | ||
38 | #define UTMI_CTRL_SUSPEND_SET2 (1 << 29) | ||
39 | #define UTMI_CTRL_RXBUF_PDWN (1 << 24) | ||
40 | #define UTMI_CTRL_TXBUF_PDWN (1 << 11) | ||
41 | |||
42 | #define UTMI_CTRL_INPKT_DELAY_SHIFT 30 | ||
43 | #define UTMI_CTRL_INPKT_DELAY_SOF_SHIFT 28 | ||
44 | #define UTMI_CTRL_PU_REF_SHIFT 20 | ||
45 | #define UTMI_CTRL_ARC_PULLDN_SHIFT 12 | ||
46 | #define UTMI_CTRL_PLL_PWR_UP_SHIFT 1 | ||
47 | #define UTMI_CTRL_PWR_UP_SHIFT 0 | ||
48 | |||
49 | /* For UTMI_PLL Register */ | ||
50 | #define UTMI_PLL_PLLCALI12_SHIFT 29 | ||
51 | #define UTMI_PLL_PLLCALI12_MASK (0x3 << 29) | ||
52 | |||
53 | #define UTMI_PLL_PLLVDD18_SHIFT 27 | ||
54 | #define UTMI_PLL_PLLVDD18_MASK (0x3 << 27) | ||
55 | |||
56 | #define UTMI_PLL_PLLVDD12_SHIFT 25 | ||
57 | #define UTMI_PLL_PLLVDD12_MASK (0x3 << 25) | ||
58 | |||
59 | #define UTMI_PLL_CLK_BLK_EN_SHIFT 24 | ||
60 | #define CLK_BLK_EN (0x1 << 24) | ||
61 | #define PLL_READY (0x1 << 23) | ||
62 | #define KVCO_EXT (0x1 << 22) | ||
63 | #define VCOCAL_START (0x1 << 21) | ||
64 | |||
65 | #define UTMI_PLL_KVCO_SHIFT 15 | ||
66 | #define UTMI_PLL_KVCO_MASK (0x7 << 15) | ||
67 | |||
68 | #define UTMI_PLL_ICP_SHIFT 12 | ||
69 | #define UTMI_PLL_ICP_MASK (0x7 << 12) | ||
70 | |||
71 | #define UTMI_PLL_FBDIV_SHIFT 4 | ||
72 | #define UTMI_PLL_FBDIV_MASK (0xFF << 4) | ||
73 | |||
74 | #define UTMI_PLL_REFDIV_SHIFT 0 | ||
75 | #define UTMI_PLL_REFDIV_MASK (0xF << 0) | ||
76 | |||
77 | /* For UTMI_TX Register */ | ||
78 | #define UTMI_TX_REG_EXT_FS_RCAL_SHIFT 27 | ||
79 | #define UTMI_TX_REG_EXT_FS_RCAL_MASK (0xf << 27) | ||
80 | |||
81 | #define UTMI_TX_REG_EXT_FS_RCAL_EN_SHIFT 26 | ||
82 | #define UTMI_TX_REG_EXT_FS_RCAL_EN_MASK (0x1 << 26) | ||
83 | |||
84 | #define UTMI_TX_TXVDD12_SHIFT 22 | ||
85 | #define UTMI_TX_TXVDD12_MASK (0x3 << 22) | ||
86 | |||
87 | #define UTMI_TX_CK60_PHSEL_SHIFT 17 | ||
88 | #define UTMI_TX_CK60_PHSEL_MASK (0xf << 17) | ||
89 | |||
90 | #define UTMI_TX_IMPCAL_VTH_SHIFT 14 | ||
91 | #define UTMI_TX_IMPCAL_VTH_MASK (0x7 << 14) | ||
92 | |||
93 | #define REG_RCAL_START (0x1 << 12) | ||
94 | |||
95 | #define UTMI_TX_LOW_VDD_EN_SHIFT 11 | ||
96 | |||
97 | #define UTMI_TX_AMP_SHIFT 0 | ||
98 | #define UTMI_TX_AMP_MASK (0x7 << 0) | ||
99 | |||
100 | /* For UTMI_RX Register */ | ||
101 | #define UTMI_REG_SQ_LENGTH_SHIFT 15 | ||
102 | #define UTMI_REG_SQ_LENGTH_MASK (0x3 << 15) | ||
103 | |||
104 | #define UTMI_RX_SQ_THRESH_SHIFT 4 | ||
105 | #define UTMI_RX_SQ_THRESH_MASK (0xf << 4) | ||
106 | |||
107 | #define UTMI_OTG_ADDON_OTG_ON (1 << 0) | ||
108 | |||
109 | enum pxa_usb_phy_version { | ||
110 | PXA_USB_PHY_MMP2, | ||
111 | PXA_USB_PHY_PXA910, | ||
112 | PXA_USB_PHY_PXA168, | ||
113 | }; | ||
114 | |||
115 | struct pxa_usb_phy { | ||
116 | struct phy *phy; | ||
117 | void __iomem *base; | ||
118 | enum pxa_usb_phy_version version; | ||
119 | }; | ||
120 | |||
121 | /***************************************************************************** | ||
122 | * The registers read/write routines | ||
123 | *****************************************************************************/ | ||
124 | |||
125 | static unsigned int u2o_get(void __iomem *base, unsigned int offset) | ||
126 | { | ||
127 | return readl_relaxed(base + offset); | ||
128 | } | ||
129 | |||
130 | static void u2o_set(void __iomem *base, unsigned int offset, | ||
131 | unsigned int value) | ||
132 | { | ||
133 | u32 reg; | ||
134 | |||
135 | reg = readl_relaxed(base + offset); | ||
136 | reg |= value; | ||
137 | writel_relaxed(reg, base + offset); | ||
138 | readl_relaxed(base + offset); | ||
139 | } | ||
140 | |||
141 | static void u2o_clear(void __iomem *base, unsigned int offset, | ||
142 | unsigned int value) | ||
143 | { | ||
144 | u32 reg; | ||
145 | |||
146 | reg = readl_relaxed(base + offset); | ||
147 | reg &= ~value; | ||
148 | writel_relaxed(reg, base + offset); | ||
149 | readl_relaxed(base + offset); | ||
150 | } | ||
151 | |||
152 | static void u2o_write(void __iomem *base, unsigned int offset, | ||
153 | unsigned int value) | ||
154 | { | ||
155 | writel_relaxed(value, base + offset); | ||
156 | readl_relaxed(base + offset); | ||
157 | } | ||
158 | |||
159 | static int pxa_usb_phy_init(struct phy *phy) | ||
160 | { | ||
161 | struct pxa_usb_phy *pxa_usb_phy = phy_get_drvdata(phy); | ||
162 | void __iomem *base = pxa_usb_phy->base; | ||
163 | int loops; | ||
164 | |||
165 | dev_info(&phy->dev, "initializing Marvell PXA USB PHY"); | ||
166 | |||
167 | /* Initialize the USB PHY power */ | ||
168 | if (pxa_usb_phy->version == PXA_USB_PHY_PXA910) { | ||
169 | u2o_set(base, UTMI_CTRL, (1<<UTMI_CTRL_INPKT_DELAY_SOF_SHIFT) | ||
170 | | (1<<UTMI_CTRL_PU_REF_SHIFT)); | ||
171 | } | ||
172 | |||
173 | u2o_set(base, UTMI_CTRL, 1<<UTMI_CTRL_PLL_PWR_UP_SHIFT); | ||
174 | u2o_set(base, UTMI_CTRL, 1<<UTMI_CTRL_PWR_UP_SHIFT); | ||
175 | |||
176 | /* UTMI_PLL settings */ | ||
177 | u2o_clear(base, UTMI_PLL, UTMI_PLL_PLLVDD18_MASK | ||
178 | | UTMI_PLL_PLLVDD12_MASK | UTMI_PLL_PLLCALI12_MASK | ||
179 | | UTMI_PLL_FBDIV_MASK | UTMI_PLL_REFDIV_MASK | ||
180 | | UTMI_PLL_ICP_MASK | UTMI_PLL_KVCO_MASK); | ||
181 | |||
182 | u2o_set(base, UTMI_PLL, 0xee<<UTMI_PLL_FBDIV_SHIFT | ||
183 | | 0xb<<UTMI_PLL_REFDIV_SHIFT | 3<<UTMI_PLL_PLLVDD18_SHIFT | ||
184 | | 3<<UTMI_PLL_PLLVDD12_SHIFT | 3<<UTMI_PLL_PLLCALI12_SHIFT | ||
185 | | 1<<UTMI_PLL_ICP_SHIFT | 3<<UTMI_PLL_KVCO_SHIFT); | ||
186 | |||
187 | /* UTMI_TX */ | ||
188 | u2o_clear(base, UTMI_TX, UTMI_TX_REG_EXT_FS_RCAL_EN_MASK | ||
189 | | UTMI_TX_TXVDD12_MASK | UTMI_TX_CK60_PHSEL_MASK | ||
190 | | UTMI_TX_IMPCAL_VTH_MASK | UTMI_TX_REG_EXT_FS_RCAL_MASK | ||
191 | | UTMI_TX_AMP_MASK); | ||
192 | u2o_set(base, UTMI_TX, 3<<UTMI_TX_TXVDD12_SHIFT | ||
193 | | 4<<UTMI_TX_CK60_PHSEL_SHIFT | 4<<UTMI_TX_IMPCAL_VTH_SHIFT | ||
194 | | 8<<UTMI_TX_REG_EXT_FS_RCAL_SHIFT | 3<<UTMI_TX_AMP_SHIFT); | ||
195 | |||
196 | /* UTMI_RX */ | ||
197 | u2o_clear(base, UTMI_RX, UTMI_RX_SQ_THRESH_MASK | ||
198 | | UTMI_REG_SQ_LENGTH_MASK); | ||
199 | u2o_set(base, UTMI_RX, 7<<UTMI_RX_SQ_THRESH_SHIFT | ||
200 | | 2<<UTMI_REG_SQ_LENGTH_SHIFT); | ||
201 | |||
202 | /* UTMI_IVREF */ | ||
203 | if (pxa_usb_phy->version == PXA_USB_PHY_PXA168) { | ||
204 | /* | ||
205 | * fixing Microsoft Altair board interface with NEC hub issue - | ||
206 | * Set UTMI_IVREF from 0x4a3 to 0x4bf | ||
207 | */ | ||
208 | u2o_write(base, UTMI_IVREF, 0x4bf); | ||
209 | } | ||
210 | |||
211 | /* toggle VCOCAL_START bit of UTMI_PLL */ | ||
212 | udelay(200); | ||
213 | u2o_set(base, UTMI_PLL, VCOCAL_START); | ||
214 | udelay(40); | ||
215 | u2o_clear(base, UTMI_PLL, VCOCAL_START); | ||
216 | |||
217 | /* toggle REG_RCAL_START bit of UTMI_TX */ | ||
218 | udelay(400); | ||
219 | u2o_set(base, UTMI_TX, REG_RCAL_START); | ||
220 | udelay(40); | ||
221 | u2o_clear(base, UTMI_TX, REG_RCAL_START); | ||
222 | udelay(400); | ||
223 | |||
224 | /* Make sure PHY PLL is ready */ | ||
225 | loops = 0; | ||
226 | while ((u2o_get(base, UTMI_PLL) & PLL_READY) == 0) { | ||
227 | mdelay(1); | ||
228 | loops++; | ||
229 | if (loops > 100) { | ||
230 | dev_warn(&phy->dev, "calibrate timeout, UTMI_PLL %x\n", | ||
231 | u2o_get(base, UTMI_PLL)); | ||
232 | break; | ||
233 | } | ||
234 | } | ||
235 | |||
236 | if (pxa_usb_phy->version == PXA_USB_PHY_PXA168) { | ||
237 | u2o_set(base, UTMI_RESERVE, 1 << 5); | ||
238 | /* Turn on UTMI PHY OTG extension */ | ||
239 | u2o_write(base, UTMI_OTG_ADDON, 1); | ||
240 | } | ||
241 | |||
242 | return 0; | ||
243 | |||
244 | } | ||
245 | |||
246 | static int pxa_usb_phy_exit(struct phy *phy) | ||
247 | { | ||
248 | struct pxa_usb_phy *pxa_usb_phy = phy_get_drvdata(phy); | ||
249 | void __iomem *base = pxa_usb_phy->base; | ||
250 | |||
251 | dev_info(&phy->dev, "deinitializing Marvell PXA USB PHY"); | ||
252 | |||
253 | if (pxa_usb_phy->version == PXA_USB_PHY_PXA168) | ||
254 | u2o_clear(base, UTMI_OTG_ADDON, UTMI_OTG_ADDON_OTG_ON); | ||
255 | |||
256 | u2o_clear(base, UTMI_CTRL, UTMI_CTRL_RXBUF_PDWN); | ||
257 | u2o_clear(base, UTMI_CTRL, UTMI_CTRL_TXBUF_PDWN); | ||
258 | u2o_clear(base, UTMI_CTRL, UTMI_CTRL_USB_CLK_EN); | ||
259 | u2o_clear(base, UTMI_CTRL, 1<<UTMI_CTRL_PWR_UP_SHIFT); | ||
260 | u2o_clear(base, UTMI_CTRL, 1<<UTMI_CTRL_PLL_PWR_UP_SHIFT); | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | static const struct phy_ops pxa_usb_phy_ops = { | ||
266 | .init = pxa_usb_phy_init, | ||
267 | .exit = pxa_usb_phy_exit, | ||
268 | .owner = THIS_MODULE, | ||
269 | }; | ||
270 | |||
271 | static const struct of_device_id pxa_usb_phy_of_match[] = { | ||
272 | { | ||
273 | .compatible = "marvell,mmp2-usb-phy", | ||
274 | .data = (void *)PXA_USB_PHY_MMP2, | ||
275 | }, { | ||
276 | .compatible = "marvell,pxa910-usb-phy", | ||
277 | .data = (void *)PXA_USB_PHY_PXA910, | ||
278 | }, { | ||
279 | .compatible = "marvell,pxa168-usb-phy", | ||
280 | .data = (void *)PXA_USB_PHY_PXA168, | ||
281 | }, | ||
282 | { }, | ||
283 | }; | ||
284 | MODULE_DEVICE_TABLE(of, pxa_usb_phy_of_match); | ||
285 | |||
286 | static int pxa_usb_phy_probe(struct platform_device *pdev) | ||
287 | { | ||
288 | struct device *dev = &pdev->dev; | ||
289 | struct resource *resource; | ||
290 | struct pxa_usb_phy *pxa_usb_phy; | ||
291 | struct phy_provider *provider; | ||
292 | const struct of_device_id *of_id; | ||
293 | |||
294 | pxa_usb_phy = devm_kzalloc(dev, sizeof(struct pxa_usb_phy), GFP_KERNEL); | ||
295 | if (!pxa_usb_phy) | ||
296 | return -ENOMEM; | ||
297 | |||
298 | of_id = of_match_node(pxa_usb_phy_of_match, dev->of_node); | ||
299 | if (of_id) | ||
300 | pxa_usb_phy->version = (enum pxa_usb_phy_version)of_id->data; | ||
301 | else | ||
302 | pxa_usb_phy->version = PXA_USB_PHY_MMP2; | ||
303 | |||
304 | resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
305 | pxa_usb_phy->base = devm_ioremap_resource(dev, resource); | ||
306 | if (IS_ERR(pxa_usb_phy->base)) { | ||
307 | dev_err(dev, "failed to remap PHY regs\n"); | ||
308 | return PTR_ERR(pxa_usb_phy->base); | ||
309 | } | ||
310 | |||
311 | pxa_usb_phy->phy = devm_phy_create(dev, NULL, &pxa_usb_phy_ops); | ||
312 | if (IS_ERR(pxa_usb_phy->phy)) { | ||
313 | dev_err(dev, "failed to create PHY\n"); | ||
314 | return PTR_ERR(pxa_usb_phy->phy); | ||
315 | } | ||
316 | |||
317 | phy_set_drvdata(pxa_usb_phy->phy, pxa_usb_phy); | ||
318 | provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); | ||
319 | if (IS_ERR(provider)) { | ||
320 | dev_err(dev, "failed to register PHY provider\n"); | ||
321 | return PTR_ERR(provider); | ||
322 | } | ||
323 | |||
324 | if (!dev->of_node) { | ||
325 | phy_create_lookup(pxa_usb_phy->phy, "usb", "mv-udc"); | ||
326 | phy_create_lookup(pxa_usb_phy->phy, "usb", "pxa-u2oehci"); | ||
327 | phy_create_lookup(pxa_usb_phy->phy, "usb", "mv-otg"); | ||
328 | } | ||
329 | |||
330 | dev_info(dev, "Marvell PXA USB PHY"); | ||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | static struct platform_driver pxa_usb_phy_driver = { | ||
335 | .probe = pxa_usb_phy_probe, | ||
336 | .driver = { | ||
337 | .name = "pxa-usb-phy", | ||
338 | .of_match_table = pxa_usb_phy_of_match, | ||
339 | }, | ||
340 | }; | ||
341 | module_platform_driver(pxa_usb_phy_driver); | ||
342 | |||
343 | MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); | ||
344 | MODULE_DESCRIPTION("Marvell PXA USB PHY Driver"); | ||
345 | MODULE_LICENSE("GPL v2"); | ||