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