Blame view

drivers/usb/chipidea/core.c 20 KB
e443b3336   Alexander Shishkin   usb: chipidea: sp...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  /*
   * core.c - ChipIdea USB IP core family device controller
   *
   * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
   *
   * Author: David Lopo
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
  
  /*
   * Description: ChipIdea USB IP core family device controller
   *
   * This driver is composed of several blocks:
   * - HW:     hardware interface
   * - DBG:    debug facilities (optional)
   * - UTIL:   utilities
   * - ISR:    interrupts handling
   * - ENDPT:  endpoint operations (Gadget API)
   * - GADGET: gadget operations (Gadget API)
   * - BUS:    bus glue code, bus abstraction layer
   *
   * Compile Options
58ce8499d   Peter Chen   usb: chipidea: up...
26
   * - CONFIG_USB_CHIPIDEA_DEBUG: enable debug facilities
e443b3336   Alexander Shishkin   usb: chipidea: sp...
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
   * - STALL_IN:  non-empty bulk-in pipes cannot be halted
   *              if defined mass storage compliance succeeds but with warnings
   *              => case 4: Hi >  Dn
   *              => case 5: Hi >  Di
   *              => case 8: Hi <> Do
   *              if undefined usbtest 13 fails
   * - TRACE:     enable function tracing (depends on DEBUG)
   *
   * Main Features
   * - Chapter 9 & Mass Storage Compliance with Gadget File Storage
   * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined)
   * - Normal & LPM support
   *
   * USBTEST Report
   * - OK: 0-12, 13 (STALL_IN defined) & 14
   * - Not Supported: 15 & 16 (ISO)
   *
   * TODO List
e443b3336   Alexander Shishkin   usb: chipidea: sp...
45
46
47
48
   * - Suspend & Remote Wakeup
   */
  #include <linux/delay.h>
  #include <linux/device.h>
e443b3336   Alexander Shishkin   usb: chipidea: sp...
49
  #include <linux/dma-mapping.h>
1e5e2d3d0   Antoine Tenart   usb: chipidea: ad...
50
  #include <linux/phy/phy.h>
e443b3336   Alexander Shishkin   usb: chipidea: sp...
51
52
  #include <linux/platform_device.h>
  #include <linux/module.h>
fe6e125e3   Richard Zhao   USB: Chipidea: ad...
53
  #include <linux/idr.h>
e443b3336   Alexander Shishkin   usb: chipidea: sp...
54
55
  #include <linux/interrupt.h>
  #include <linux/io.h>
e443b3336   Alexander Shishkin   usb: chipidea: sp...
56
57
58
59
60
61
62
  #include <linux/kernel.h>
  #include <linux/slab.h>
  #include <linux/pm_runtime.h>
  #include <linux/usb/ch9.h>
  #include <linux/usb/gadget.h>
  #include <linux/usb/otg.h>
  #include <linux/usb/chipidea.h>
40dcd0e80   Michael Grzeschik   usb: chipidea: ad...
63
  #include <linux/usb/of.h>
4f6743d5c   Michael Grzeschik   usb: chipidea: ud...
64
  #include <linux/of.h>
40dcd0e80   Michael Grzeschik   usb: chipidea: ad...
65
  #include <linux/phy.h>
1542d9c35   Peter Chen   usb: chipidea: mo...
66
  #include <linux/regulator/consumer.h>
e443b3336   Alexander Shishkin   usb: chipidea: sp...
67
68
69
70
  
  #include "ci.h"
  #include "udc.h"
  #include "bits.h"
eb70e5ab8   Alexander Shishkin   usb: chipidea: ad...
71
  #include "host.h"
e443b3336   Alexander Shishkin   usb: chipidea: sp...
72
  #include "debug.h"
c10b4f033   Peter Chen   usb: chipidea: ot...
73
  #include "otg.h"
4dcf720c5   Li Jun   usb: chipidea: OT...
74
  #include "otg_fsm.h"
e443b3336   Alexander Shishkin   usb: chipidea: sp...
75

5f36e231e   Alexander Shishkin   usb: chipidea: ad...
76
  /* Controller register map */
987e7bc34   Marc Kleine-Budde   usb: chipidea: ma...
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  static const u8 ci_regs_nolpm[] = {
  	[CAP_CAPLENGTH]		= 0x00U,
  	[CAP_HCCPARAMS]		= 0x08U,
  	[CAP_DCCPARAMS]		= 0x24U,
  	[CAP_TESTMODE]		= 0x38U,
  	[OP_USBCMD]		= 0x00U,
  	[OP_USBSTS]		= 0x04U,
  	[OP_USBINTR]		= 0x08U,
  	[OP_DEVICEADDR]		= 0x14U,
  	[OP_ENDPTLISTADDR]	= 0x18U,
  	[OP_PORTSC]		= 0x44U,
  	[OP_DEVLC]		= 0x84U,
  	[OP_OTGSC]		= 0x64U,
  	[OP_USBMODE]		= 0x68U,
  	[OP_ENDPTSETUPSTAT]	= 0x6CU,
  	[OP_ENDPTPRIME]		= 0x70U,
  	[OP_ENDPTFLUSH]		= 0x74U,
  	[OP_ENDPTSTAT]		= 0x78U,
  	[OP_ENDPTCOMPLETE]	= 0x7CU,
  	[OP_ENDPTCTRL]		= 0x80U,
e443b3336   Alexander Shishkin   usb: chipidea: sp...
97
  };
987e7bc34   Marc Kleine-Budde   usb: chipidea: ma...
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
  static const u8 ci_regs_lpm[] = {
  	[CAP_CAPLENGTH]		= 0x00U,
  	[CAP_HCCPARAMS]		= 0x08U,
  	[CAP_DCCPARAMS]		= 0x24U,
  	[CAP_TESTMODE]		= 0xFCU,
  	[OP_USBCMD]		= 0x00U,
  	[OP_USBSTS]		= 0x04U,
  	[OP_USBINTR]		= 0x08U,
  	[OP_DEVICEADDR]		= 0x14U,
  	[OP_ENDPTLISTADDR]	= 0x18U,
  	[OP_PORTSC]		= 0x44U,
  	[OP_DEVLC]		= 0x84U,
  	[OP_OTGSC]		= 0xC4U,
  	[OP_USBMODE]		= 0xC8U,
  	[OP_ENDPTSETUPSTAT]	= 0xD8U,
  	[OP_ENDPTPRIME]		= 0xDCU,
  	[OP_ENDPTFLUSH]		= 0xE0U,
  	[OP_ENDPTSTAT]		= 0xE4U,
  	[OP_ENDPTCOMPLETE]	= 0xE8U,
  	[OP_ENDPTCTRL]		= 0xECU,
e443b3336   Alexander Shishkin   usb: chipidea: sp...
118
  };
8e22978c5   Alexander Shishkin   usb: chipidea: dr...
119
  static int hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm)
e443b3336   Alexander Shishkin   usb: chipidea: sp...
120
121
  {
  	int i;
e443b3336   Alexander Shishkin   usb: chipidea: sp...
122
  	for (i = 0; i < OP_ENDPTCTRL; i++)
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
123
124
  		ci->hw_bank.regmap[i] =
  			(i <= CAP_LAST ? ci->hw_bank.cap : ci->hw_bank.op) +
e443b3336   Alexander Shishkin   usb: chipidea: sp...
125
126
127
  			(is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]);
  
  	for (; i <= OP_LAST; i++)
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
128
  		ci->hw_bank.regmap[i] = ci->hw_bank.op +
e443b3336   Alexander Shishkin   usb: chipidea: sp...
129
130
131
132
133
134
135
136
137
  			4 * (i - OP_ENDPTCTRL) +
  			(is_lpm
  			 ? ci_regs_lpm[OP_ENDPTCTRL]
  			 : ci_regs_nolpm[OP_ENDPTCTRL]);
  
  	return 0;
  }
  
  /**
36304b061   Li Jun   usb: chipidea: ex...
138
139
   * hw_read_intr_enable: returns interrupt enable register
   *
19353881b   Peter Chen   usb: chipidea: en...
140
141
   * @ci: the controller
   *
36304b061   Li Jun   usb: chipidea: ex...
142
143
144
145
146
147
148
149
150
151
   * This function returns register data
   */
  u32 hw_read_intr_enable(struct ci_hdrc *ci)
  {
  	return hw_read(ci, OP_USBINTR, ~0);
  }
  
  /**
   * hw_read_intr_status: returns interrupt status register
   *
19353881b   Peter Chen   usb: chipidea: en...
152
153
   * @ci: the controller
   *
36304b061   Li Jun   usb: chipidea: ex...
154
155
156
157
158
159
160
161
   * This function returns register data
   */
  u32 hw_read_intr_status(struct ci_hdrc *ci)
  {
  	return hw_read(ci, OP_USBSTS, ~0);
  }
  
  /**
e443b3336   Alexander Shishkin   usb: chipidea: sp...
162
163
164
165
166
   * hw_port_test_set: writes port test mode (execute without interruption)
   * @mode: new value
   *
   * This function returns an error code
   */
8e22978c5   Alexander Shishkin   usb: chipidea: dr...
167
  int hw_port_test_set(struct ci_hdrc *ci, u8 mode)
e443b3336   Alexander Shishkin   usb: chipidea: sp...
168
169
170
171
172
  {
  	const u8 TEST_MODE_MAX = 7;
  
  	if (mode > TEST_MODE_MAX)
  		return -EINVAL;
727b4ddb4   Felipe Balbi   usb: chipidea: do...
173
  	hw_write(ci, OP_PORTSC, PORTSC_PTC, mode << __ffs(PORTSC_PTC));
e443b3336   Alexander Shishkin   usb: chipidea: sp...
174
175
176
177
178
179
  	return 0;
  }
  
  /**
   * hw_port_test_get: reads port test mode value
   *
19353881b   Peter Chen   usb: chipidea: en...
180
181
   * @ci: the controller
   *
e443b3336   Alexander Shishkin   usb: chipidea: sp...
182
183
   * This function returns port test mode value
   */
8e22978c5   Alexander Shishkin   usb: chipidea: dr...
184
  u8 hw_port_test_get(struct ci_hdrc *ci)
e443b3336   Alexander Shishkin   usb: chipidea: sp...
185
  {
727b4ddb4   Felipe Balbi   usb: chipidea: do...
186
  	return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC);
e443b3336   Alexander Shishkin   usb: chipidea: sp...
187
  }
b82613cf0   Peter Chen   usb: chipidea: ad...
188
189
190
191
192
193
194
195
196
197
  static void hw_wait_phy_stable(void)
  {
  	/*
  	 * The phy needs some delay to output the stable status from low
  	 * power mode. And for OTGSC, the status inputs are debounced
  	 * using a 1 ms time constant, so, delay 2ms for controller to get
  	 * the stable status, like vbus and id when the phy leaves low power.
  	 */
  	usleep_range(2000, 2500);
  }
864cf9499   Peter Chen   usb: chipidea: ad...
198
199
200
201
202
  /* The PHY enters/leaves low power mode */
  static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
  {
  	enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC;
  	bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm)));
6d037db64   Peter Chen   usb: chipidea: re...
203
  	if (enable && !lpm)
864cf9499   Peter Chen   usb: chipidea: ad...
204
205
  		hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
  				PORTSC_PHCD(ci->hw_bank.lpm));
6d037db64   Peter Chen   usb: chipidea: re...
206
  	else if (!enable && lpm)
864cf9499   Peter Chen   usb: chipidea: ad...
207
208
  		hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
  				0);
864cf9499   Peter Chen   usb: chipidea: ad...
209
  }
8e22978c5   Alexander Shishkin   usb: chipidea: dr...
210
  static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
e443b3336   Alexander Shishkin   usb: chipidea: sp...
211
212
213
214
  {
  	u32 reg;
  
  	/* bank is a module variable */
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
215
  	ci->hw_bank.abs = base;
e443b3336   Alexander Shishkin   usb: chipidea: sp...
216

5f36e231e   Alexander Shishkin   usb: chipidea: ad...
217
  	ci->hw_bank.cap = ci->hw_bank.abs;
77c4400f2   Richard Zhao   USB: Chipidea: re...
218
  	ci->hw_bank.cap += ci->platdata->capoffset;
938d323f1   Svetoslav Neykov   usb: chipidea: bi...
219
  	ci->hw_bank.op = ci->hw_bank.cap + (ioread32(ci->hw_bank.cap) & 0xff);
e443b3336   Alexander Shishkin   usb: chipidea: sp...
220

5f36e231e   Alexander Shishkin   usb: chipidea: ad...
221
222
  	hw_alloc_regmap(ci, false);
  	reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >>
727b4ddb4   Felipe Balbi   usb: chipidea: do...
223
  		__ffs(HCCPARAMS_LEN);
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
224
  	ci->hw_bank.lpm  = reg;
aeb2c1210   Chris Ruehl   usb: chipidea: Re...
225
226
  	if (reg)
  		hw_alloc_regmap(ci, !!reg);
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
227
228
229
  	ci->hw_bank.size = ci->hw_bank.op - ci->hw_bank.abs;
  	ci->hw_bank.size += OP_LAST;
  	ci->hw_bank.size /= sizeof(u32);
e443b3336   Alexander Shishkin   usb: chipidea: sp...
230

5f36e231e   Alexander Shishkin   usb: chipidea: ad...
231
  	reg = hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DEN) >>
727b4ddb4   Felipe Balbi   usb: chipidea: do...
232
  		__ffs(DCCPARAMS_DEN);
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
233
  	ci->hw_ep_max = reg * 2;   /* cache hw ENDPT_MAX */
e443b3336   Alexander Shishkin   usb: chipidea: sp...
234

09c94e628   Richard Zhao   usb: chipidea: re...
235
  	if (ci->hw_ep_max > ENDPT_MAX)
e443b3336   Alexander Shishkin   usb: chipidea: sp...
236
  		return -ENODEV;
864cf9499   Peter Chen   usb: chipidea: ad...
237
  	ci_hdrc_enter_lpm(ci, false);
c344b5180   Peter Chen   usb: chipidea: di...
238
239
240
241
242
  	/* Disable all interrupts bits */
  	hw_write(ci, OP_USBINTR, 0xffffffff, 0);
  
  	/* Clear all interrupts status bits*/
  	hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff);
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
243
244
245
  	dev_dbg(ci->dev, "ChipIdea HDRC found, lpm: %d; cap: %p op: %p
  ",
  		ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op);
e443b3336   Alexander Shishkin   usb: chipidea: sp...
246
247
248
249
250
251
252
253
254
  
  	/* setup lock mode ? */
  
  	/* ENDPTSETUPSTAT is '0' by default */
  
  	/* HCSPARAMS.bf.ppc SHOULD BE zero for device */
  
  	return 0;
  }
8e22978c5   Alexander Shishkin   usb: chipidea: dr...
255
  static void hw_phymode_configure(struct ci_hdrc *ci)
40dcd0e80   Michael Grzeschik   usb: chipidea: ad...
256
  {
3b5d3e684   Chris Ruehl   usb: chipidea: Fi...
257
  	u32 portsc, lpm, sts = 0;
40dcd0e80   Michael Grzeschik   usb: chipidea: ad...
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
  
  	switch (ci->platdata->phy_mode) {
  	case USBPHY_INTERFACE_MODE_UTMI:
  		portsc = PORTSC_PTS(PTS_UTMI);
  		lpm = DEVLC_PTS(PTS_UTMI);
  		break;
  	case USBPHY_INTERFACE_MODE_UTMIW:
  		portsc = PORTSC_PTS(PTS_UTMI) | PORTSC_PTW;
  		lpm = DEVLC_PTS(PTS_UTMI) | DEVLC_PTW;
  		break;
  	case USBPHY_INTERFACE_MODE_ULPI:
  		portsc = PORTSC_PTS(PTS_ULPI);
  		lpm = DEVLC_PTS(PTS_ULPI);
  		break;
  	case USBPHY_INTERFACE_MODE_SERIAL:
  		portsc = PORTSC_PTS(PTS_SERIAL);
  		lpm = DEVLC_PTS(PTS_SERIAL);
  		sts = 1;
  		break;
  	case USBPHY_INTERFACE_MODE_HSIC:
  		portsc = PORTSC_PTS(PTS_HSIC);
  		lpm = DEVLC_PTS(PTS_HSIC);
  		break;
  	default:
  		return;
  	}
  
  	if (ci->hw_bank.lpm) {
  		hw_write(ci, OP_DEVLC, DEVLC_PTS(7) | DEVLC_PTW, lpm);
3b5d3e684   Chris Ruehl   usb: chipidea: Fi...
287
288
  		if (sts)
  			hw_write(ci, OP_DEVLC, DEVLC_STS, DEVLC_STS);
40dcd0e80   Michael Grzeschik   usb: chipidea: ad...
289
290
  	} else {
  		hw_write(ci, OP_PORTSC, PORTSC_PTS(7) | PORTSC_PTW, portsc);
3b5d3e684   Chris Ruehl   usb: chipidea: Fi...
291
292
  		if (sts)
  			hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS);
40dcd0e80   Michael Grzeschik   usb: chipidea: ad...
293
294
  	}
  }
e443b3336   Alexander Shishkin   usb: chipidea: sp...
295
  /**
1e5e2d3d0   Antoine Tenart   usb: chipidea: ad...
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
   * _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy
   * interfaces
   * @ci: the controller
   *
   * This function returns an error code if the phy failed to init
   */
  static int _ci_usb_phy_init(struct ci_hdrc *ci)
  {
  	int ret;
  
  	if (ci->phy) {
  		ret = phy_init(ci->phy);
  		if (ret)
  			return ret;
  
  		ret = phy_power_on(ci->phy);
  		if (ret) {
  			phy_exit(ci->phy);
  			return ret;
  		}
  	} else {
  		ret = usb_phy_init(ci->usb_phy);
  	}
  
  	return ret;
  }
  
  /**
   * _ci_usb_phy_exit: deinitialize phy taking in account both phy and usb_phy
   * interfaces
   * @ci: the controller
   */
  static void ci_usb_phy_exit(struct ci_hdrc *ci)
  {
  	if (ci->phy) {
  		phy_power_off(ci->phy);
  		phy_exit(ci->phy);
  	} else {
  		usb_phy_shutdown(ci->usb_phy);
  	}
  }
  
  /**
d03cccff9   Peter Chen   usb: chipidea: co...
339
340
   * ci_usb_phy_init: initialize phy according to different phy type
   * @ci: the controller
19353881b   Peter Chen   usb: chipidea: en...
341
   *
d03cccff9   Peter Chen   usb: chipidea: co...
342
343
344
345
346
347
348
349
350
351
   * This function returns an error code if usb_phy_init has failed
   */
  static int ci_usb_phy_init(struct ci_hdrc *ci)
  {
  	int ret;
  
  	switch (ci->platdata->phy_mode) {
  	case USBPHY_INTERFACE_MODE_UTMI:
  	case USBPHY_INTERFACE_MODE_UTMIW:
  	case USBPHY_INTERFACE_MODE_HSIC:
1e5e2d3d0   Antoine Tenart   usb: chipidea: ad...
352
  		ret = _ci_usb_phy_init(ci);
b82613cf0   Peter Chen   usb: chipidea: ad...
353
354
355
  		if (!ret)
  			hw_wait_phy_stable();
  		else
d03cccff9   Peter Chen   usb: chipidea: co...
356
357
358
359
360
361
  			return ret;
  		hw_phymode_configure(ci);
  		break;
  	case USBPHY_INTERFACE_MODE_ULPI:
  	case USBPHY_INTERFACE_MODE_SERIAL:
  		hw_phymode_configure(ci);
1e5e2d3d0   Antoine Tenart   usb: chipidea: ad...
362
  		ret = _ci_usb_phy_init(ci);
d03cccff9   Peter Chen   usb: chipidea: co...
363
364
365
366
  		if (ret)
  			return ret;
  		break;
  	default:
1e5e2d3d0   Antoine Tenart   usb: chipidea: ad...
367
  		ret = _ci_usb_phy_init(ci);
b82613cf0   Peter Chen   usb: chipidea: ad...
368
369
  		if (!ret)
  			hw_wait_phy_stable();
d03cccff9   Peter Chen   usb: chipidea: co...
370
371
372
373
374
375
  	}
  
  	return ret;
  }
  
  /**
cdd278f21   Peter Chen   usb: chipidea: ad...
376
   * hw_controller_reset: do controller reset
e443b3336   Alexander Shishkin   usb: chipidea: sp...
377
378
379
380
   * @ci: the controller
    *
   * This function returns an error code
   */
cdd278f21   Peter Chen   usb: chipidea: ad...
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
  static int hw_controller_reset(struct ci_hdrc *ci)
  {
  	int count = 0;
  
  	hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST);
  	while (hw_read(ci, OP_USBCMD, USBCMD_RST)) {
  		udelay(10);
  		if (count++ > 1000)
  			return -ETIMEDOUT;
  	}
  
  	return 0;
  }
  
  /**
   * hw_device_reset: resets chip (execute without interruption)
   * @ci: the controller
   *
   * This function returns an error code
   */
5b1573005   Peter Chen   usb: chipidea: pa...
401
  int hw_device_reset(struct ci_hdrc *ci)
e443b3336   Alexander Shishkin   usb: chipidea: sp...
402
  {
cdd278f21   Peter Chen   usb: chipidea: ad...
403
  	int ret;
e443b3336   Alexander Shishkin   usb: chipidea: sp...
404
405
406
  	/* should flush & stop before reset */
  	hw_write(ci, OP_ENDPTFLUSH, ~0, ~0);
  	hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
cdd278f21   Peter Chen   usb: chipidea: ad...
407
408
409
410
411
412
  	ret = hw_controller_reset(ci);
  	if (ret) {
  		dev_err(ci->dev, "error resetting controller, ret=%d
  ", ret);
  		return ret;
  	}
e443b3336   Alexander Shishkin   usb: chipidea: sp...
413

77c4400f2   Richard Zhao   USB: Chipidea: re...
414
415
  	if (ci->platdata->notify_event)
  		ci->platdata->notify_event(ci,
8e22978c5   Alexander Shishkin   usb: chipidea: dr...
416
  			CI_HDRC_CONTROLLER_RESET_EVENT);
e443b3336   Alexander Shishkin   usb: chipidea: sp...
417

8e22978c5   Alexander Shishkin   usb: chipidea: dr...
418
  	if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING)
758fc9860   Alexander Shishkin   usb: chipidea: us...
419
  		hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
e443b3336   Alexander Shishkin   usb: chipidea: sp...
420

4f6743d5c   Michael Grzeschik   usb: chipidea: ud...
421
422
423
424
425
426
  	if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) {
  		if (ci->hw_bank.lpm)
  			hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC);
  		else
  			hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC);
  	}
e443b3336   Alexander Shishkin   usb: chipidea: sp...
427
428
  	/* USBMODE should be configured step by step */
  	hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
5b1573005   Peter Chen   usb: chipidea: pa...
429
  	hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_DC);
e443b3336   Alexander Shishkin   usb: chipidea: sp...
430
431
  	/* HW >= 2.3 */
  	hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM);
5b1573005   Peter Chen   usb: chipidea: pa...
432
433
  	if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) {
  		pr_err("cannot enter in %s device mode", ci_role(ci)->name);
e443b3336   Alexander Shishkin   usb: chipidea: sp...
434
435
436
437
438
439
  		pr_err("lpm = %i", ci->hw_bank.lpm);
  		return -ENODEV;
  	}
  
  	return 0;
  }
22fa84455   Peter Chen   usb: chipidea: ad...
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
  /**
   * hw_wait_reg: wait the register value
   *
   * Sometimes, it needs to wait register value before going on.
   * Eg, when switch to device mode, the vbus value should be lower
   * than OTGSC_BSV before connects to host.
   *
   * @ci: the controller
   * @reg: register index
   * @mask: mast bit
   * @value: the bit value to wait
   * @timeout_ms: timeout in millisecond
   *
   * This function returns an error code if timeout
   */
  int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask,
  				u32 value, unsigned int timeout_ms)
  {
  	unsigned long elapse = jiffies + msecs_to_jiffies(timeout_ms);
  
  	while (hw_read(ci, reg, mask) != value) {
  		if (time_after(jiffies, elapse)) {
  			dev_err(ci->dev, "timeout waiting for %08x in %d
  ",
  					mask, reg);
  			return -ETIMEDOUT;
  		}
  		msleep(20);
  	}
  
  	return 0;
  }
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
472
473
  static irqreturn_t ci_irq(int irq, void *data)
  {
8e22978c5   Alexander Shishkin   usb: chipidea: dr...
474
  	struct ci_hdrc *ci = data;
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
475
  	irqreturn_t ret = IRQ_NONE;
b183c19f9   Richard Zhao   USB: chipidea: re...
476
  	u32 otgsc = 0;
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
477

4dcf720c5   Li Jun   usb: chipidea: OT...
478
  	if (ci->is_otg) {
0c33bf781   Li Jun   usb: chipidea: op...
479
  		otgsc = hw_read_otgsc(ci, ~0);
4dcf720c5   Li Jun   usb: chipidea: OT...
480
481
482
483
484
485
  		if (ci_otg_is_fsm_mode(ci)) {
  			ret = ci_otg_fsm_irq(ci);
  			if (ret == IRQ_HANDLED)
  				return ret;
  		}
  	}
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
486

a107f8c50   Peter Chen   usb: chipidea: ad...
487
488
489
490
491
492
  	/*
  	 * Handle id change interrupt, it indicates device/host function
  	 * switch.
  	 */
  	if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
  		ci->id_event = true;
0c33bf781   Li Jun   usb: chipidea: op...
493
494
  		/* Clear ID change irq status */
  		hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
be6b0c1bd   Peter Chen   usb: chipidea: us...
495
  		ci_otg_queue_work(ci);
a107f8c50   Peter Chen   usb: chipidea: ad...
496
497
  		return IRQ_HANDLED;
  	}
b183c19f9   Richard Zhao   USB: chipidea: re...
498

a107f8c50   Peter Chen   usb: chipidea: ad...
499
500
501
502
503
504
  	/*
  	 * Handle vbus change interrupt, it indicates device connection
  	 * and disconnection events.
  	 */
  	if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
  		ci->b_sess_valid_event = true;
0c33bf781   Li Jun   usb: chipidea: op...
505
506
  		/* Clear BSV irq */
  		hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
be6b0c1bd   Peter Chen   usb: chipidea: us...
507
  		ci_otg_queue_work(ci);
a107f8c50   Peter Chen   usb: chipidea: ad...
508
  		return IRQ_HANDLED;
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
509
  	}
a107f8c50   Peter Chen   usb: chipidea: ad...
510
511
512
  	/* Handle device/host interrupt */
  	if (ci->role != CI_ROLE_END)
  		ret = ci_role(ci)->irq(ci);
b183c19f9   Richard Zhao   USB: chipidea: re...
513
  	return ret;
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
514
  }
1542d9c35   Peter Chen   usb: chipidea: mo...
515
516
517
  static int ci_get_platdata(struct device *dev,
  		struct ci_hdrc_platform_data *platdata)
  {
c22600c3e   Peter Chen   usb: chipidea: mo...
518
519
520
521
522
523
524
525
  	if (!platdata->phy_mode)
  		platdata->phy_mode = of_usb_get_phy_mode(dev->of_node);
  
  	if (!platdata->dr_mode)
  		platdata->dr_mode = of_usb_get_dr_mode(dev->of_node);
  
  	if (platdata->dr_mode == USB_DR_MODE_UNKNOWN)
  		platdata->dr_mode = USB_DR_MODE_OTG;
c2ec3a732   Peter Chen   usb: chipidea: on...
526
527
528
529
530
531
  	if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) {
  		/* Get the vbus regulator */
  		platdata->reg_vbus = devm_regulator_get(dev, "vbus");
  		if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) {
  			return -EPROBE_DEFER;
  		} else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) {
6629467ba   Mickael Maison   usb: chipidea: Fi...
532
  			/* no vbus regulator is needed */
c2ec3a732   Peter Chen   usb: chipidea: on...
533
534
535
536
537
538
539
  			platdata->reg_vbus = NULL;
  		} else if (IS_ERR(platdata->reg_vbus)) {
  			dev_err(dev, "Getting regulator error: %ld
  ",
  				PTR_ERR(platdata->reg_vbus));
  			return PTR_ERR(platdata->reg_vbus);
  		}
f6a9ff078   Peter Chen   usb: chipidea: ad...
540
541
542
543
  		/* Get TPL support */
  		if (!platdata->tpl_support)
  			platdata->tpl_support =
  				of_usb_host_tpl_support(dev->of_node);
c2ec3a732   Peter Chen   usb: chipidea: on...
544
  	}
4f6743d5c   Michael Grzeschik   usb: chipidea: ud...
545
546
  	if (of_usb_get_maximum_speed(dev->of_node) == USB_SPEED_FULL)
  		platdata->flags |= CI_HDRC_FORCE_FULLSPEED;
1542d9c35   Peter Chen   usb: chipidea: mo...
547
548
  	return 0;
  }
fe6e125e3   Richard Zhao   USB: Chipidea: ad...
549
  static DEFINE_IDA(ci_ida);
8e22978c5   Alexander Shishkin   usb: chipidea: dr...
550
  struct platform_device *ci_hdrc_add_device(struct device *dev,
cbc6dc2af   Richard Zhao   USB: Chipidea: ad...
551
  			struct resource *res, int nres,
8e22978c5   Alexander Shishkin   usb: chipidea: dr...
552
  			struct ci_hdrc_platform_data *platdata)
cbc6dc2af   Richard Zhao   USB: Chipidea: ad...
553
554
  {
  	struct platform_device *pdev;
fe6e125e3   Richard Zhao   USB: Chipidea: ad...
555
  	int id, ret;
cbc6dc2af   Richard Zhao   USB: Chipidea: ad...
556

1542d9c35   Peter Chen   usb: chipidea: mo...
557
558
559
  	ret = ci_get_platdata(dev, platdata);
  	if (ret)
  		return ERR_PTR(ret);
fe6e125e3   Richard Zhao   USB: Chipidea: ad...
560
561
562
563
564
565
566
567
568
  	id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL);
  	if (id < 0)
  		return ERR_PTR(id);
  
  	pdev = platform_device_alloc("ci_hdrc", id);
  	if (!pdev) {
  		ret = -ENOMEM;
  		goto put_id;
  	}
cbc6dc2af   Richard Zhao   USB: Chipidea: ad...
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
  
  	pdev->dev.parent = dev;
  	pdev->dev.dma_mask = dev->dma_mask;
  	pdev->dev.dma_parms = dev->dma_parms;
  	dma_set_coherent_mask(&pdev->dev, dev->coherent_dma_mask);
  
  	ret = platform_device_add_resources(pdev, res, nres);
  	if (ret)
  		goto err;
  
  	ret = platform_device_add_data(pdev, platdata, sizeof(*platdata));
  	if (ret)
  		goto err;
  
  	ret = platform_device_add(pdev);
  	if (ret)
  		goto err;
  
  	return pdev;
  
  err:
  	platform_device_put(pdev);
fe6e125e3   Richard Zhao   USB: Chipidea: ad...
591
592
  put_id:
  	ida_simple_remove(&ci_ida, id);
cbc6dc2af   Richard Zhao   USB: Chipidea: ad...
593
594
  	return ERR_PTR(ret);
  }
8e22978c5   Alexander Shishkin   usb: chipidea: dr...
595
  EXPORT_SYMBOL_GPL(ci_hdrc_add_device);
cbc6dc2af   Richard Zhao   USB: Chipidea: ad...
596

8e22978c5   Alexander Shishkin   usb: chipidea: dr...
597
  void ci_hdrc_remove_device(struct platform_device *pdev)
cbc6dc2af   Richard Zhao   USB: Chipidea: ad...
598
  {
98c355344   Lothar Waßmann   USB: chipidea: fi...
599
  	int id = pdev->id;
cbc6dc2af   Richard Zhao   USB: Chipidea: ad...
600
  	platform_device_unregister(pdev);
98c355344   Lothar Waßmann   USB: chipidea: fi...
601
  	ida_simple_remove(&ci_ida, id);
cbc6dc2af   Richard Zhao   USB: Chipidea: ad...
602
  }
8e22978c5   Alexander Shishkin   usb: chipidea: dr...
603
  EXPORT_SYMBOL_GPL(ci_hdrc_remove_device);
cbc6dc2af   Richard Zhao   USB: Chipidea: ad...
604

3f124d233   Peter Chen   usb: chipidea: ad...
605
606
607
608
  static inline void ci_role_destroy(struct ci_hdrc *ci)
  {
  	ci_hdrc_gadget_destroy(ci);
  	ci_hdrc_host_destroy(ci);
cbec6bd55   Peter Chen   usb: chipidea: mo...
609
610
  	if (ci->is_otg)
  		ci_hdrc_otg_destroy(ci);
3f124d233   Peter Chen   usb: chipidea: ad...
611
  }
577b232fc   Peter Chen   usb: chipidea: ad...
612
613
614
615
616
617
618
619
  static void ci_get_otg_capable(struct ci_hdrc *ci)
  {
  	if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG)
  		ci->is_otg = false;
  	else
  		ci->is_otg = (hw_read(ci, CAP_DCCPARAMS,
  				DCCPARAMS_DC | DCCPARAMS_HC)
  					== (DCCPARAMS_DC | DCCPARAMS_HC));
90893b90d   Peter Chen   usb: chipidea: ad...
620
  	if (ci->is_otg)
577b232fc   Peter Chen   usb: chipidea: ad...
621
622
623
  		dev_dbg(ci->dev, "It is OTG capable controller
  ");
  }
41ac7b3ab   Bill Pemberton   usb: remove use o...
624
  static int ci_hdrc_probe(struct platform_device *pdev)
e443b3336   Alexander Shishkin   usb: chipidea: sp...
625
626
  {
  	struct device	*dev = &pdev->dev;
8e22978c5   Alexander Shishkin   usb: chipidea: dr...
627
  	struct ci_hdrc	*ci;
e443b3336   Alexander Shishkin   usb: chipidea: sp...
628
629
630
  	struct resource	*res;
  	void __iomem	*base;
  	int		ret;
691962d15   Sascha Hauer   usb: chipidea: in...
631
  	enum usb_dr_mode dr_mode;
e443b3336   Alexander Shishkin   usb: chipidea: sp...
632

fad56745a   Jingoo Han   usb: chipidea: us...
633
  	if (!dev_get_platdata(dev)) {
e443b3336   Alexander Shishkin   usb: chipidea: sp...
634
635
636
637
638
639
  		dev_err(dev, "platform data missing
  ");
  		return -ENODEV;
  	}
  
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
19290816c   Felipe Balbi   usb: chipidea: co...
640
641
642
  	base = devm_ioremap_resource(dev, res);
  	if (IS_ERR(base))
  		return PTR_ERR(base);
e443b3336   Alexander Shishkin   usb: chipidea: sp...
643

5f36e231e   Alexander Shishkin   usb: chipidea: ad...
644
  	ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL);
d0f992498   Fabio Estevam   usb: chipidea: co...
645
  	if (!ci)
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
646
  		return -ENOMEM;
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
647
648
  
  	ci->dev = dev;
fad56745a   Jingoo Han   usb: chipidea: us...
649
  	ci->platdata = dev_get_platdata(dev);
ed8f8318d   Peter Chen   usb: chipidea: ad...
650
651
  	ci->imx28_write_fix = !!(ci->platdata->flags &
  		CI_HDRC_IMX28_WRITE_FIX);
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
652
653
654
655
656
657
658
  
  	ret = hw_device_init(ci, base);
  	if (ret < 0) {
  		dev_err(dev, "can't initialize hardware
  ");
  		return -ENODEV;
  	}
e443b3336   Alexander Shishkin   usb: chipidea: sp...
659

1e5e2d3d0   Antoine Tenart   usb: chipidea: ad...
660
661
662
  	if (ci->platdata->phy) {
  		ci->phy = ci->platdata->phy;
  	} else if (ci->platdata->usb_phy) {
ef44cb422   Antoine Tenart   usb: allow to sup...
663
  		ci->usb_phy = ci->platdata->usb_phy;
1e5e2d3d0   Antoine Tenart   usb: chipidea: ad...
664
  	} else {
21a5b579c   Antoine Tenart   usb: chipidea: fi...
665
666
  		ci->phy = devm_phy_get(dev->parent, "usb-phy");
  		ci->usb_phy = devm_usb_get_phy(dev->parent, USB_PHY_TYPE_USB2);
c859aa65a   Peter Chen   usb: chipidea: re...
667

1e5e2d3d0   Antoine Tenart   usb: chipidea: ad...
668
669
670
671
672
673
674
  		/* if both generic PHY and USB PHY layers aren't enabled */
  		if (PTR_ERR(ci->phy) == -ENOSYS &&
  				PTR_ERR(ci->usb_phy) == -ENXIO)
  			return -ENXIO;
  
  		if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy))
  			return -EPROBE_DEFER;
c859aa65a   Peter Chen   usb: chipidea: re...
675

1e5e2d3d0   Antoine Tenart   usb: chipidea: ad...
676
677
678
679
  		if (IS_ERR(ci->phy))
  			ci->phy = NULL;
  		else if (IS_ERR(ci->usb_phy))
  			ci->usb_phy = NULL;
c859aa65a   Peter Chen   usb: chipidea: re...
680
  	}
d03cccff9   Peter Chen   usb: chipidea: co...
681
  	ret = ci_usb_phy_init(ci);
74475ede7   Peter Chen   usb: chipidea: mo...
682
683
684
685
686
  	if (ret) {
  		dev_err(dev, "unable to init phy: %d
  ", ret);
  		return ret;
  	}
eb70e5ab8   Alexander Shishkin   usb: chipidea: ad...
687
  	ci->hw_bank.phys = res->start;
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
688
689
  	ci->irq = platform_get_irq(pdev, 0);
  	if (ci->irq < 0) {
e443b3336   Alexander Shishkin   usb: chipidea: sp...
690
691
  		dev_err(dev, "missing IRQ
  ");
42d182124   Fabio Estevam   usb: chipidea: Pr...
692
  		ret = ci->irq;
c859aa65a   Peter Chen   usb: chipidea: re...
693
  		goto deinit_phy;
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
694
  	}
577b232fc   Peter Chen   usb: chipidea: ad...
695
  	ci_get_otg_capable(ci);
691962d15   Sascha Hauer   usb: chipidea: in...
696
  	dr_mode = ci->platdata->dr_mode;
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
697
  	/* initialize role(s) before the interrupt is requested */
691962d15   Sascha Hauer   usb: chipidea: in...
698
699
700
701
702
703
  	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
  		ret = ci_hdrc_host_init(ci);
  		if (ret)
  			dev_info(dev, "doesn't support host
  ");
  	}
eb70e5ab8   Alexander Shishkin   usb: chipidea: ad...
704

691962d15   Sascha Hauer   usb: chipidea: in...
705
706
707
708
709
710
  	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
  		ret = ci_hdrc_gadget_init(ci);
  		if (ret)
  			dev_info(dev, "doesn't support gadget
  ");
  	}
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
711
712
713
714
  
  	if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) {
  		dev_err(dev, "no supported roles
  ");
74475ede7   Peter Chen   usb: chipidea: mo...
715
  		ret = -ENODEV;
c859aa65a   Peter Chen   usb: chipidea: re...
716
  		goto deinit_phy;
cbec6bd55   Peter Chen   usb: chipidea: mo...
717
  	}
27c62c2da   Peter Chen   usb: chipidea: ot...
718
  	if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) {
90893b90d   Peter Chen   usb: chipidea: ad...
719
720
721
  		/* Disable and clear all OTG irq */
  		hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
  							OTGSC_INT_STATUS_BITS);
cbec6bd55   Peter Chen   usb: chipidea: mo...
722
723
724
725
726
727
  		ret = ci_hdrc_otg_init(ci);
  		if (ret) {
  			dev_err(dev, "init otg fails, ret = %d
  ", ret);
  			goto stop;
  		}
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
728
729
730
  	}
  
  	if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
577b232fc   Peter Chen   usb: chipidea: ad...
731
  		if (ci->is_otg) {
577b232fc   Peter Chen   usb: chipidea: ad...
732
  			ci->role = ci_otg_role(ci);
0c33bf781   Li Jun   usb: chipidea: op...
733
734
  			/* Enable ID change irq */
  			hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE);
577b232fc   Peter Chen   usb: chipidea: ad...
735
736
737
738
739
740
741
742
  		} else {
  			/*
  			 * If the controller is not OTG capable, but support
  			 * role switch, the defalt role is gadget, and the
  			 * user can switch it through debugfs.
  			 */
  			ci->role = CI_ROLE_GADGET;
  		}
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
743
744
745
746
747
  	} else {
  		ci->role = ci->roles[CI_ROLE_HOST]
  			? CI_ROLE_HOST
  			: CI_ROLE_GADGET;
  	}
5a1e1456f   Peter Chen   usb: chipidea: fi...
748
749
750
  	/* only update vbus status for peripheral */
  	if (ci->role == CI_ROLE_GADGET)
  		ci_handle_vbus_change(ci);
4dcf720c5   Li Jun   usb: chipidea: OT...
751
752
753
754
755
756
757
758
  	if (!ci_otg_is_fsm_mode(ci)) {
  		ret = ci_role_start(ci, ci->role);
  		if (ret) {
  			dev_err(dev, "can't start %s role
  ",
  						ci_role(ci)->name);
  			goto stop;
  		}
e443b3336   Alexander Shishkin   usb: chipidea: sp...
759
  	}
24c498df1   Peter Chen   Revert "usb: chip...
760
  	platform_set_drvdata(pdev, ci);
4c503dd5f   Peter Chen   usb: chipidea: us...
761
762
  	ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED,
  			ci->platdata->name, ci);
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
763
764
  	if (ret)
  		goto stop;
e443b3336   Alexander Shishkin   usb: chipidea: sp...
765

4dcf720c5   Li Jun   usb: chipidea: OT...
766
767
  	if (ci_otg_is_fsm_mode(ci))
  		ci_hdrc_otg_fsm_start(ci);
adf0f735e   Alexander Shishkin   usb: chipidea: mo...
768
769
770
  	ret = dbg_create_files(ci);
  	if (!ret)
  		return 0;
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
771

5f36e231e   Alexander Shishkin   usb: chipidea: ad...
772
  stop:
3f124d233   Peter Chen   usb: chipidea: ad...
773
  	ci_role_destroy(ci);
c859aa65a   Peter Chen   usb: chipidea: re...
774
  deinit_phy:
1e5e2d3d0   Antoine Tenart   usb: chipidea: ad...
775
  	ci_usb_phy_exit(ci);
e443b3336   Alexander Shishkin   usb: chipidea: sp...
776
777
778
  
  	return ret;
  }
fb4e98ab6   Bill Pemberton   usb: remove use o...
779
  static int ci_hdrc_remove(struct platform_device *pdev)
e443b3336   Alexander Shishkin   usb: chipidea: sp...
780
  {
8e22978c5   Alexander Shishkin   usb: chipidea: dr...
781
  	struct ci_hdrc *ci = platform_get_drvdata(pdev);
e443b3336   Alexander Shishkin   usb: chipidea: sp...
782

adf0f735e   Alexander Shishkin   usb: chipidea: mo...
783
  	dbg_remove_files(ci);
3f124d233   Peter Chen   usb: chipidea: ad...
784
  	ci_role_destroy(ci);
864cf9499   Peter Chen   usb: chipidea: ad...
785
  	ci_hdrc_enter_lpm(ci, true);
1e5e2d3d0   Antoine Tenart   usb: chipidea: ad...
786
  	ci_usb_phy_exit(ci);
e443b3336   Alexander Shishkin   usb: chipidea: sp...
787
788
789
  
  	return 0;
  }
8076932ff   Peter Chen   usb: chipidea: ad...
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
  #ifdef CONFIG_PM_SLEEP
  static void ci_controller_suspend(struct ci_hdrc *ci)
  {
  	ci_hdrc_enter_lpm(ci, true);
  
  	if (ci->usb_phy)
  		usb_phy_set_suspend(ci->usb_phy, 1);
  }
  
  static int ci_controller_resume(struct device *dev)
  {
  	struct ci_hdrc *ci = dev_get_drvdata(dev);
  
  	dev_dbg(dev, "at %s
  ", __func__);
  
  	ci_hdrc_enter_lpm(ci, false);
  
  	if (ci->usb_phy) {
  		usb_phy_set_suspend(ci->usb_phy, 0);
  		usb_phy_set_wakeup(ci->usb_phy, false);
  		hw_wait_phy_stable();
  	}
  
  	return 0;
  }
  
  static int ci_suspend(struct device *dev)
  {
  	struct ci_hdrc *ci = dev_get_drvdata(dev);
  
  	if (ci->wq)
  		flush_workqueue(ci->wq);
  
  	ci_controller_suspend(ci);
  
  	return 0;
  }
  
  static int ci_resume(struct device *dev)
  {
  	return ci_controller_resume(dev);
  }
  #endif /* CONFIG_PM_SLEEP */
  
  static const struct dev_pm_ops ci_pm_ops = {
  	SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume)
  };
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
838
839
  static struct platform_driver ci_hdrc_driver = {
  	.probe	= ci_hdrc_probe,
7690417db   Bill Pemberton   usb: remove use o...
840
  	.remove	= ci_hdrc_remove,
e443b3336   Alexander Shishkin   usb: chipidea: sp...
841
  	.driver	= {
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
842
  		.name	= "ci_hdrc",
8076932ff   Peter Chen   usb: chipidea: ad...
843
  		.pm	= &ci_pm_ops,
e443b3336   Alexander Shishkin   usb: chipidea: sp...
844
845
  	},
  };
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
846
  module_platform_driver(ci_hdrc_driver);
e443b3336   Alexander Shishkin   usb: chipidea: sp...
847

5f36e231e   Alexander Shishkin   usb: chipidea: ad...
848
  MODULE_ALIAS("platform:ci_hdrc");
e443b3336   Alexander Shishkin   usb: chipidea: sp...
849
850
  MODULE_LICENSE("GPL v2");
  MODULE_AUTHOR("David Lopo <dlopo@chipidea.mips.com>");
5f36e231e   Alexander Shishkin   usb: chipidea: ad...
851
  MODULE_DESCRIPTION("ChipIdea HDRC Driver");