diff options
Diffstat (limited to 'drivers/usb/host/shost/shost_hcd.c')
-rw-r--r-- | drivers/usb/host/shost/shost_hcd.c | 912 |
1 files changed, 912 insertions, 0 deletions
diff --git a/drivers/usb/host/shost/shost_hcd.c b/drivers/usb/host/shost/shost_hcd.c new file mode 100644 index 0000000..f06b237 --- /dev/null +++ b/drivers/usb/host/shost/shost_hcd.c @@ -0,0 +1,912 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * @file s3c-otg-hcdi-hcd.c + * @brief implementation of structure hc_drive + * @version + * -# Jun 11,2008 v1.0 by SeungSoo Yang (ss1.yang@samsung.com) + * : Creating the initial version of this code + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) + * : Optimizing for performance + * -# Aug 18,2008 v1.3 by SeungSoo Yang (ss1.yang@samsung.com) + * : Modifying for successful rmmod & disconnecting + * @see None + ****************************************************************************/ +/**************************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ****************************************************************************/ + +/*Internal function of isr */ +static void process_port_intr(struct usb_hcd *hcd) +{ + hprt_t hprt; /* by ss1, clear_hprt; */ + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + hprt.d32 = read_reg_32(HPRT); + + otg_dbg(OTG_DBG_ISR, "Port Interrupt() : HPRT = 0x%x\n", hprt.d32); + + if (hprt.b.prtconndet) { + otg_dbg(true, "detect connection"); + + otghost->port_flag.b.port_connect_status_change = 1; + + if (hprt.b.prtconnsts) + otghost->port_flag.b.port_connect_status = 1; + + /* wake_lock(&otghost->wake_lock); */ + } + + + if (hprt.b.prtenchng) { + otg_dbg(true, "port enable/disable changed\n"); + otghost->port_flag.b.port_enable_change = 1; + } + + if (hprt.b.prtovrcurrchng) { + otg_dbg(true, "over current condition is changed\n"); + + if (hprt.b.prtovrcurract) { + otg_dbg(true, "port_over_current_change = 1\n"); + otghost->port_flag.b.port_over_current_change = 1; + + } else { + otghost->port_flag.b.port_over_current_change = 0; + } + /* defer otg power control into a kernel thread */ + queue_work(otghost->wq, &otghost->work); + } + + hprt.b.prtena = 0; /* prtena를 writeclear시키면 안됨. */ + /* hprt.b.prtpwr = 0; */ + hprt.b.prtrst = 0; + hprt.b.prtconnsts = 0; + + write_reg_32(HPRT, hprt.d32); +} + +/** + * void otg_handle_interrupt(void) + * + * @brief Main interrupt processing routine + * + * @param None + * + * @return None + * + * @remark + * + */ + +static inline void otg_handle_interrupt(struct usb_hcd *hcd) +{ + gintsts_t clearIntr = {.d32 = 0}; + gintsts_t gintsts = {.d32 = 0}; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + gintsts.d32 = read_reg_32(GINTSTS) & read_reg_32(GINTMSK); + + otg_dbg(OTG_DBG_ISR, "otg_handle_interrupt - GINTSTS=0x%8x\n", + gintsts.d32); + + if (gintsts.b.wkupintr) { + otg_dbg(true, "Wakeup Interrupt\n"); + clearIntr.b.wkupintr = 1; + } + + if (gintsts.b.disconnect) { + otg_dbg(true, "Disconnect Interrupt\n"); + otghost->port_flag.b.port_connect_status_change = 1; + otghost->port_flag.b.port_connect_status = 0; + clearIntr.b.disconnect = 1; + /* + wake_unlock(&otghost->wake_lock); + */ + } + + if (gintsts.b.conidstschng) { + otg_dbg(OTG_DBG_ISR, "Connect ID Status Change Interrupt\n"); + clearIntr.b.conidstschng = 1; + oci_init_mode(); + } + + if (gintsts.b.hcintr) { + /* Mask Channel Interrupt to prevent generating interrupt */ + otg_dbg(OTG_DBG_ISR, "Channel Interrupt\n"); + + if (!otghost->ch_halt) + do_transfer_checker(otghost); + } + + if (gintsts.b.portintr) { + /* Read Only */ + otg_dbg(true, "Port Interrupt\n"); + process_port_intr(hcd); + } + + + if (gintsts.b.otgintr) { + /* Read Only */ + otg_dbg(OTG_DBG_ISR, "OTG Interrupt\n"); + } + + if (gintsts.b.sofintr) { + /* otg_dbg(OTG_DBG_ISR, "SOF Interrupt\n"); */ + do_schedule(otghost); + clearIntr.b.sofintr = 1; + } + + if (gintsts.b.modemismatch) { + otg_dbg(OTG_DBG_ISR, "Mode Mismatch Interrupt\n"); + clearIntr.b.modemismatch = 1; + } + update_reg_32(GINTSTS, clearIntr.d32); +} + + + +/** + * otg_hcd_init_modules(struct sec_otghost *otghost) + * + * @brief call other modules' init functions + * + * @return PASS : If success + * FAIL : If fail + */ +static int otg_hcd_init_modules(struct sec_otghost *otghost) +{ + otg_dbg(OTG_DBG_OTGHCDI_HCD, "otg_hcd_init_modules\n"); + + spin_lock_init(&otghost->lock); + + init_transfer(); + init_scheduler(); + oci_init(otghost); + + return USB_ERR_SUCCESS; +}; + +/** + * void otg_hcd_deinit_modules(struct sec_otghost *otghost) + * + * @brief call other modules' de-init functions + * + * @return PASS : If success + * FAIL : If fail + */ +static void otg_hcd_deinit_modules(struct sec_otghost *otghost) +{ + unsigned long spin_lock_flag = 0; + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "otg_hcd_deinit_modules\n"); + + spin_lock_irqsave(&otghost->lock, spin_lock_flag); + + deinit_transfer(otghost); + + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); +} + +/** + * irqreturn_t (*s5pc110_otghcd_irq) (struct usb_hcd *hcd) + * + * @brief interrupt handler of otg irq + * + * @param [in] hcd : pointer of usb_hcd + * + * @return IRQ_HANDLED + */ +static irqreturn_t s5pc110_otghcd_irq(struct usb_hcd *hcd) +{ + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + otg_dbg(OTG_DBG_OTGHCDI_IRQ, "s5pc110_otghcd_irq\n"); + + spin_lock(&otghost->lock); + otg_handle_interrupt(hcd); + spin_unlock(&otghost->lock); + + return IRQ_HANDLED; +} + +/** + * int s5pc110_otghcd_start(struct usb_hcd *hcd) + * + * @brief initialize and start otg hcd + * + * @param [in] usb_hcd_p : pointer of usb_hcd + * + * @return USB_ERR_SUCCESS : If success + * USB_ERR_FAIL : If call fail + */ +static int s5pc110_otghcd_start(struct usb_hcd *usb_hcd_p) +{ + struct usb_bus *usb_bus_p; + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_start\n"); + + usb_bus_p = hcd_to_bus(usb_hcd_p); + + /* Initialize and connect root hub if one is not already attached */ + if (usb_bus_p->root_hub) { + otg_dbg(OTG_DBG_OTGHCDI_HCD, "OTG HCD Has Root Hub\n"); + + /* Inform the HUB driver to resume. */ + otg_usbcore_resume_roothub(); + } else { + otg_err(OTG_DBG_OTGHCDI_HCD, + "OTG HCD Does Not Have Root Hub\n"); + return USB_ERR_FAIL; + } + +/* #ifdef CONFIG_MACH_C1 */ +#if 0 + usb_hcd_p->poll_rh = 1; +#else + set_bit(HCD_FLAG_POLL_RH, &usb_hcd_p->flags); +#endif + usb_hcd_p->uses_new_polling = 1; + usb_hcd_p->has_tt = 1; + + /* init bus state before enable irq */ + usb_hcd_p->state = HC_STATE_RUNNING; + + oci_start(); /* enable irq */ + + return USB_ERR_SUCCESS; +} + +/** + * void s5pc110_otghcd_stop(struct usb_hcd *hcd) + * + * @brief deinitialize and stop otg hcd + * + * @param [in] hcd : pointer of usb_hcd + * + */ +static void s5pc110_otghcd_stop(struct usb_hcd *hcd) +{ + unsigned long spin_lock_flag = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_stop\n"); + + otg_hcd_deinit_modules(otghost); + + spin_lock_irqsave(&otghost->lock, spin_lock_flag); + + oci_stop(); + + root_hub_feature(hcd, 0, ClearPortFeature, USB_PORT_FEAT_POWER, NULL); + + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); +} + +/** + * void s5pc110_otghcd_shutdown(struct usb_hcd *hcd) + * + * @brief shutdown otg hcd + * + * @param [in] usb_hcd_p : pointer of usb_hcd + * + */ +static void s5pc110_otghcd_shutdown(struct usb_hcd *usb_hcd_p) +{ + unsigned long spin_lock_flag = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(usb_hcd_p); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_shutdown\n"); + otg_hcd_deinit_modules(otghost); + + spin_lock_irqsave(&otghost->lock, spin_lock_flag); + + oci_stop(); + root_hub_feature(usb_hcd_p, 0, ClearPortFeature, + USB_PORT_FEAT_POWER, NULL); + + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + + free_irq(IRQ_OTG, usb_hcd_p); + usb_hcd_p->state = HC_STATE_HALT; + otg_usbcore_hc_died(); +} + + +/** + * int s5pc110_otghcd_get_frame_number(struct usb_hcd *hcd) + * + * @brief get currnet frame number + * + * @param [in] hcd : pointer of usb_hcd + * + * @return ret : frame number + */ +static int s5pc110_otghcd_get_frame_number(struct usb_hcd *hcd) +{ + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + unsigned long spin_lock_flag = 0; + int ret = 0; + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_get_frame_number\n"); + + spin_lock_irqsave(&otghost->lock, spin_lock_flag); + ret = oci_get_frame_num(); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + + return ret; +} + +int compare_ed(struct sec_otghost *otghost, void *hcpriv, struct urb *urb) +{ + struct ed *ped = NULL; + int ret = 0; + u32 update = 0; + + if (!hcpriv) + return ret; + + ped = (struct ed *)hcpriv; + + if (ped->ed_desc.device_addr != usb_pipedevice(urb->pipe)) { + ped->ed_desc.device_addr = usb_pipedevice(urb->pipe); + update |= 1 << 1; + } + + if (ped->ed_desc.endpoint_num != usb_pipeendpoint(urb->pipe)) { + ped->ed_desc.endpoint_num = usb_pipeendpoint(urb->pipe); + update |= 1 << 2; + } + + if (ped->ed_desc.is_ep_in != (usb_pipein(urb->pipe) ? 1 : 0)) { + ped->ed_desc.is_ep_in = usb_pipein(urb->pipe) ? 1 : 0; + update |= 1 << 3; + } + + if (ped->ed_desc.max_packet_size != + usb_maxpacket(urb->dev, urb->pipe, + !(usb_pipein(urb->pipe)))) { + update |= 1 << 4; + } + + if (update) + otg_dbg(1, "update ed %d (0x%x)\n", update, update); + + return ret; +} + +/** + * int s5pc110_otghcd_urb_enqueue() + * + * @brief enqueue a urb to otg hcd + * + * @param [in] hcd : pointer of usb_hcd + * [in] ep : pointer of usb_host_endpoint + * [in] urb : pointer of urb + * [in] mem_flags : type of gfp_t + * + * @return USB_ERR_SUCCESS : If success + * USB_ERR_FAIL : If call fail + */ +static int s5pc110_otghcd_urb_enqueue(struct usb_hcd *hcd, + struct urb *urb, + gfp_t mem_flags) +{ + int ret_val = 0; + u32 trans_flag = 0; + u32 return_td_addr = 0; + u8 dev_speed, ed_type = 0, additional_multi_count; + u16 max_packet_size; + + u8 dev_addr = 0; + u8 ep_num = 0; + bool f_is_ep_in = true; + u8 interval = 0; + u32 sched_frame = 0; + u8 hub_addr = 0; + u8 hub_port = 0; + bool f_is_do_split = false; + + struct ed *target_ed = NULL; + struct isoch_packet_desc *new_isoch_packet_desc = NULL; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + unsigned long spin_lock_flag = 0; + + if (!otghost && !otghost->port_flag.b.port_connect_status) { + otg_err(1, "Error : %s is zero\n", otghost ? + "Port status" : "otghost"); + return USB_ERR_NOIO; + } + + spin_lock_irqsave(&otghost->lock, spin_lock_flag); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "enqueue\n"); + if (compare_ed(otghost, urb->ep->hcpriv, urb)) { + otg_err(OTG_DBG_OTGHCDI_HCD, "compare ed error\n"); + pr_info("otg compare ed error\n"); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + return USB_ERR_FAIL; + } + + /* check ep has ed_t or not */ + if (!(urb->ep->hcpriv)) { + /* for getting dev_speed */ + switch (urb->dev->speed) { + case USB_SPEED_HIGH: + otg_dbg(OTG_DBG_OTGHCDI_HCD, "HS_OTG\n"); + dev_speed = HIGH_SPEED_OTG; + break; + + case USB_SPEED_FULL: + otg_dbg(OTG_DBG_OTGHCDI_HCD, "FS_OTG\n"); + dev_speed = FULL_SPEED_OTG; + break; + + case USB_SPEED_LOW: + otg_dbg(OTG_DBG_OTGHCDI_HCD, "LS_OTG\n"); + dev_speed = LOW_SPEED_OTG; + break; + + default: + otg_err(OTG_DBG_OTGHCDI_HCD, + "unKnown Device Speed\n"); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + return USB_ERR_FAIL; + } + + /* for getting ed_type */ + switch (usb_pipetype(urb->pipe)) { + case PIPE_BULK: + otg_dbg(OTG_DBG_OTGHCDI_HCD, "bulk transfer\n"); + ed_type = BULK_TRANSFER; + break; + + case PIPE_INTERRUPT: + otg_dbg(OTG_DBG_OTGHCDI_HCD, "interrupt transfer\n"); + ed_type = INT_TRANSFER; + break; + + case PIPE_CONTROL: + otg_dbg(OTG_DBG_OTGHCDI_HCD, "control transfer\n"); + ed_type = CONTROL_TRANSFER; + break; + + case PIPE_ISOCHRONOUS: + otg_dbg(OTG_DBG_OTGHCDI_HCD, "isochronous transfer\n"); + ed_type = ISOCH_TRANSFER; + break; + default: + otg_err(OTG_DBG_OTGHCDI_HCD, "unKnown ep type\n"); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + return USB_ERR_FAIL; + } + + max_packet_size = usb_maxpacket(urb->dev, urb->pipe, + !(usb_pipein(urb->pipe))); + + additional_multi_count = ((max_packet_size) >> 11) & 0x03; + dev_addr = usb_pipedevice(urb->pipe); + ep_num = usb_pipeendpoint(urb->pipe); + + f_is_ep_in = usb_pipein(urb->pipe) ? true : false; + interval = (u8)(urb->interval); + sched_frame = (u8)(urb->start_frame); + + /* check */ + if (urb->dev->tt == NULL) { + otg_dbg(OTG_DBG_OTGHCDI_HCD, "urb->dev->tt == NULL\n"); + hub_port = 0; /* u8 hub_port */ + hub_addr = 0; /* u8 hub_addr */ + + } else { + hub_port = (u8)(urb->dev->ttport); + if (urb->dev->tt->hub) { + if (((dev_speed == FULL_SPEED_OTG) || + (dev_speed == LOW_SPEED_OTG)) && + (urb->dev->tt) && + (urb->dev->tt->hub->devnum != 1)) { + f_is_do_split = true; + } + hub_addr = (u8)(urb->dev->tt->hub->devnum); + } + + if (urb->dev->tt->multi) + hub_addr = 0x80; + } + otg_dbg(OTG_DBG_OTGHCDI_HCD, + "dev_spped =%d, hub_port=%d, hub_addr=%d\n", + dev_speed, hub_port, hub_addr); + + ret_val = create_ed(&target_ed); + + if (ret_val != USB_ERR_SUCCESS) { + otg_err(OTG_DBG_OTGHCDI_HCD, + "fail to create_ed()\n"); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + return ret_val; + } + + ret_val = init_ed(target_ed, + dev_addr, + ep_num, + f_is_ep_in, + dev_speed, + ed_type, + max_packet_size, + additional_multi_count, + interval, + sched_frame, + hub_addr, + hub_port, + f_is_do_split, + (void *)urb->ep); + + if (ret_val != USB_ERR_SUCCESS) { + otg_err(OTG_DBG_OTGHCDI_HCD, + "fail to init_ed() :err = %d\n", (int)ret_val); + otg_mem_free(target_ed); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + return USB_ERR_FAIL; + } + + urb->ep->hcpriv = (void *)(target_ed); + + } else { /* if (!(ep->hcpriv)) */ + + dev_addr = usb_pipedevice(urb->pipe); + if (((struct ed *)(urb->ep->hcpriv))->ed_desc.device_addr + != dev_addr) + ((struct ed *)urb->ep->hcpriv)->ed_desc.device_addr + = dev_addr; + } + + target_ed = (struct ed *)urb->ep->hcpriv; + + if (urb->transfer_flags & URB_SHORT_NOT_OK) + trans_flag += USB_TRANS_FLAG_NOT_SHORT; + + if (urb->transfer_flags & URB_ISO_ASAP) + trans_flag += USB_TRANS_FLAG_ISO_ASYNCH; + + if (ed_type == ISOCH_TRANSFER) { + otg_err(OTG_DBG_OTGHCDI_HCD, "ISO not yet supported\n"); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + return USB_ERR_FAIL; + } + + if (!HC_IS_RUNNING(hcd->state)) { + otg_err(OTG_DBG_OTGHCDI_HCD, "!HC_IS_RUNNING(hcd->state)\n"); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + return -USB_ERR_NODEV; + } + + /* in case of unlink-during-submit */ + if (urb->status != -EINPROGRESS) { + otg_err(OTG_DBG_OTGHCDI_HCD, "urb->status is -EINPROGRESS\n"); + urb->hcpriv = NULL; + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + usb_hcd_giveback_urb(hcd, urb, urb->status); + return USB_ERR_SUCCESS; + } + + ret_val = issue_transfer(otghost, target_ed, (void *)NULL, (void *)NULL, + trans_flag, + (usb_pipetype(urb->pipe) == PIPE_CONTROL) ? true : false, + (u32)urb->setup_packet, (u32)urb->setup_dma, + (u32)urb->transfer_buffer, (u32)urb->transfer_dma, + (u32)urb->transfer_buffer_length, + (u32)urb->start_frame, (u32)urb->number_of_packets, + new_isoch_packet_desc, (void *)urb, &return_td_addr); + + if (ret_val != USB_ERR_SUCCESS) { + otg_err(OTG_DBG_OTGHCDI_HCD, "fail to issue_transfer()\n"); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + return USB_ERR_FAIL; + } + urb->hcpriv = (void *)return_td_addr; + + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + return USB_ERR_SUCCESS; +} + +/** + * int s5pc110_otghcd_urb_dequeue(struct usb_hcd *_hcd, struct urb *_urb ) + * + * @brief dequeue a urb to otg + * + * @param [in] _hcd : pointer of usb_hcd + * [in] _urb : pointer of urb + * + * @return USB_ERR_SUCCESS : If success + * USB_ERR_FAIL : If call fail + */ +static int s5pc110_otghcd_urb_dequeue( + struct usb_hcd *_hcd, struct urb *_urb, int status) +{ + int ret_val = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(_hcd); + + unsigned long spin_lock_flag = 0; + struct td *cancel_td = (struct td *)_urb->hcpriv; + + spin_lock_irqsave(&otghost->lock, spin_lock_flag); + + /* otg_dbg(OTG_DBG_OTGHCDI_HCD, "dequeue\n"); */ + + if (cancel_td == NULL) { + otg_err(OTG_DBG_OTGHCDI_HCD, "cancel_td is NULL\n"); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + return USB_ERR_FAIL; + } + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "dequeue status = %d\n", status); + + ret_val = usb_hcd_check_unlink_urb(_hcd, _urb, status); + if ((ret_val) && (ret_val != -EIDRM)) { + otg_dbg(OTG_DBG_OTGHCDI_HCD, "ret_val = %d\n", ret_val); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + return ret_val; + } + + if (!HC_IS_RUNNING(_hcd->state)) { + otg_err(OTG_DBG_OTGHCDI_HCD, "!HC_IS_RUNNING(hcd->state)\n"); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + otg_usbcore_giveback(cancel_td); + return USB_ERR_SUCCESS; + } + + ret_val = cancel_transfer(otghost, cancel_td->parent_ed_p, cancel_td); + if (ret_val != USB_ERR_DEQUEUED && ret_val != USB_ERR_NOELEMENT) { + otg_err(OTG_DBG_OTGHCDI_HCD, "fail to cancel_transfer()\n"); +/* otg_usbcore_giveback(cancel_td); */ + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + return USB_ERR_FAIL; + } + +/* otg_usbcore_giveback(cancel_td); */ + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + return USB_ERR_SUCCESS; +} + +/** + * void s5pc110_otghcd_endpoint_disable( + * struct usb_hcd *hcd, + * struct usb_host_endpoint *ep) + * + * @brief disable a endpoint + * + * @param [in] hcd : pointer of usb_hcd + * [in] ep : pointer of usb_host_endpoint + */ +static void s5pc110_otghcd_endpoint_disable( + struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + unsigned long spin_lock_flag = 0; + int ret_val = 0; + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_endpoint_disable\n"); + + if (!((struct ed *)ep->hcpriv)) + return; + + spin_lock_irqsave(&otghost->lock, spin_lock_flag); + ret_val = delete_ed(otghost, (struct ed *)ep->hcpriv); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + + if (ret_val != USB_ERR_SUCCESS) { + otg_err(OTG_DBG_OTGHCDI_HCD, "fail to delete_ed()\n"); + return; + } + + /* ep->hcpriv = NULL; delete_ed coveres it */ +} + +/** + * int s5pc110_otghcd_hub_status_data(struct usb_hcd *_hcd, char *_buf) + * + * @brief get status of root hub + * + * @param [in] _hcd : pointer of usb_hcd + * [inout] _buf : pointer of buffer for write a status data + * + * @return ret_val : return port status + */ +static int s5pc110_otghcd_hub_status_data(struct usb_hcd *_hcd, char *_buf) +{ + int ret_val = 0; + unsigned long spin_lock_flag = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(_hcd); + + /* otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_hub_status_data\n"); */ + + /* if !USB_SUSPEND, root hub timers won't get shut down ... */ + if (!HC_IS_RUNNING(_hcd->state)) { + otg_dbg(OTG_DBG_OTGHCDI_HCD, + "_hcd->state is NOT HC_IS_RUNNING\n"); + return 0; + } + + spin_lock_irqsave(&otghost->lock, spin_lock_flag); + ret_val = get_otg_port_status(_hcd, OTG_PORT_NUMBER, _buf); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + + return (int)ret_val; +} + +/** + * int s5pc110_otghcd_hub_control() + * + * @brief control root hub + * + * @param [in] hcd : pointer of usb_hcd + * [in] typeReq : type of control request + * [in] value : value + * [in] index : index + * [in] buf_p : pointer of urb + * [in] length : type of gfp_t + * + * @return ret_val : return root_hub_feature + */ +static int +s5pc110_otghcd_hub_control( + struct usb_hcd *hcd, + u16 typeReq, + u16 value, + u16 index, + char *buf_p, + u16 length) +{ + int ret_val = 0; + unsigned long spin_lock_flag = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_hub_control\n"); + + spin_lock_irqsave(&otghost->lock, spin_lock_flag); + + ret_val = root_hub_feature(hcd, OTG_PORT_NUMBER, + typeReq, value, (void *)buf_p); + + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + if (ret_val != USB_ERR_SUCCESS) { + otg_err(OTG_DBG_OTGHCDI_HCD, "fail to root_hub_feature()\n"); + return ret_val; + } + return (int)ret_val; +} + +/** + * int s5pc110_otghcd_bus_suspend(struct usb_hcd *hcd) + * + * @brief suspend otg hcd + * + * @param [in] hcd : pointer of usb_hcd + * + * @return USB_ERR_SUCCESS + */ +static int s5pc110_otghcd_bus_suspend(struct usb_hcd *hcd) +{ + unsigned long spin_lock_flag = 0; + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_bus_suspend\n"); + + spin_lock_irqsave(&otghost->lock, spin_lock_flag); + bus_suspend(); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + + return USB_ERR_SUCCESS; +} + +/** + * int s5pc110_otghcd_bus_resume(struct usb_hcd *hcd) + * + * @brief resume otg hcd + * + * @param [in] hcd : pointer of usb_hcd + * + * @return USB_ERR_SUCCESS + */ +static int s5pc110_otghcd_bus_resume(struct usb_hcd *hcd) +{ + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + unsigned long spin_lock_flag = 0; + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_bus_resume\n"); + + spin_lock_irqsave(&otghost->lock, spin_lock_flag); + + if (bus_resume(otghost) != USB_ERR_SUCCESS) + return USB_ERR_FAIL; + + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + return USB_ERR_SUCCESS; +} + +/** + * int s5pc110_otghcd_start_port_reset(struct usb_hcd *hcd, unsigned port) + * + * @brief reset otg port + * + * @param [in] hcd : pointer of usb_hcd + * [in] port : number of port + * + * @return USB_ERR_SUCCESS : If success + * ret_val : If call fail + */ +static int s5pc110_otghcd_start_port_reset(struct usb_hcd *hcd, unsigned port) +{ + struct sec_otghost *otghost = hcd_to_sec_otghost(hcd); + unsigned long spin_lock_flag = 0; + int ret_val = 0; + + otg_dbg(OTG_DBG_OTGHCDI_HCD, "s5pc110_otghcd_start_port_reset\n"); + + spin_lock_irqsave(&otghost->lock, spin_lock_flag); + ret_val = reset_and_enable_port(hcd, OTG_PORT_NUMBER); + spin_unlock_irqrestore(&otghost->lock, spin_lock_flag); + + if (ret_val != USB_ERR_SUCCESS) { + otg_err(OTG_DBG_OTGHCDI_HCD, + "fail to reset_and_enable_port()\n"); + return ret_val; + } + return USB_ERR_SUCCESS; +} + + +/** + * @struct hc_driver s5pc110_otg_hc_driver + * + * @brief implementation of hc_driver for OTG HCD + * + * describe in detail + */ +static const struct hc_driver s5pc110_otg_hc_driver = { + .description = "EMSP_OTGHCD", + .product_desc = "S3C OTGHCD", + .hcd_priv_size = sizeof(struct sec_otghost), + + .irq = s5pc110_otghcd_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /** basic lifecycle operations */ + /* .reset = */ + .start = s5pc110_otghcd_start, + /* .suspend = */ + /* .resume = */ + .stop = s5pc110_otghcd_stop, + .shutdown = s5pc110_otghcd_shutdown, + + /** managing i/o requests and associated device resources */ + .urb_enqueue = s5pc110_otghcd_urb_enqueue, + .urb_dequeue = s5pc110_otghcd_urb_dequeue, + .endpoint_disable = s5pc110_otghcd_endpoint_disable, + + /** scheduling support */ + .get_frame_number = s5pc110_otghcd_get_frame_number, + + /** root hub support */ + .hub_status_data = s5pc110_otghcd_hub_status_data, + .hub_control = s5pc110_otghcd_hub_control, + /* .hub_irq_enable = */ + .bus_suspend = s5pc110_otghcd_bus_suspend, + .bus_resume = s5pc110_otghcd_bus_resume, + .start_port_reset = s5pc110_otghcd_start_port_reset, +}; + + |