2 * c67x00-hcd.c: Cypress C67X00 USB Host Controller Driver
4 * Copyright (C) 2006-2008 Barco N.V.
5 * Derived from the Cypress cy7c67200/300 ezusb linux driver and
6 * based on multiple host controller drivers inside the linux kernel.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 #include <linux/device.h>
25 #include <linux/platform_device.h>
26 #include <linux/usb.h>
29 #include "c67x00-hcd.h"
31 /* --------------------------------------------------------------------------
35 static __u8 c67x00_hub_des
[] = {
36 0x09, /* __u8 bLength; */
37 0x29, /* __u8 bDescriptorType; Hub-descriptor */
38 0x02, /* __u8 bNbrPorts; */
39 0x00, /* __u16 wHubCharacteristics; */
40 0x00, /* (per-port OC, no power switching) */
41 0x32, /* __u8 bPwrOn2pwrGood; 2ms */
42 0x00, /* __u8 bHubContrCurrent; 0 mA */
43 0x00, /* __u8 DeviceRemovable; ** 7 Ports max ** */
44 0xff, /* __u8 PortPwrCtrlMask; ** 7 ports max ** */
47 static void c67x00_hub_reset_host_port(struct c67x00_sie
*sie
, int port
)
49 struct c67x00_hcd
*c67x00
= sie
->private_data
;
52 c67x00_ll_husb_reset(sie
, port
);
54 spin_lock_irqsave(&c67x00
->lock
, flags
);
55 c67x00_ll_husb_reset_port(sie
, port
);
56 spin_unlock_irqrestore(&c67x00
->lock
, flags
);
58 c67x00_ll_set_husb_eot(sie
->dev
, DEFAULT_EOT
);
61 static int c67x00_hub_status_data(struct usb_hcd
*hcd
, char *buf
)
63 struct c67x00_hcd
*c67x00
= hcd_to_c67x00_hcd(hcd
);
64 struct c67x00_sie
*sie
= c67x00
->sie
;
69 status
= c67x00_ll_usb_get_status(sie
);
70 for (i
= 0; i
< C67X00_PORTS
; i
++)
71 if (status
& PORT_CONNECT_CHANGE(i
))
74 /* bit 0 denotes hub change, b1..n port change */
80 static int c67x00_hub_control(struct usb_hcd
*hcd
, u16 typeReq
, u16 wValue
,
81 u16 wIndex
, char *buf
, u16 wLength
)
83 struct c67x00_hcd
*c67x00
= hcd_to_c67x00_hcd(hcd
);
84 struct c67x00_sie
*sie
= c67x00
->sie
;
85 u16 status
, usb_status
;
87 unsigned int port
= wIndex
-1;
88 u16 wPortChange
, wPortStatus
;
93 *(__le32
*) buf
= cpu_to_le32(0);
94 len
= 4; /* hub power */
98 if (wIndex
> C67X00_PORTS
)
101 status
= c67x00_ll_usb_get_status(sie
);
102 usb_status
= c67x00_ll_get_usb_ctl(sie
);
105 if (status
& PORT_CONNECT_CHANGE(port
))
106 wPortChange
|= USB_PORT_STAT_C_CONNECTION
;
108 wPortStatus
= USB_PORT_STAT_POWER
;
109 if (!(status
& PORT_SE0_STATUS(port
)))
110 wPortStatus
|= USB_PORT_STAT_CONNECTION
;
111 if (usb_status
& LOW_SPEED_PORT(port
)) {
112 wPortStatus
|= USB_PORT_STAT_LOW_SPEED
;
113 c67x00
->low_speed_ports
|= (1 << port
);
115 c67x00
->low_speed_ports
&= ~(1 << port
);
117 if (usb_status
& SOF_EOP_EN(port
))
118 wPortStatus
|= USB_PORT_STAT_ENABLE
;
120 *(__le16
*) buf
= cpu_to_le16(wPortStatus
);
121 *(__le16
*) (buf
+ 2) = cpu_to_le16(wPortChange
);
125 case SetHubFeature
: /* We don't implement these */
126 case ClearHubFeature
:
128 case C_HUB_OVER_CURRENT
:
129 case C_HUB_LOCAL_POWER
:
139 if (wIndex
> C67X00_PORTS
)
143 case USB_PORT_FEAT_SUSPEND
:
144 dev_dbg(c67x00_hcd_dev(c67x00
),
145 "SetPortFeature %d (SUSPEND)\n", port
);
149 case USB_PORT_FEAT_RESET
:
150 c67x00_hub_reset_host_port(sie
, port
);
154 case USB_PORT_FEAT_POWER
:
155 /* Power always enabled */
160 dev_dbg(c67x00_hcd_dev(c67x00
),
161 "%s: SetPortFeature %d (0x%04x) Error!\n",
162 __func__
, port
, wValue
);
167 case ClearPortFeature
:
168 if (wIndex
> C67X00_PORTS
)
172 case USB_PORT_FEAT_ENABLE
:
173 /* Reset the port so that the c67x00 also notices the
175 c67x00_hub_reset_host_port(sie
, port
);
179 case USB_PORT_FEAT_C_ENABLE
:
180 dev_dbg(c67x00_hcd_dev(c67x00
),
181 "ClearPortFeature (%d): C_ENABLE\n", port
);
185 case USB_PORT_FEAT_SUSPEND
:
186 dev_dbg(c67x00_hcd_dev(c67x00
),
187 "ClearPortFeature (%d): SUSPEND\n", port
);
191 case USB_PORT_FEAT_C_SUSPEND
:
192 dev_dbg(c67x00_hcd_dev(c67x00
),
193 "ClearPortFeature (%d): C_SUSPEND\n", port
);
197 case USB_PORT_FEAT_POWER
:
198 dev_dbg(c67x00_hcd_dev(c67x00
),
199 "ClearPortFeature (%d): POWER\n", port
);
202 case USB_PORT_FEAT_C_CONNECTION
:
203 c67x00_ll_usb_clear_status(sie
,
204 PORT_CONNECT_CHANGE(port
));
208 case USB_PORT_FEAT_C_OVER_CURRENT
:
209 dev_dbg(c67x00_hcd_dev(c67x00
),
210 "ClearPortFeature (%d): OVER_CURRENT\n", port
);
214 case USB_PORT_FEAT_C_RESET
:
215 dev_dbg(c67x00_hcd_dev(c67x00
),
216 "ClearPortFeature (%d): C_RESET\n", port
);
221 dev_dbg(c67x00_hcd_dev(c67x00
),
222 "%s: ClearPortFeature %d (0x%04x) Error!\n",
223 __func__
, port
, wValue
);
228 case GetHubDescriptor
:
229 len
= min_t(unsigned int, sizeof(c67x00_hub_des
), wLength
);
230 memcpy(buf
, c67x00_hub_des
, len
);
234 dev_dbg(c67x00_hcd_dev(c67x00
), "%s: unknown\n", __func__
);
241 /* ---------------------------------------------------------------------
242 * Main part of host controller driver
248 * This function is called from the interrupt handler in c67x00-drv.c
250 static void c67x00_hcd_irq(struct c67x00_sie
*sie
, u16 int_status
, u16 msg
)
252 struct c67x00_hcd
*c67x00
= sie
->private_data
;
253 struct usb_hcd
*hcd
= c67x00_hcd_to_hcd(c67x00
);
255 /* Handle sie message flags */
257 if (msg
& HUSB_TDListDone
)
258 c67x00_sched_kick(c67x00
);
260 dev_warn(c67x00_hcd_dev(c67x00
),
261 "Unknown SIE msg flag(s): 0x%04x\n", msg
);
264 if (unlikely(hcd
->state
== HC_STATE_HALT
))
267 if (!HCD_HW_ACCESSIBLE(hcd
))
270 /* Handle Start of frame events */
271 if (int_status
& SOFEOP_FLG(sie
->sie_num
)) {
272 c67x00_ll_usb_clear_status(sie
, SOF_EOP_IRQ_FLG
);
273 c67x00_sched_kick(c67x00
);
274 set_bit(HCD_FLAG_SAW_IRQ
, &hcd
->flags
);
279 * c67x00_hcd_start: Host controller start hook
281 static int c67x00_hcd_start(struct usb_hcd
*hcd
)
283 hcd
->uses_new_polling
= 1;
284 hcd
->state
= HC_STATE_RUNNING
;
285 set_bit(HCD_FLAG_POLL_RH
, &hcd
->flags
);
291 * c67x00_hcd_stop: Host controller stop hook
293 static void c67x00_hcd_stop(struct usb_hcd
*hcd
)
298 static int c67x00_hcd_get_frame(struct usb_hcd
*hcd
)
300 struct c67x00_hcd
*c67x00
= hcd_to_c67x00_hcd(hcd
);
303 dev_dbg(c67x00_hcd_dev(c67x00
), "%s\n", __func__
);
304 temp_val
= c67x00_ll_husb_get_frame(c67x00
->sie
);
305 temp_val
&= HOST_FRAME_MASK
;
306 return temp_val
? (temp_val
- 1) : HOST_FRAME_MASK
;
309 static struct hc_driver c67x00_hc_driver
= {
310 .description
= "c67x00-hcd",
311 .product_desc
= "Cypress C67X00 Host Controller",
312 .hcd_priv_size
= sizeof(struct c67x00_hcd
),
313 .flags
= HCD_USB11
| HCD_MEMORY
,
316 * basic lifecycle operations
318 .start
= c67x00_hcd_start
,
319 .stop
= c67x00_hcd_stop
,
322 * managing i/o requests and associated device resources
324 .urb_enqueue
= c67x00_urb_enqueue
,
325 .urb_dequeue
= c67x00_urb_dequeue
,
326 .endpoint_disable
= c67x00_endpoint_disable
,
331 .get_frame_number
= c67x00_hcd_get_frame
,
336 .hub_status_data
= c67x00_hub_status_data
,
337 .hub_control
= c67x00_hub_control
,
340 /* ---------------------------------------------------------------------
341 * Setup/Teardown routines
344 int c67x00_hcd_probe(struct c67x00_sie
*sie
)
346 struct c67x00_hcd
*c67x00
;
354 hcd
= usb_create_hcd(&c67x00_hc_driver
, sie_dev(sie
), "c67x00_sie");
359 c67x00
= hcd_to_c67x00_hcd(hcd
);
361 spin_lock_init(&c67x00
->lock
);
364 INIT_LIST_HEAD(&c67x00
->list
[PIPE_ISOCHRONOUS
]);
365 INIT_LIST_HEAD(&c67x00
->list
[PIPE_INTERRUPT
]);
366 INIT_LIST_HEAD(&c67x00
->list
[PIPE_CONTROL
]);
367 INIT_LIST_HEAD(&c67x00
->list
[PIPE_BULK
]);
368 c67x00
->urb_count
= 0;
369 INIT_LIST_HEAD(&c67x00
->td_list
);
370 c67x00
->td_base_addr
= CY_HCD_BUF_ADDR
+ SIE_TD_OFFSET(sie
->sie_num
);
371 c67x00
->buf_base_addr
= CY_HCD_BUF_ADDR
+ SIE_BUF_OFFSET(sie
->sie_num
);
372 c67x00
->max_frame_bw
= MAX_FRAME_BW_STD
;
374 c67x00_ll_husb_init_host_port(sie
);
376 init_completion(&c67x00
->endpoint_disable
);
377 retval
= c67x00_sched_start_scheduler(c67x00
);
381 retval
= usb_add_hcd(hcd
, 0, 0);
383 dev_dbg(sie_dev(sie
), "%s: usb_add_hcd returned %d\n",
388 spin_lock_irqsave(&sie
->lock
, flags
);
389 sie
->private_data
= c67x00
;
390 sie
->irq
= c67x00_hcd_irq
;
391 spin_unlock_irqrestore(&sie
->lock
, flags
);
396 c67x00_sched_stop_scheduler(c67x00
);
403 /* may be called with controller, bus, and devices active */
404 void c67x00_hcd_remove(struct c67x00_sie
*sie
)
406 struct c67x00_hcd
*c67x00
= sie
->private_data
;
407 struct usb_hcd
*hcd
= c67x00_hcd_to_hcd(c67x00
);
409 c67x00_sched_stop_scheduler(c67x00
);