aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/shost/shost_transferchecker_interrupt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/shost/shost_transferchecker_interrupt.c')
-rw-r--r--drivers/usb/host/shost/shost_transferchecker_interrupt.c617
1 files changed, 617 insertions, 0 deletions
diff --git a/drivers/usb/host/shost/shost_transferchecker_interrupt.c b/drivers/usb/host/shost/shost_transferchecker_interrupt.c
new file mode 100644
index 0000000..fa85721
--- /dev/null
+++ b/drivers/usb/host/shost/shost_transferchecker_interrupt.c
@@ -0,0 +1,617 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : IntTransferChecker.c
+ * [Description] : The Source file implements the external
+ * and internal functions of IntTransferChecker
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/19
+ * [Revision History]
+ * (1) 2008/06/18 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and implements functions of IntTransferChecker
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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
+ ****************************************************************************/
+
+
+
+/******************************************************************************/
+/*!
+ * @name u8 process_xfercompl_on_intr( struct td *result_td,
+ * struct hc_info *hcinfo)
+ *
+ *
+ * @brief this function deals with the xfercompl event
+ * the procedure of this function is as following
+ * 1. clears all bits of the channel' HCINT
+ * by using clear_ch_intr() of S3CIsr.
+ * 2. masks ack/nak(?)/datatglerr(?) bit of HCINTMSK
+ * 3. Resets the err_cnt of result_td.
+ * 4. updates the result_td->parent_ed_p->ed_status.
+ * IntDataTgl.
+ * 5. calculates the tranferred size by calling
+ * calc_transferred_size() on DATA_STAGE.
+ *
+ * @param [IN] result_td
+ * -indicates the pointer of the struct td to be mapped with the uChNum.
+ * [IN] HCRegData
+ * -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return DE_ALLOCATE -if USB Transfer is completed.
+ * RE_TRANSMIT -if need to retransmit the result_td.
+ */
+/******************************************************************************/
+static u8 process_xfercompl_on_intr(struct td *result_td,
+ struct hc_info *hcinfo)
+{
+ u8 ret_val = 0;
+
+ result_td->err_cnt = 0;
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_DataTglErr);
+
+ result_td->transferred_szie +=
+ calc_transferred_size(true, result_td, hcinfo);
+
+ if (result_td->transferred_szie == result_td->buf_size) {
+ /* at IN Transfer, short transfer is accepted. */
+ result_td->error_code = USB_ERR_STATUS_COMPLETE;
+ ret_val = DE_ALLOCATE;
+
+ } else {
+
+ if (result_td->parent_ed_p->ed_desc.is_ep_in &&
+ hcinfo->hc_size.b.xfersize) {
+
+ if (result_td->transfer_flag & USB_TRANS_FLAG_NOT_SHORT)
+ result_td->error_code =
+ USB_ERR_STATUS_SHORTREAD;
+ else
+ result_td->error_code = USB_ERR_STATUS_COMPLETE;
+ ret_val = DE_ALLOCATE;
+
+ } else {
+ update_data_toggle(result_td, hcinfo->hc_size.b.pid);
+ ret_val = RE_TRANSMIT;
+ }
+ }
+
+ update_data_toggle(result_td, hcinfo->hc_size.b.pid);
+
+ return ret_val;
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_ahb_on_intr(struct td *result_td,
+ * struct hc_info *hcinfo)
+ *
+ *
+ * @brief this function deals with theAHB Errorl event
+ * this function stop the channel to be executed
+ *
+ *
+ * @param [IN] result_td
+ * -indicates the pointer of the struct td to be mapped with the uChNum.
+ * [IN] HCRegData
+ * -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return DE_ALLOCATE
+ */
+/******************************************************************************/
+/* TODO: not used. removed */
+#if 0
+static u8 process_ahb_on_intr(struct td *result_td,
+ struct hc_info *hcinfo)
+{
+ result_td->err_cnt = 0;
+ result_td->error_code = USB_ERR_STATUS_AHBERR;
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_AHBErr);
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_DataTglErr);
+
+ result_td->transferred_szie +=
+ calc_transferred_size(false, result_td, hcinfo);
+ result_td->parent_ed_p->ed_status.is_ping_enable = false;
+
+ return DE_ALLOCATE;
+
+}
+#endif
+
+/******************************************************************************/
+/*!
+ * @name u8 process_stall_on_intr(struct td *result_td,
+ * struct hc_info *hcinfo)
+ *
+ *
+ * @brief this function deals with the Stall event
+ * when Stall is occured at Int Transfer, we should reset the PID as DATA0
+ *
+ * @param [IN] result_td
+ * -indicates the pointer of the struct td to be mapped with the uChNum.
+ * [IN] HCRegData
+ * -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return DE_ALLOCATE
+ */
+/******************************************************************************/
+static u8 process_stall_on_intr(struct td *result_td,
+ struct hc_info *hcinfo)
+{
+ result_td->err_cnt = 0;
+ result_td->error_code = USB_ERR_STATUS_STALL;
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_DataTglErr);
+
+ result_td->transferred_szie +=
+ calc_transferred_size(false, result_td, hcinfo);
+
+ update_data_toggle(result_td, DATA0);
+
+ return DE_ALLOCATE;
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_nak_on_intr(struct td *result_td,
+ * struct hc_info *hcinfo)
+ *
+ *
+ * @brief this function deals with the nak event
+ * nak is occured at OUT/IN Transaction of Interrupt Transfer.
+ * we can't use ping protocol on Interrupt Transfer.
+ * and Syonopsys OTG IP occures
+ * chhltd interrupt on nak of IN/OUT Transaction.
+ * So we should retransmit the transfer on IN Transfer.
+ * If nak is occured at IN Transaction,
+ * this function processes this interrupt as following.
+ * 1. resets the result_td->err_cnt.
+ * 2. masks ack/nak/DaaTglErr bit of HCINTMSK.
+ * 3. clears the nak bit of HCINT
+ * 4. calculates frame number to retransmit this Interrupt Transfer.
+ *
+ * If nak is occured at OUT Transaction,
+ * this function processes this interrupt as following.
+ * 1. all procedures of IN Transaction are executed.
+ * 2. calculates the size of the transferred data.
+ * 3. if the speed of USB Device is High-Speed, sets the ping protocol.
+ * 4. update the Toggle
+ * at OUT Transaction, this function check whether the speed of
+ * USB Device is High-Speed or not.
+ * if USB Device is High-Speed, then
+ * this function sets the ping protocol.
+ *
+ * @param [IN] result_td
+ * -indicates the pointer of the struct td to be mapped with the uChNum.
+ * [IN] HCRegData
+ * -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_SCHEDULE -if the direction of the Transfer is OUT
+ * NO_ACTION -if the direction of the Transfer is IN
+ */
+/******************************************************************************/
+static u8 process_nak_on_intr(struct td *result_td,
+ struct hc_info *hcinfo)
+{
+ result_td->err_cnt = 0;
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_DataTglErr);
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_NAK);
+
+
+ result_td->transferred_szie +=
+ calc_transferred_size(false, result_td, hcinfo);
+
+ update_data_toggle(result_td, hcinfo->hc_size.b.pid);
+
+ update_frame_number(result_td);
+
+ return RE_SCHEDULE;
+
+
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_ack_on_intr(struct td *result_td,
+ * struct hc_info *hcinfo)
+ *
+ *
+ * @brief this function deals with the ack event
+ * ack of IN/OUT Transaction don't need any retransmit.
+ * this function just resets result_td->err_cnt
+ * and masks ack/nak/DataTgl of HCINTMSK.
+ * finally, this function clears ack bit of HCINT
+ * and ed_status.is_ping_enable.
+ *
+ * @param [IN] result_td
+ * -indicates the pointer of the struct td to be mapped with the uChNum.
+ * [IN] HCRegData
+ * -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return NO_ACTION
+ */
+/******************************************************************************/
+static u8 process_ack_on_intr(struct td *result_td,
+ struct hc_info *hcinfo)
+{
+ result_td->err_cnt = 0;
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_DataTglErr);
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_ACK);
+
+ return NO_ACTION;
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_xacterr_on_intr(struct td *result_td,
+ * struct hc_info *hcinfo)
+ *
+ * @brief this function deals with the xacterr event
+ * xacterr is occured at OUT/IN Transaction and we should
+ * retransmit the USB Transfer
+ * if the Error Counter is less than the RETRANSMIT_THRESHOLD.
+ * the reasons of xacterr is Timeout/CRC error/false EOP.
+ * the procedure to process xacterr is as following.
+ * 1. increses the result_td->err_cnt
+ * 2. check whether the result_td->err_cnt is equal to 3.
+ * 3. unmasks ack/nak/datatglerr bit of HCINTMSK.
+ * 4. clears the xacterr bit of HCINT
+ * 5. calculates the size of the transferred data.
+ * 6. update the Data Toggle.
+ * 7. update the frame number to start retransmitting
+ * the Interrupt Transfer.
+ *
+ * @param [IN] result_td
+ * -indicates the pointer of the struct td to be mapped with the uChNum.
+ * [IN] HCRegData
+ * -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_SCHEDULE -if the error count is less than 3
+ * DE_ALLOCATE -if the error count is equal to 3
+ */
+/******************************************************************************/
+static u8 process_xacterr_on_intr(struct td *result_td,
+ struct hc_info *hcinfo)
+{
+ u8 ret_val = 0;
+
+ if (result_td->err_cnt < RETRANSMIT_THRESHOLD) {
+ result_td->cur_stransfer.hc_reg.hc_int_msk.d32 |=
+ (CH_STATUS_ACK + CH_STATUS_NAK + CH_STATUS_DataTglErr);
+ ret_val = RE_SCHEDULE;
+ result_td->err_cnt++ ;
+
+ } else {
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_DataTglErr);
+ ret_val = DE_ALLOCATE;
+ result_td->err_cnt = 0 ;
+ }
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_DataTglErr);
+
+ result_td->transferred_szie +=
+ calc_transferred_size(false, result_td, hcinfo);
+
+ update_data_toggle(result_td, hcinfo->hc_size.b.pid);
+
+ if (ret_val == RE_SCHEDULE)
+ update_frame_number(result_td);
+
+ return ret_val;
+}
+
+/******************************************************************************/
+/*!
+ * @name void process_bblerr_on_intr(struct td *result_td,
+ * struct hc_info *hcinfo)
+ *
+ * @brief this function deals with the Babble event
+ * babble error is occured when the USB device continues to send packets
+ * althrough EOP is occured. So Babble error is only occured
+ * at IN Transfer.
+ * when Babble Error is occured, we should stop the USB Transfer,
+ * and return the fact to Application.
+ *
+ * @param [IN] result_td
+ * -indicates the pointer of the struct td to be mapped with the uChNum.
+ * [IN] HCRegData
+ * -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return DE_ALLOCATE
+ */
+/******************************************************************************/
+static u8 process_bblerr_on_intr(struct td *result_td,
+ struct hc_info *hcinfo)
+{
+ if (!result_td->parent_ed_p->ed_desc.is_ep_in)
+ return NO_ACTION;
+
+ result_td->err_cnt = 0;
+ result_td->error_code = USB_ERR_STATUS_BBLERR;
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum, CH_STATUS_ALL);
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_DataTglErr);
+
+ result_td->transferred_szie +=
+ calc_transferred_size(false, result_td, hcinfo);
+ return DE_ALLOCATE;
+}
+
+/******************************************************************************/
+/*!
+ * @name u8 process_datatgl_on_intr( struct td *result_td,
+ * struct hc_info *hcinfo)
+ *
+ * @brief this function deals with the datatglerr event
+ * the datatglerr event is occured at IN Transfer,
+ * and the channel is not halted.
+ * this function just resets result_td->err_cnt
+ * and masks ack/nak/DataTgl of HCINTMSK.
+ * finally, this function clears datatglerr bit of HCINT.
+ *
+ * @param [IN] result_td
+ * -indicates the pointer of the struct td to be mapped with the uChNum.
+ * [IN] HCRegData
+ * -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_SCHEDULE
+ */
+/******************************************************************************/
+static u8 process_datatgl_on_intr(struct td *result_td,
+ struct hc_info *hcinfo)
+{
+ result_td->err_cnt = 0;
+
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_DataTglErr);
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_NAK);
+
+ result_td->transferred_szie +=
+ calc_transferred_size(false, result_td, hcinfo);
+
+ update_data_toggle(result_td, hcinfo->hc_size.b.pid);
+
+ update_frame_number(result_td);
+
+ return RE_SCHEDULE;
+}
+
+static u8 process_frmovrrun_on_intr(struct td *result_td,
+ struct hc_info *hcinfo)
+{
+
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_NAK);
+
+ update_data_toggle(result_td, hcinfo->hc_size.b.pid);
+
+ update_frame_number(result_td);
+
+ return RE_TRANSMIT;
+}
+
+
+/******************************************************************************/
+/*!
+ * @name u8 process_chhltd_on_intr(struct td *result_td,
+ * struct hc_info *HCRegData)
+ *
+ *
+ * @brief this function processes Channel Halt event
+ * firstly, this function checks the reason of the Channel Halt,
+ * and according to the reason,
+ * calls the sub-functions to process the result.
+ *
+ *
+ * @param [IN] result_td
+ * -indicates the pointer of the struct td to be mapped with the uChNum.
+ * [IN] HCRegData
+ * -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_TRANSMIT -if need to retransmit the result_td.
+ * RE_SCHEDULE -if need to reschedule the result_td.
+ * DE_ALLOCATE -if USB Transfer is completed.
+ */
+/******************************************************************************/
+static u8 process_chhltd_on_intr(struct td *result_td,
+ struct hc_info *hcinfo)
+{
+ if (result_td->parent_ed_p->ed_desc.is_ep_in) {
+ if (hcinfo->hc_int.b.xfercompl)
+ return process_xfercompl_on_intr(result_td, hcinfo);
+
+ else if (hcinfo->hc_int.b.stall)
+ return process_stall_on_intr(result_td, hcinfo);
+
+ else if (hcinfo->hc_int.b.bblerr)
+ return process_bblerr_on_intr(result_td, hcinfo);
+
+ else if (hcinfo->hc_int.b.nak)
+ return process_nak_on_intr(result_td, hcinfo);
+
+ else if (hcinfo->hc_int.b.datatglerr)
+ return process_datatgl_on_intr(result_td, hcinfo);
+
+ else if (hcinfo->hc_int.b.frmovrun)
+ return process_frmovrrun_on_intr(result_td, hcinfo);
+
+ else if (hcinfo->hc_int.b.xacterr)
+ return process_xacterr_on_intr(result_td, hcinfo);
+
+ else {
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_ALL);
+
+ mask_channel_interrupt(result_td->cur_stransfer.
+ alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.
+ alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.
+ alloc_chnum, CH_STATUS_DataTglErr);
+ return RE_TRANSMIT;
+ }
+
+ } else {
+ if (hcinfo->hc_int.b.xfercompl)
+ return process_xfercompl_on_intr(result_td, hcinfo);
+
+ else if (hcinfo->hc_int.b.stall)
+ return process_stall_on_intr(result_td, hcinfo);
+
+ else if (hcinfo->hc_int.b.nak)
+ return process_nak_on_intr(result_td, hcinfo);
+
+ else if (hcinfo->hc_int.b.frmovrun)
+ return process_frmovrrun_on_intr(result_td, hcinfo);
+
+ else if (hcinfo->hc_int.b.xacterr)
+ return process_xacterr_on_intr(result_td, hcinfo);
+
+ else {
+ clear_ch_intr(result_td->cur_stransfer.alloc_chnum,
+ CH_STATUS_ALL);
+
+ mask_channel_interrupt(result_td->cur_stransfer.
+ alloc_chnum, CH_STATUS_ACK);
+ mask_channel_interrupt(result_td->cur_stransfer.
+ alloc_chnum, CH_STATUS_NAK);
+ mask_channel_interrupt(result_td->cur_stransfer.
+ alloc_chnum, CH_STATUS_DataTglErr);
+
+ result_td->err_cnt++;
+
+ if (result_td->err_cnt == 3) {
+ result_td->error_code = USB_ERR_STATUS_XACTERR;
+ result_td->err_cnt = 0;
+ return DE_ALLOCATE;
+ }
+
+ return RE_TRANSMIT;
+ }
+ }
+}
+
+
+
+/******************************************************************************/
+/*!
+ * @name u8 process_intr_transfer(struct td *result_td,
+ * struct hc_info *HCRegData)
+ *
+ * @brief this function processes the result of the Interrupt Transfer.
+ * firstly, this function checks the result of the Interrupt Transfer.
+ * and according to the result, calls the sub-functions
+ * to process the result.
+ *
+ *
+ * @param [IN] result_td
+ * -indicates the pointer of the struct td whose channel is interruped.
+ * [IN] HCRegData
+ * -indicates the interrupt information of the Channel to be interrupted
+ *
+ * @return RE_TRANSMIT -if need to retransmit the result_td.
+ * RE_SCHEDULE -if need to reschedule the result_td.
+ * DE_ALLOCATE -if USB Transfer is completed.
+ * NO_ACTION -if we don't need any action,
+ */
+/******************************************************************************/
+static u8 process_intr_transfer(struct td *result_td,
+ struct hc_info *hcinfo)
+{
+ hcintn_t hc_intr_info;
+ u8 ret_val = 0;
+
+ hc_intr_info.d32 = hcinfo->hc_int.d32 &
+ result_td->cur_stransfer.hc_reg.hc_int_msk.d32;
+
+ if (result_td->parent_ed_p->ed_desc.is_ep_in) {
+
+ if (hc_intr_info.b.chhltd)
+ ret_val = process_chhltd_on_intr(result_td, hcinfo);
+
+ else if (hc_intr_info.b.ack)
+ ret_val = process_ack_on_intr(result_td, hcinfo);
+
+ } else {
+ if (hc_intr_info.b.chhltd)
+ ret_val = process_chhltd_on_intr(result_td, hcinfo);
+
+ else if (hc_intr_info.b.ack)
+ ret_val = process_ack_on_intr(result_td, hcinfo);
+ }
+
+ return ret_val;
+}
+
+