Merge tag 'sound-5.13-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
[linux-2.6-microblaze.git] / drivers / usb / c67x00 / c67x00-hcd.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * c67x00-hcd.c: Cypress C67X00 USB Host Controller Driver
4  *
5  * Copyright (C) 2006-2008 Barco N.V.
6  *    Derived from the Cypress cy7c67200/300 ezusb linux driver and
7  *    based on multiple host controller drivers inside the linux kernel.
8  */
9
10 #include <linux/device.h>
11 #include <linux/platform_device.h>
12 #include <linux/usb.h>
13
14 #include "c67x00.h"
15 #include "c67x00-hcd.h"
16
17 /* --------------------------------------------------------------------------
18  * Root Hub Support
19  */
20
21 static __u8 c67x00_hub_des[] = {
22         0x09,                   /*  __u8  bLength; */
23         USB_DT_HUB,             /*  __u8  bDescriptorType; Hub-descriptor */
24         0x02,                   /*  __u8  bNbrPorts; */
25         0x00,                   /* __u16  wHubCharacteristics; */
26         0x00,                   /*   (per-port OC, no power switching) */
27         0x32,                   /*  __u8  bPwrOn2pwrGood; 2ms */
28         0x00,                   /*  __u8  bHubContrCurrent; 0 mA */
29         0x00,                   /*  __u8  DeviceRemovable; ** 7 Ports max ** */
30         0xff,                   /*  __u8  PortPwrCtrlMask; ** 7 ports max ** */
31 };
32
33 static void c67x00_hub_reset_host_port(struct c67x00_sie *sie, int port)
34 {
35         struct c67x00_hcd *c67x00 = sie->private_data;
36         unsigned long flags;
37
38         c67x00_ll_husb_reset(sie, port);
39
40         spin_lock_irqsave(&c67x00->lock, flags);
41         c67x00_ll_husb_reset_port(sie, port);
42         spin_unlock_irqrestore(&c67x00->lock, flags);
43
44         c67x00_ll_set_husb_eot(sie->dev, DEFAULT_EOT);
45 }
46
47 static int c67x00_hub_status_data(struct usb_hcd *hcd, char *buf)
48 {
49         struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
50         struct c67x00_sie *sie = c67x00->sie;
51         u16 status;
52         int i;
53
54         *buf = 0;
55         status = c67x00_ll_usb_get_status(sie);
56         for (i = 0; i < C67X00_PORTS; i++)
57                 if (status & PORT_CONNECT_CHANGE(i))
58                         *buf |= (1 << i);
59
60         /* bit 0 denotes hub change, b1..n port change */
61         *buf <<= 1;
62
63         return !!*buf;
64 }
65
66 static int c67x00_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
67                               u16 wIndex, char *buf, u16 wLength)
68 {
69         struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
70         struct c67x00_sie *sie = c67x00->sie;
71         u16 status, usb_status;
72         int len = 0;
73         unsigned int port = wIndex-1;
74         u16 wPortChange, wPortStatus;
75
76         switch (typeReq) {
77
78         case GetHubStatus:
79                 *(__le32 *) buf = cpu_to_le32(0);
80                 len = 4;                /* hub power */
81                 break;
82
83         case GetPortStatus:
84                 if (wIndex > C67X00_PORTS)
85                         return -EPIPE;
86
87                 status = c67x00_ll_usb_get_status(sie);
88                 usb_status = c67x00_ll_get_usb_ctl(sie);
89
90                 wPortChange = 0;
91                 if (status & PORT_CONNECT_CHANGE(port))
92                         wPortChange |= USB_PORT_STAT_C_CONNECTION;
93
94                 wPortStatus = USB_PORT_STAT_POWER;
95                 if (!(status & PORT_SE0_STATUS(port)))
96                         wPortStatus |= USB_PORT_STAT_CONNECTION;
97                 if (usb_status & LOW_SPEED_PORT(port)) {
98                         wPortStatus |= USB_PORT_STAT_LOW_SPEED;
99                         c67x00->low_speed_ports |= (1 << port);
100                 } else
101                         c67x00->low_speed_ports &= ~(1 << port);
102
103                 if (usb_status & SOF_EOP_EN(port))
104                         wPortStatus |= USB_PORT_STAT_ENABLE;
105
106                 *(__le16 *) buf = cpu_to_le16(wPortStatus);
107                 *(__le16 *) (buf + 2) = cpu_to_le16(wPortChange);
108                 len = 4;
109                 break;
110
111         case SetHubFeature:     /* We don't implement these */
112         case ClearHubFeature:
113                 switch (wValue) {
114                 case C_HUB_OVER_CURRENT:
115                 case C_HUB_LOCAL_POWER:
116                         len = 0;
117                         break;
118
119                 default:
120                         return -EPIPE;
121                 }
122                 break;
123
124         case SetPortFeature:
125                 if (wIndex > C67X00_PORTS)
126                         return -EPIPE;
127
128                 switch (wValue) {
129                 case USB_PORT_FEAT_SUSPEND:
130                         dev_dbg(c67x00_hcd_dev(c67x00),
131                                 "SetPortFeature %d (SUSPEND)\n", port);
132                         len = 0;
133                         break;
134
135                 case USB_PORT_FEAT_RESET:
136                         c67x00_hub_reset_host_port(sie, port);
137                         len = 0;
138                         break;
139
140                 case USB_PORT_FEAT_POWER:
141                         /* Power always enabled */
142                         len = 0;
143                         break;
144
145                 default:
146                         dev_dbg(c67x00_hcd_dev(c67x00),
147                                 "%s: SetPortFeature %d (0x%04x) Error!\n",
148                                 __func__, port, wValue);
149                         return -EPIPE;
150                 }
151                 break;
152
153         case ClearPortFeature:
154                 if (wIndex > C67X00_PORTS)
155                         return -EPIPE;
156
157                 switch (wValue) {
158                 case USB_PORT_FEAT_ENABLE:
159                         /* Reset the port so that the c67x00 also notices the
160                          * disconnect */
161                         c67x00_hub_reset_host_port(sie, port);
162                         len = 0;
163                         break;
164
165                 case USB_PORT_FEAT_C_ENABLE:
166                         dev_dbg(c67x00_hcd_dev(c67x00),
167                                 "ClearPortFeature (%d): C_ENABLE\n", port);
168                         len = 0;
169                         break;
170
171                 case USB_PORT_FEAT_SUSPEND:
172                         dev_dbg(c67x00_hcd_dev(c67x00),
173                                 "ClearPortFeature (%d): SUSPEND\n", port);
174                         len = 0;
175                         break;
176
177                 case USB_PORT_FEAT_C_SUSPEND:
178                         dev_dbg(c67x00_hcd_dev(c67x00),
179                                 "ClearPortFeature (%d): C_SUSPEND\n", port);
180                         len = 0;
181                         break;
182
183                 case USB_PORT_FEAT_POWER:
184                         dev_dbg(c67x00_hcd_dev(c67x00),
185                                 "ClearPortFeature (%d): POWER\n", port);
186                         return -EPIPE;
187
188                 case USB_PORT_FEAT_C_CONNECTION:
189                         c67x00_ll_usb_clear_status(sie,
190                                                    PORT_CONNECT_CHANGE(port));
191                         len = 0;
192                         break;
193
194                 case USB_PORT_FEAT_C_OVER_CURRENT:
195                         dev_dbg(c67x00_hcd_dev(c67x00),
196                                 "ClearPortFeature (%d): OVER_CURRENT\n", port);
197                         len = 0;
198                         break;
199
200                 case USB_PORT_FEAT_C_RESET:
201                         dev_dbg(c67x00_hcd_dev(c67x00),
202                                 "ClearPortFeature (%d): C_RESET\n", port);
203                         len = 0;
204                         break;
205
206                 default:
207                         dev_dbg(c67x00_hcd_dev(c67x00),
208                                 "%s: ClearPortFeature %d (0x%04x) Error!\n",
209                                 __func__, port, wValue);
210                         return -EPIPE;
211                 }
212                 break;
213
214         case GetHubDescriptor:
215                 len = min_t(unsigned int, sizeof(c67x00_hub_des), wLength);
216                 memcpy(buf, c67x00_hub_des, len);
217                 break;
218
219         default:
220                 dev_dbg(c67x00_hcd_dev(c67x00), "%s: unknown\n", __func__);
221                 return -EPIPE;
222         }
223
224         return 0;
225 }
226
227 /* ---------------------------------------------------------------------
228  * Main part of host controller driver
229  */
230
231 /*
232  * c67x00_hcd_irq
233  *
234  * This function is called from the interrupt handler in c67x00-drv.c
235  */
236 static void c67x00_hcd_irq(struct c67x00_sie *sie, u16 int_status, u16 msg)
237 {
238         struct c67x00_hcd *c67x00 = sie->private_data;
239         struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
240
241         /* Handle sie message flags */
242         if (msg) {
243                 if (msg & HUSB_TDListDone)
244                         c67x00_sched_kick(c67x00);
245                 else
246                         dev_warn(c67x00_hcd_dev(c67x00),
247                                  "Unknown SIE msg flag(s): 0x%04x\n", msg);
248         }
249
250         if (unlikely(hcd->state == HC_STATE_HALT))
251                 return;
252
253         if (!HCD_HW_ACCESSIBLE(hcd))
254                 return;
255
256         /* Handle Start of frame events */
257         if (int_status & SOFEOP_FLG(sie->sie_num)) {
258                 c67x00_ll_usb_clear_status(sie, SOF_EOP_IRQ_FLG);
259                 c67x00_sched_kick(c67x00);
260         }
261 }
262
263 /*
264  * c67x00_hcd_start: Host controller start hook
265  */
266 static int c67x00_hcd_start(struct usb_hcd *hcd)
267 {
268         hcd->uses_new_polling = 1;
269         hcd->state = HC_STATE_RUNNING;
270         set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
271
272         return 0;
273 }
274
275 /*
276  * c67x00_hcd_stop: Host controller stop hook
277  */
278 static void c67x00_hcd_stop(struct usb_hcd *hcd)
279 {
280         /* Nothing to do */
281 }
282
283 static int c67x00_hcd_get_frame(struct usb_hcd *hcd)
284 {
285         struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
286         u16 temp_val;
287
288         dev_dbg(c67x00_hcd_dev(c67x00), "%s\n", __func__);
289         temp_val = c67x00_ll_husb_get_frame(c67x00->sie);
290         temp_val &= HOST_FRAME_MASK;
291         return temp_val ? (temp_val - 1) : HOST_FRAME_MASK;
292 }
293
294 static const struct hc_driver c67x00_hc_driver = {
295         .description    = "c67x00-hcd",
296         .product_desc   = "Cypress C67X00 Host Controller",
297         .hcd_priv_size  = sizeof(struct c67x00_hcd),
298         .flags          = HCD_USB11 | HCD_MEMORY,
299
300         /*
301          * basic lifecycle operations
302          */
303         .start          = c67x00_hcd_start,
304         .stop           = c67x00_hcd_stop,
305
306         /*
307          * managing i/o requests and associated device resources
308          */
309         .urb_enqueue    = c67x00_urb_enqueue,
310         .urb_dequeue    = c67x00_urb_dequeue,
311         .endpoint_disable = c67x00_endpoint_disable,
312
313         /*
314          * scheduling support
315          */
316         .get_frame_number = c67x00_hcd_get_frame,
317
318         /*
319          * root hub support
320          */
321         .hub_status_data = c67x00_hub_status_data,
322         .hub_control    = c67x00_hub_control,
323 };
324
325 /* ---------------------------------------------------------------------
326  * Setup/Teardown routines
327  */
328
329 int c67x00_hcd_probe(struct c67x00_sie *sie)
330 {
331         struct c67x00_hcd *c67x00;
332         struct usb_hcd *hcd;
333         unsigned long flags;
334         int retval;
335
336         if (usb_disabled())
337                 return -ENODEV;
338
339         hcd = usb_create_hcd(&c67x00_hc_driver, sie_dev(sie), "c67x00_sie");
340         if (!hcd) {
341                 retval = -ENOMEM;
342                 goto err0;
343         }
344         c67x00 = hcd_to_c67x00_hcd(hcd);
345
346         spin_lock_init(&c67x00->lock);
347         c67x00->sie = sie;
348
349         INIT_LIST_HEAD(&c67x00->list[PIPE_ISOCHRONOUS]);
350         INIT_LIST_HEAD(&c67x00->list[PIPE_INTERRUPT]);
351         INIT_LIST_HEAD(&c67x00->list[PIPE_CONTROL]);
352         INIT_LIST_HEAD(&c67x00->list[PIPE_BULK]);
353         c67x00->urb_count = 0;
354         INIT_LIST_HEAD(&c67x00->td_list);
355         c67x00->td_base_addr = CY_HCD_BUF_ADDR + SIE_TD_OFFSET(sie->sie_num);
356         c67x00->buf_base_addr = CY_HCD_BUF_ADDR + SIE_BUF_OFFSET(sie->sie_num);
357         c67x00->max_frame_bw = MAX_FRAME_BW_STD;
358
359         c67x00_ll_husb_init_host_port(sie);
360
361         init_completion(&c67x00->endpoint_disable);
362         retval = c67x00_sched_start_scheduler(c67x00);
363         if (retval)
364                 goto err1;
365
366         retval = usb_add_hcd(hcd, 0, 0);
367         if (retval) {
368                 dev_dbg(sie_dev(sie), "%s: usb_add_hcd returned %d\n",
369                         __func__, retval);
370                 goto err2;
371         }
372
373         device_wakeup_enable(hcd->self.controller);
374
375         spin_lock_irqsave(&sie->lock, flags);
376         sie->private_data = c67x00;
377         sie->irq = c67x00_hcd_irq;
378         spin_unlock_irqrestore(&sie->lock, flags);
379
380         return retval;
381
382  err2:
383         c67x00_sched_stop_scheduler(c67x00);
384  err1:
385         usb_put_hcd(hcd);
386  err0:
387         return retval;
388 }
389
390 /* may be called with controller, bus, and devices active */
391 void c67x00_hcd_remove(struct c67x00_sie *sie)
392 {
393         struct c67x00_hcd *c67x00 = sie->private_data;
394         struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
395
396         c67x00_sched_stop_scheduler(c67x00);
397         usb_remove_hcd(hcd);
398         usb_put_hcd(hcd);
399 }