Amol Jadi | f3d5a89 | 2013-07-23 16:09:44 -0700 | [diff] [blame] | 1 | /* Copyright (c) 2013, The Linux Foundation. All rights reserved. |
| 2 | * |
| 3 | * Redistribution and use in source and binary forms, with or without |
| 4 | * modification, are permitted provided that the following conditions are |
| 5 | * met: |
| 6 | * * Redistributions of source code must retain the above copyright |
| 7 | * notice, this list of conditions and the following disclaimer. |
| 8 | * * Redistributions in binary form must reproduce the above |
| 9 | * copyright notice, this list of conditions and the following |
| 10 | * disclaimer in the documentation and/or other materials provided |
| 11 | * with the distribution. |
| 12 | * * Neither the name of The Linux Foundation nor the names of its |
| 13 | * contributors may be used to endorse or promote products derived |
| 14 | * from this software without specific prior written permission. |
| 15 | * |
| 16 | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| 20 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| 23 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| 25 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| 26 | * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | */ |
| 28 | #include <debug.h> |
| 29 | #include <reg.h> |
| 30 | #include <bits.h> |
| 31 | #include <string.h> |
| 32 | #include <malloc.h> |
| 33 | #include <stdlib.h> |
| 34 | #include <arch/defines.h> |
| 35 | #include <platform/timer.h> |
| 36 | #include <platform/interrupts.h> |
| 37 | #include <platform/irqs.h> |
| 38 | #include <kernel/event.h> |
| 39 | #include <usb30_dwc_hwio.h> |
| 40 | #include <usb30_dwc.h> |
| 41 | #include <usb30_dwc_hw.h> |
| 42 | |
| 43 | extern char* ss_link_state_lookup[20]; |
| 44 | extern char* hs_link_state_lookup[20]; |
| 45 | extern char* event_lookup_device[20]; |
| 46 | extern char* event_lookup_ep[20]; |
| 47 | extern char* dev_ctrl_state_lookup[20]; |
| 48 | extern char* ep_state_lookup[20]; |
| 49 | extern char* dev_state_lookup[20]; |
| 50 | extern char* speed_lookup[20]; |
| 51 | |
| 52 | //#define DEBUG_USB |
| 53 | |
| 54 | #ifdef DEBUG_USB |
| 55 | #define DBG(...) dprintf(ALWAYS, __VA_ARGS__) |
| 56 | #else |
| 57 | #define DBG(...) |
| 58 | #endif |
| 59 | |
| 60 | #define ERR(...) dprintf(ALWAYS, __VA_ARGS__) |
| 61 | |
| 62 | /* This file provides interface to interact with DWC hardware. This code |
| 63 | * does not maintain any soft states. It programs the h/w as requested by the |
| 64 | * APIs. |
| 65 | */ |
| 66 | |
| 67 | /* generic api to send endpoint command */ |
| 68 | static void dwc_ep_cmd(dwc_dev_t *dev, uint8_t ep_phy_num, dwc_ep_cmd_t *ep_cmd) |
| 69 | { |
| 70 | if(REG_READ_FIELDI(dev, GUSB2PHYCFG, 0, SUSPENDUSB20)) |
| 71 | { |
| 72 | /* this must be 0. see snps 6.3.2.5.8 */ |
| 73 | ASSERT(0); |
| 74 | } |
| 75 | |
| 76 | /* wait until previous command is in-active */ |
| 77 | while( REG_READ_FIELDI(dev, DEPCMD, ep_phy_num, CMDACT) == 1); |
| 78 | |
| 79 | /* clear cmd reg */ |
| 80 | REG_WRITEI(dev, DEPCMD, ep_phy_num, 0); |
| 81 | |
| 82 | /* write the command parameters */ |
| 83 | REG_WRITEI(dev, DEPCMDPAR2, ep_phy_num, ep_cmd->param2); |
| 84 | REG_WRITEI(dev, DEPCMDPAR1, ep_phy_num, ep_cmd->param1); |
| 85 | REG_WRITEI(dev, DEPCMDPAR0, ep_phy_num, ep_cmd->param0); |
| 86 | |
| 87 | /* command */ |
| 88 | REG_WRITE_FIELDI(dev, DEPCMD, ep_phy_num, CMDTYP, ep_cmd->cmd); |
| 89 | |
| 90 | if ((ep_cmd->cmd == DEPCMD_CMD_UPDATE_TRANSFER) || |
| 91 | (ep_cmd->cmd == DEPCMD_CMD_END_TRANSFER) || |
| 92 | (ep_cmd->cmd == DEPCMD_CMD_START_NEW_CONF)) |
| 93 | { |
| 94 | /* set the transfer resource index */ |
| 95 | REG_WRITE_FIELDI(dev, |
| 96 | DEPCMD, |
| 97 | ep_phy_num, |
| 98 | COMMANDPARAM, |
| 99 | ep_cmd->xfer_resource_index); |
| 100 | } |
| 101 | |
| 102 | /* command interrupt can be set only if device is in running state. */ |
| 103 | if(dwc_device_run_status(dev)) |
| 104 | { |
| 105 | REG_WRITE_FIELDI(dev, DEPCMD, ep_phy_num, CMDIOC, 1); |
| 106 | } |
| 107 | |
| 108 | DBG("\nEP CMD: ep = %d : 0x%05x " |
| 109 | "pram0 = 0x%08x param1 = 0x%08x param2 = 0x%08x", |
| 110 | ep_phy_num, |
| 111 | REG_READI(dev, DEPCMD, ep_phy_num) | (1 << 10), |
| 112 | ep_cmd->param0, |
| 113 | ep_cmd->param1, |
| 114 | ep_cmd->param2); |
| 115 | |
| 116 | /* set active */ |
| 117 | REG_WRITE_FIELDI(dev, DEPCMD, ep_phy_num, CMDACT, 1); |
| 118 | |
| 119 | /* Wait until active bit is cleared. |
| 120 | * This does not necessarily mean that command is executed. |
| 121 | * It only means a new command can be issued. |
| 122 | * We get an interrupt when command execution is complete. |
| 123 | */ |
| 124 | while( REG_READ_FIELDI(dev, DEPCMD, ep_phy_num, CMDACT) == 1); |
| 125 | } |
| 126 | |
| 127 | /* send start transfer command to the specified ep. |
| 128 | * assumes the trb are already populated. |
| 129 | */ |
| 130 | void dwc_ep_cmd_start_transfer(dwc_dev_t *dev, uint8_t ep_phy_num) |
| 131 | { |
| 132 | dwc_ep_cmd_t ep_cmd; |
| 133 | dwc_ep_t *ep = &dev->ep[DWC_EP_PHY_TO_INDEX(ep_phy_num)]; |
| 134 | |
| 135 | uint32_t td_addr_low = (uint32_t) ep->trb; |
| 136 | uint32_t td_addr_high = (uint32_t) 0x0; |
| 137 | |
| 138 | /* transfer descriptor (aka TRB list) address must be on 16 byte boundary.*/ |
| 139 | ASSERT((td_addr_low & 0xF) == 0); |
| 140 | ASSERT((td_addr_high & 0xF) == 0); |
| 141 | |
| 142 | /* set command */ |
| 143 | ep_cmd.cmd = DEPCMD_CMD_START_TRANSFER; |
| 144 | |
| 145 | /* set params */ |
| 146 | ep_cmd.param2 = 0; |
| 147 | ep_cmd.param1 = td_addr_low; |
| 148 | ep_cmd.param0 = td_addr_high; |
| 149 | |
| 150 | dwc_ep_cmd(dev, ep_phy_num, &ep_cmd); |
| 151 | |
| 152 | /* Note: On execution of this cmd, a ep command complete event occurs. |
| 153 | * this DEPEVT ep event returns a XferRscIdx - transfer resource |
| 154 | * index. That must be used to Update or End this transfer. |
| 155 | */ |
| 156 | DBG("\n START_TRANSFER: new EP phy_num = %d state is = %s", |
| 157 | ep_phy_num, ep_state_lookup[ep->state]); |
| 158 | } |
| 159 | |
| 160 | /* end transfer on a particular endpoint */ |
| 161 | void dwc_ep_cmd_end_transfer(dwc_dev_t *dev, uint8_t ep_phy_num) |
| 162 | { |
| 163 | dwc_ep_cmd_t ep_cmd; |
| 164 | |
| 165 | dwc_ep_t *ep = &dev->ep[DWC_EP_PHY_TO_INDEX(ep_phy_num)]; |
| 166 | |
| 167 | ep_cmd.cmd = 0; |
| 168 | |
| 169 | /* set cmd and the resource index */ |
| 170 | ep_cmd.cmd = DEPCMD_CMD_END_TRANSFER; |
| 171 | ep_cmd.xfer_resource_index = ep->resource_idx; |
| 172 | |
| 173 | /* params */ |
| 174 | ep_cmd.param2 = 0; |
| 175 | ep_cmd.param1 = 0; |
| 176 | ep_cmd.param0 = 0; |
| 177 | |
| 178 | /* note: TRB status is not updated by the h/w when end transfer is issued. |
| 179 | * snps: 6.3.2.5.7 |
| 180 | */ |
| 181 | dwc_ep_cmd(dev, ep_phy_num, &ep_cmd); |
| 182 | } |
| 183 | |
| 184 | /* set number of transfer resources to be used for the ep. */ |
| 185 | void dwc_ep_cmd_set_transfer_resource(dwc_dev_t *dev, uint8_t ep_phy_num) |
| 186 | { |
| 187 | dwc_ep_cmd_t ep_cmd; |
| 188 | |
| 189 | /* set command */ |
| 190 | ep_cmd.cmd = DEPCMD_CMD_SET_TR_CONF; |
| 191 | |
| 192 | ep_cmd.param2 = 0; |
| 193 | ep_cmd.param1 = 0; |
| 194 | ep_cmd.param0 = 1; /* number of transfer resources: always set to 1 */ |
| 195 | |
| 196 | dwc_ep_cmd(dev, ep_phy_num, &ep_cmd); |
| 197 | } |
| 198 | |
| 199 | /* Configure end point in the core before starting to use it. The following |
| 200 | parameters need to be configured: |
| 201 | - usb ep number |
| 202 | - ep direction |
| 203 | - ep type |
| 204 | - mak pkt size |
| 205 | - burst size |
| 206 | - transfer events to be generated for this ep |
| 207 | - for IN ep, tx fifo to be used |
| 208 | */ |
| 209 | void dwc_ep_cmd_set_config(dwc_dev_t *dev, uint8_t index, uint8_t action) |
| 210 | { |
| 211 | uint8_t ep_phy_num; |
| 212 | uint8_t ep_usb_num; |
| 213 | uint8_t ep_direction; |
| 214 | uint16_t max_pkt_size; |
| 215 | uint32_t burst_size; |
| 216 | uint8_t tx_fifo_num; |
| 217 | |
| 218 | dwc_ep_t ep; |
| 219 | dwc_ep_cmd_t ep_cmd; |
| 220 | dwc_ep_type_t ep_type; |
| 221 | |
| 222 | ep = dev->ep[index]; |
| 223 | |
| 224 | /* get the corresponding physical ep number */ |
| 225 | ep_phy_num = ep.phy_num; |
| 226 | ep_usb_num = ep.number; |
| 227 | ep_direction = ep.dir; |
| 228 | ep_type = ep.type; |
| 229 | max_pkt_size = ep.max_pkt_size; |
| 230 | burst_size = ep.burst_size; |
| 231 | tx_fifo_num = ep.tx_fifo_num; |
| 232 | |
| 233 | /* set command */ |
| 234 | ep_cmd.cmd = DEPCMD_CMD_SET_EP_CONF; |
| 235 | ep_cmd.param2 = 0x0; |
| 236 | ep_cmd.param1 = 0x0; |
| 237 | ep_cmd.param0 = 0x0; |
| 238 | |
| 239 | /* TODO: set bInterval according to ep value. |
| 240 | * ignored since it is not used for bulk. |
| 241 | */ |
| 242 | |
| 243 | /* map this usb ep to the ep_phy_num */ |
| 244 | ep_cmd.param1 |= ep_usb_num << DEPCMDPAR1_USB_EP_NUM_BIT; |
| 245 | ep_cmd.param1 |= ep_direction << DEPCMDPAR1_USB_EP_DIR_BIT; |
| 246 | |
| 247 | /* enable event generation */ |
| 248 | ep_cmd.param1 |= BIT(DEPCMDPAR2_XFER_N_RDY_BIT); |
| 249 | ep_cmd.param1 |= BIT(DEPCMDPAR2_XFER_COMPLETE_BIT); |
| 250 | |
| 251 | /* interrupt number: which event buffer to be used. */ |
| 252 | ep_cmd.param1 |= 0; |
| 253 | |
| 254 | /* action: 0 = initialize */ |
| 255 | ep_cmd.param0 |= (action << DEPCMDPAR0_ACTION_BIT); |
| 256 | /* burst size */ |
| 257 | ep_cmd.param0 |= (burst_size << DEPCMDPAR0_BURST_SIZE_BIT); |
| 258 | |
| 259 | ep_cmd.param0 |= tx_fifo_num << DEPCMDPAR0_FIFO_NUM_BIT; |
| 260 | ep_cmd.param0 |= ep_type << DEPCMDPAR0_EP_TYPE_BIT; |
| 261 | ep_cmd.param0 |= max_pkt_size << DEPCMDPAR0_MAX_PKT_SIZE_BIT; |
| 262 | |
| 263 | dwc_ep_cmd(dev, ep_phy_num, &ep_cmd); |
| 264 | } |
| 265 | |
| 266 | /* send stall command to ep */ |
| 267 | void dwc_ep_cmd_stall(dwc_dev_t *dev, uint8_t ep_phy_num) |
| 268 | { |
| 269 | dwc_ep_cmd_t ep_cmd; |
| 270 | |
| 271 | /* set command */ |
| 272 | ep_cmd.cmd = DEPCMD_CMD_SET_STALL; |
| 273 | |
| 274 | ep_cmd.param2 = 0; |
| 275 | ep_cmd.param1 = 0; |
| 276 | ep_cmd.param0 = 0; |
| 277 | |
| 278 | DBG("\nSTALLING.... ep_phy_num = %d\n", ep_phy_num); |
| 279 | |
| 280 | dwc_ep_cmd(dev, ep_phy_num, &ep_cmd); |
| 281 | } |
| 282 | |
| 283 | /* clear stall */ |
| 284 | void dwc_ep_cmd_clear_stall(dwc_dev_t *dev, uint8_t ep_phy_num) |
| 285 | { |
| 286 | dwc_ep_cmd_t ep_cmd; |
| 287 | |
| 288 | /* set command */ |
| 289 | ep_cmd.cmd = DEPCMD_CMD_CLEAR_STALL; |
| 290 | |
| 291 | ep_cmd.param2 = 0; |
| 292 | ep_cmd.param1 = 0; |
| 293 | ep_cmd.param0 = 0; |
| 294 | |
| 295 | dwc_ep_cmd(dev, ep_phy_num, &ep_cmd); |
| 296 | } |
| 297 | |
| 298 | /* send a start new config command */ |
| 299 | void dwc_ep_cmd_start_new_config(dwc_dev_t *dev, |
| 300 | uint8_t ep_phy_num, |
| 301 | uint8_t resource_idx) |
| 302 | { |
| 303 | dwc_ep_cmd_t ep_cmd; |
| 304 | |
| 305 | /* set command */ |
| 306 | ep_cmd.cmd = DEPCMD_CMD_START_NEW_CONF; |
| 307 | ep_cmd.xfer_resource_index = resource_idx; |
| 308 | |
| 309 | ep_cmd.param2 = 0; |
| 310 | ep_cmd.param1 = 0; |
| 311 | ep_cmd.param0 = 0; |
| 312 | |
| 313 | dwc_ep_cmd(dev, ep_phy_num, &ep_cmd); |
| 314 | } |
| 315 | |
| 316 | /******************** DWC Device related APIs *********************************/ |
| 317 | |
| 318 | /* generic api to send device command */ |
| 319 | static void dwc_device_cmd(dwc_dev_t *dev, dwc_device_cmd_t *cmd) |
| 320 | { |
| 321 | uint8_t active = REG_READ_FIELD(dev, DGCMD, CMDACT); |
| 322 | |
| 323 | ASSERT(active); |
| 324 | |
| 325 | REG_WRITE(dev, DGCMDPAR, cmd->param); |
| 326 | REG_WRITE_FIELD(dev, DGCMD, CMDTYP, cmd->cmd); |
| 327 | |
| 328 | /* wait until active field is cleared. */ |
| 329 | while(!REG_READ_FIELD(dev, DGCMD, CMDACT)); |
| 330 | |
| 331 | if(REG_READ_FIELD(dev, DGCMD, CMDSTATUS)) |
| 332 | { |
| 333 | ERR("\n\n device command failed. \n\n"); |
| 334 | ASSERT(0); |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | /* set periodic param */ |
| 339 | void dwc_device_set_periodic_param(dwc_dev_t *dev, uint32_t val) |
| 340 | { |
| 341 | dwc_device_cmd_t cmd; |
| 342 | |
| 343 | cmd.cmd = DWC_DEV_CMD_SET_PERIODIC_PARAMS_VAL; |
| 344 | cmd.param = val; |
| 345 | |
| 346 | /* send device command to set period param value */ |
| 347 | dwc_device_cmd(dev, &cmd); |
| 348 | } |
| 349 | |
| 350 | /* set device address */ |
| 351 | void dwc_device_set_addr(dwc_dev_t *dev, uint16_t addr) |
| 352 | { |
| 353 | REG_WRITE_FIELD(dev, DCFG, DEVADDR, addr); |
| 354 | } |
| 355 | |
| 356 | /* reset device */ |
| 357 | void dwc_device_reset(dwc_dev_t *dev) |
| 358 | { |
| 359 | /* start reset */ |
| 360 | REG_WRITE_FIELD(dev, DCTL, CSFTRST, 1); |
| 361 | |
| 362 | /* wait until done */ |
| 363 | while(REG_READ_FIELD(dev, DCTL, CSFTRST)); |
| 364 | } |
| 365 | |
| 366 | /* Run/Stop device: 1 == run. 0 == stop */ |
| 367 | void dwc_device_run(dwc_dev_t *dev, uint8_t run) |
| 368 | { |
| 369 | REG_WRITE_FIELD(dev, DCTL, RUN_STOP, run); |
| 370 | } |
| 371 | |
| 372 | /* is device running? */ |
| 373 | uint8_t dwc_device_run_status(dwc_dev_t *dev) |
| 374 | { |
| 375 | return REG_READ_FIELD(dev, DCTL, RUN_STOP); |
| 376 | } |
| 377 | |
| 378 | /******************** Managing various events *********************************/ |
| 379 | /* event init: |
| 380 | program event buffer address, size and reset event count to 0. |
| 381 | */ |
| 382 | void dwc_event_init(dwc_dev_t *dev) |
| 383 | { |
| 384 | /* snps 8.2.2 */ |
| 385 | |
| 386 | /* event buffer address */ |
| 387 | REG_WRITEI(dev, GEVNTADRLO, 0, (uint32_t) dev->event_buf.buf); |
| 388 | REG_WRITEI(dev, GEVNTADRHI, 0, 0x0); |
| 389 | |
| 390 | /* set buffer size. assuming interrupt is always needed on new event, |
| 391 | * bit 31 is not set. |
| 392 | */ |
| 393 | REG_WRITEI(dev, GEVNTSIZ, 0, dev->event_buf.buf_size); |
| 394 | |
| 395 | /* reset count */ |
| 396 | REG_WRITEI(dev, GEVNTCOUNT, 0, 0x0); |
| 397 | } |
| 398 | |
| 399 | /* event update index */ |
| 400 | static void dwc_event_update_index(uint16_t *index, uint16_t max_count) |
| 401 | { |
| 402 | if(*index == max_count) |
| 403 | { |
| 404 | /* we have read the last entry. Need to roll over for next reading.*/ |
| 405 | *index = 0; |
| 406 | } |
| 407 | else |
| 408 | { |
| 409 | *index += 1; |
| 410 | } |
| 411 | } |
| 412 | |
| 413 | /* Returns next event from event queue and the size of event |
| 414 | * Event buffer is a circular buffer that the hardware populates when any of |
| 415 | * the enabled event occurs. An interrupt is generated if interrupt is enabled |
| 416 | * for that event. |
| 417 | * This api returns the next valid event from the event buffer and updates event |
| 418 | * buffer index. |
| 419 | * Most events are 4 byte long |
| 420 | * Note: caller must provide at least 12 bytes buffer in case the |
| 421 | * next event is the special 12 byte event. |
| 422 | */ |
| 423 | uint16_t dwc_event_get_next(dwc_dev_t *dev, uint32_t *event) |
| 424 | { |
| 425 | uint16_t count; |
| 426 | uint16_t event_size = 0; |
| 427 | uint32_t *buf; |
| 428 | |
| 429 | /* read the number of valid event data in event buffer. */ |
| 430 | count = REG_READI(dev, GEVNTCOUNT, 0); |
| 431 | |
| 432 | if(count == 0) |
| 433 | { |
| 434 | /* no events in buffer. */ |
| 435 | return event_size; |
| 436 | } |
| 437 | |
| 438 | /* each event is at least 4 bytes long. |
| 439 | * make sure there is at least one event to read. |
| 440 | */ |
| 441 | ASSERT(count >= 4); |
| 442 | |
| 443 | /* get event buffer for this device */ |
| 444 | buf = dev->event_buf.buf; |
| 445 | |
| 446 | /* invalidate cached event buf data */ |
| 447 | arch_invalidate_cache_range((addr_t) (buf + dev->event_buf.index), 4); |
| 448 | |
| 449 | /* read next event */ |
| 450 | *event = buf[dev->event_buf.index]; |
| 451 | event_size += 4; |
| 452 | dwc_event_update_index(&dev->event_buf.index, dev->event_buf.max_index); |
| 453 | |
| 454 | |
| 455 | /* is this buffer overflow event? */ |
| 456 | if((DWC_EVENT_DEVICE_EVENT_ID(*event) == DWC_EVENT_DEVICE_EVENT_ID_BUFFER_OVERFLOW)) |
| 457 | { |
| 458 | /* ouch... */ |
| 459 | ERR("\n Event buffer is full. Need to increase size.\n"); |
| 460 | ASSERT(0); |
| 461 | } |
| 462 | |
| 463 | /* check for that special 12 byte event */ |
| 464 | if( DWC_EVENT_IS_DEVICE_EVENT(*event) & |
| 465 | (DWC_EVENT_DEVICE_EVENT_ID(*event) == DWC_EVENT_DEVICE_EVENT_ID_VENDOR_DEVICE_TEST_LMP)) |
| 466 | { |
| 467 | /* invalidate cached event buf data */ |
| 468 | arch_invalidate_cache_range((addr_t) (buf + dev->event_buf.index), 4); |
| 469 | |
| 470 | *(event + 1) = buf[dev->event_buf.index]; |
| 471 | event_size += 4; |
| 472 | dwc_event_update_index(&dev->event_buf.index, dev->event_buf.buf_size); |
| 473 | |
| 474 | /* invalidate cached event buf data */ |
| 475 | arch_invalidate_cache_range((addr_t) (buf + dev->event_buf.index), 4); |
| 476 | |
| 477 | *(event + 1) = buf[dev->event_buf.index]; |
| 478 | event_size += 4; |
| 479 | dwc_event_update_index(&dev->event_buf.index, dev->event_buf.buf_size); |
| 480 | } |
| 481 | |
| 482 | return event_size; |
| 483 | } |
| 484 | |
| 485 | /* Lets h/w know that we have processed "count" bytes of data from event buffer |
| 486 | * and it can use that space for new events. |
| 487 | * This must be done only after the event is processed. |
| 488 | */ |
| 489 | void dwc_event_processed(dwc_dev_t *dev, uint16_t count) |
| 490 | { |
| 491 | REG_WRITEI(dev, GEVNTCOUNT, 0, count); |
| 492 | } |
| 493 | |
| 494 | /* enable device event generation: |
| 495 | * events - bit map of events defined in dwc_event_device_event_id_t |
| 496 | */ |
| 497 | void dwc_event_device_enable(dwc_dev_t *dev, uint32_t events) |
| 498 | { |
| 499 | REG_WRITE(dev, DEVTEN, events); |
| 500 | } |
| 501 | |
| 502 | /*************** Generic APIs affecting overall controller ********************/ |
| 503 | |
| 504 | /* reset HS and SS PHY's digital interface: UTMI + PIPE3 */ |
| 505 | void dwc_phy_digital_reset(dwc_dev_t *dev) |
| 506 | { |
| 507 | REG_WRITE_FIELDI(dev, GUSB2PHYCFG, 0, PHYSOFTRST, 1); |
| 508 | REG_WRITE_FIELDI(dev, GUSB3PIPECTL, 0, PHYSOFTRST, 1); |
| 509 | |
| 510 | /* per HPG */ |
| 511 | udelay(100); |
| 512 | |
| 513 | REG_WRITE_FIELDI(dev, GUSB2PHYCFG, 0, PHYSOFTRST, 0); |
| 514 | REG_WRITE_FIELDI(dev, GUSB3PIPECTL, 0, PHYSOFTRST, 0); |
| 515 | |
| 516 | /* per HPG */ |
| 517 | udelay(100); |
| 518 | } |
| 519 | |
| 520 | void dwc_usb2_phy_soft_reset(dwc_dev_t *dev) |
| 521 | { |
| 522 | REG_WRITE_FIELDI(dev, GUSB2PHYCFG, 0, PHYSOFTRST, 1); |
| 523 | |
| 524 | udelay(100); |
| 525 | |
| 526 | REG_WRITE_FIELDI(dev, GUSB2PHYCFG, 0, PHYSOFTRST, 0); |
| 527 | |
| 528 | udelay(100); |
| 529 | } |
| 530 | |
| 531 | /* workaround_12 as described in HPG */ |
| 532 | void dwc_ss_phy_workaround_12(dwc_dev_t *dev) |
| 533 | { |
| 534 | /* 12. */ |
| 535 | REG_WRITEI(dev, GUSB3PIPECTL, 0, 0x30C0003); |
| 536 | } |
| 537 | |
| 538 | /* AXI master config */ |
| 539 | void dwc_axi_master_config(dwc_dev_t *dev) |
| 540 | { |
| 541 | uint32_t reg = 0; |
| 542 | |
| 543 | reg = (DWC_GSBUSCFG0_INCR4BRSTENA_BMSK | |
| 544 | DWC_GSBUSCFG0_INCR8BRSTENA_BMSK | |
| 545 | DWC_GSBUSCFG0_INCR16BRSTENA_BMSK); |
| 546 | |
| 547 | REG_WRITE(dev, GSBUSCFG0, reg); |
| 548 | } |
| 549 | |
| 550 | /* read the controller id and version information */ |
| 551 | uint32_t dwc_coreid(dwc_dev_t *dev) |
| 552 | { |
| 553 | return REG_READ(dev, GSNPSID); |
| 554 | } |
| 555 | |
| 556 | /* read the current connection speed. */ |
| 557 | uint8_t dwc_connectspeed(dwc_dev_t *dev) |
| 558 | { |
| 559 | return REG_READ_FIELD(dev, DSTS, CONNECTSPD); |
| 560 | } |
| 561 | |
| 562 | /* disable all non-control EPs */ |
| 563 | void dwc_ep_disable_non_control(dwc_dev_t *dev) |
| 564 | { |
| 565 | uint32_t reg = REG_READ(dev, DALEPENA); |
| 566 | |
| 567 | /* clear all except the control IN and OUT ep */ |
| 568 | reg &= 0x3; |
| 569 | |
| 570 | REG_WRITE(dev, DALEPENA, reg); |
| 571 | } |
| 572 | |
| 573 | /* disable a specific ep */ |
| 574 | void dwc_ep_disable(dwc_dev_t *dev, uint8_t ep_phy_num) |
| 575 | { |
| 576 | uint32_t reg = REG_READ(dev, DALEPENA); |
| 577 | |
| 578 | reg &= ~BIT(ep_phy_num); |
| 579 | |
| 580 | REG_WRITE(dev, DALEPENA, reg); |
| 581 | } |
| 582 | |
| 583 | /* enable a specific ep */ |
| 584 | void dwc_ep_enable(dwc_dev_t *dev, uint8_t ep_phy_num) |
| 585 | { |
| 586 | uint32_t reg = REG_READ(dev, DALEPENA); |
| 587 | |
| 588 | reg |= BIT(ep_phy_num); |
| 589 | |
| 590 | REG_WRITE(dev, DALEPENA, reg); |
| 591 | } |
| 592 | |
| 593 | /* global reset of controller. |
| 594 | * 1 == put in reset. 0 == out of reset |
| 595 | */ |
| 596 | void dwc_reset(dwc_dev_t *dev, uint8_t reset) |
| 597 | { |
| 598 | /* snps databook table 6-11 indicates this field to be used only for debug |
| 599 | * purpose. use dctl.softreset instead for devide mode. |
| 600 | * but hpg 4.4.2. 8.a says use this. |
| 601 | */ |
| 602 | REG_WRITE_FIELD(dev, GCTL, CORESOFTRESET, reset); |
| 603 | |
| 604 | /* per HPG */ |
| 605 | udelay(100); |
| 606 | } |
| 607 | |
| 608 | /* initialize global control reg for device mode operation. |
| 609 | * sequence numbers are as described in HPG. |
| 610 | */ |
| 611 | void dwc_gctl_init(dwc_dev_t *dev) |
| 612 | { |
| 613 | /* 16. */ |
| 614 | /* a. default value is good for RAM clock */ |
| 615 | /* b. default value is good for Disable Debug Attach */ |
| 616 | REG_WRITE_FIELD(dev, GCTL, DEBUGATTACH, 0); |
| 617 | |
| 618 | /* c & d: disable loopback/local loopback |
| 619 | * TODO: possibly for older version. no such fields in GCTL |
| 620 | */ |
| 621 | |
| 622 | /* e. no soft reset. */ |
| 623 | REG_WRITE_FIELD(dev, GCTL, CORESOFTRESET, 0); |
| 624 | |
| 625 | /* f. set port capability direction: device */ |
| 626 | REG_WRITE_FIELD(dev, GCTL, PRTCAPDIR, 0x2); |
| 627 | |
| 628 | /* g. set scale down value */ |
| 629 | REG_WRITE_FIELD(dev, GCTL, FRMSCLDWN, 0x0); |
| 630 | |
| 631 | /* h. enable multiple attempts for SS connection */ |
| 632 | REG_WRITE_FIELD(dev, GCTL, U2RSTECN, 1); |
| 633 | |
| 634 | /* i. set power down scale of snps phy */ |
| 635 | REG_WRITE_FIELD(dev, GCTL, PWRDNSCALE, 0x2); |
| 636 | |
| 637 | /* j. clear SOFITPSYNC bit */ |
| 638 | REG_WRITE_FIELD(dev, GCTL, SOFITPSYNC, 0); |
| 639 | } |