diff options
author | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
---|---|---|
committer | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
commit | c6da2cfeb05178a11c6d062a06f8078150ee492f (patch) | |
tree | f3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/usb/host/shost/shost_schedulerlib.c | |
parent | c6d7c4dbff353eac7919342ae6b3299a378160a6 (diff) | |
download | kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2 |
samsung update 1
Diffstat (limited to 'drivers/usb/host/shost/shost_schedulerlib.c')
-rw-r--r-- | drivers/usb/host/shost/shost_schedulerlib.c | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/drivers/usb/host/shost/shost_schedulerlib.c b/drivers/usb/host/shost/shost_schedulerlib.c new file mode 100644 index 0000000..de17401 --- /dev/null +++ b/drivers/usb/host/shost/shost_schedulerlib.c @@ -0,0 +1,426 @@ +/**************************************************************************** + * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved + * + * [File Name] : Scheduler.c + * [Description] : The source file implements the internal + * functions of Scheduler. + * [Author] : Yang Soon Yeal { syatom.yang@samsung.com } + * [Department] : System LSI Division/System SW Lab + * [Created Date]: 2008/06/04 + * [Revision History] + * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com } + * - Created this file and implements functions of Scheduler + * -# Jul 15,2008 v1.2 by SeungSoo Yang (ss1.yang@samsung.com) + * : Optimizing for performance + * + ****************************************************************************/ +/**************************************************************************** + * 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 + ****************************************************************************/ + +/* the max periodic bus time is 80%*125us on High Speed Mode */ +static const u32 perio_highbustime_threshold = 100; + +/* the max periodic bus time is 90%*1000us(1ms) on Full/Low Speed Mode. */ +static const u32 perio_fullbustime_threshold = 900; + +static const u8 perio_chnum_threshold = 14; +/* static const u8 total_chnum_threshold = 16; */ +static u8 total_chnum_threshold = 16; + +static u32 perio_used_bustime; +static u8 perio_used_chnum; +static u8 nonperio_used_chnum; +static u8 total_used_chnum; +static u32 transferring_td_array[16]; + + +static int inc_perio_bus_time(u32 bus_time, u8 dev_speed) +{ + switch (dev_speed) { + + case HIGH_SPEED_OTG: + if ((bus_time + perio_used_bustime) <= + perio_highbustime_threshold) { + + perio_used_bustime += bus_time; + return USB_ERR_SUCCESS; + } else + return USB_ERR_FAIL; + + case LOW_SPEED_OTG: + case FULL_SPEED_OTG: + if ((bus_time + perio_used_bustime) <= + perio_fullbustime_threshold) { + + perio_used_bustime += bus_time; + return USB_ERR_SUCCESS; + } else + return USB_ERR_FAIL; + + case SUPER_SPEED_OTG: + break; + default: + break; + } + return USB_ERR_FAIL; +} + +static int dec_perio_bus_time(u32 bus_time) +{ + if (perio_used_bustime >= bus_time) { + perio_used_bustime -= bus_time; + return USB_ERR_SUCCESS; + } else + return USB_ERR_FAIL; +} + +static int inc_perio_chnum(void) +{ + if (perio_used_chnum < perio_chnum_threshold) { + if (total_used_chnum < total_chnum_threshold) { + perio_used_chnum++; + total_used_chnum++; + return USB_ERR_SUCCESS; + } + } + return USB_ERR_FAIL; +} + +static u8 get_avail_chnum(void) +{ + return total_chnum_threshold - total_used_chnum; +} + +static int dec_perio_chnum(void) +{ + if (perio_used_chnum > 0) { + if (total_used_chnum > 0) { + perio_used_chnum--; + total_used_chnum--; + return USB_ERR_SUCCESS; + } + } + return USB_ERR_FAIL; +} + +static int inc_non_perio_chnum(void) +{ + if (nonperio_used_chnum < total_chnum_threshold) { + if (total_used_chnum < total_chnum_threshold) { + nonperio_used_chnum++; + total_used_chnum++; + return USB_ERR_SUCCESS; + } + } + return USB_ERR_FAIL; +} + +static int dec_nonperio_chnum(void) +{ + if (nonperio_used_chnum > 0) { + if (total_used_chnum > 0) { + nonperio_used_chnum--; + total_used_chnum--; + return USB_ERR_SUCCESS; + } + } + return USB_ERR_FAIL; +} + +static int get_transferring_td_array(u8 chnum, unsigned int *td_addr) +{ + if (transferring_td_array[chnum] != 0) { + *td_addr = transferring_td_array[chnum]; + return USB_ERR_SUCCESS; + } + return USB_ERR_FAIL; +} + +static int set_transferring_td_array(u8 chnum, u32 td_addr) +{ + if (td_addr == 0) { + transferring_td_array[chnum] = td_addr; + return USB_ERR_SUCCESS; + } + + if (transferring_td_array[chnum] == 0) { + transferring_td_array[chnum] = td_addr; + return USB_ERR_SUCCESS; + } else + return USB_ERR_FAIL; +} + +/******************************************************************************/ +/*! + * @name int do_periodic_schedule(struct sec_otghost *otghost) + * + * @brief this function schedules PeriodicTransferReadyQ. + * this function checks whether PeriodicTransferReadyQ has some ed_t. + * if there are some ed_t on PeriodicTransferReadyQ + * , this function request to start USB Trasnfer to S3C6400OCI. + * + * + * @param void + * + * @return void + */ +/******************************************************************************/ +static void do_periodic_schedule(struct sec_otghost *otghost) +{ + struct ed *scheduling_ed = NULL; + int err_sched = USB_ERR_SUCCESS; + u32 sched_cnt = 0; + + otg_list_head *td_list_entry; + struct td *td; + u32 cur_frame_num = 0; + u8 alloc_ch; + + otg_dbg(OTG_DBG_SCHEDULE, "***** Start periodicSchedul *****\n"); + + sched_cnt = get_periodic_ready_q_entity_num(); + + while (sched_cnt) { + /* in periodic transfser, + the channel resource was already reserved. + So, we don't need this routine... */ + +start_sched_perio_transfer: + if (!sched_cnt) + goto end_sched_perio_transfer; + + otg_dbg(OTG_DBG_SCHEDULE, "sched_cnt : %d\n", sched_cnt); + + err_sched = get_ed_from_ready_q(&scheduling_ed, true); + + if (err_sched != USB_ERR_SUCCESS) { + /* there is no ED on PeriodicTransferQ. + So we finish scheduling.*/ + otg_dbg(OTG_DBG_SCHEDULE, "no ed\n"); + goto end_sched_perio_transfer; + } + + cur_frame_num = 0; + + otg_dbg(OTG_DBG_SCHEDULE, + "the %d ed to be scheduled\n", (int)scheduling_ed); + + sched_cnt--; + td_list_entry = scheduling_ed->td_list_entry.next; + + if (td_list_entry == &scheduling_ed->td_list_entry) { + /* scheduling_ed has no struct td. + so we schedules another ed on + PeriodicTransferReadyQ. */ + goto start_sched_perio_transfer; + } + + if (scheduling_ed->ed_status.is_in_transferring) { + /* scheduling_ed is already Scheduled. + so we schedules another ed on + PeriodicTransferReadyQ. */ + goto start_sched_perio_transfer; + } + + cur_frame_num = oci_get_frame_num(); + + if (((cur_frame_num - scheduling_ed->ed_desc.sched_frame) & + HFNUM_MAX_FRNUM) > (HFNUM_MAX_FRNUM >> 1)) { + + insert_ed_to_ready_q(scheduling_ed, false); + goto start_sched_perio_transfer; + } + + td = otg_list_get_node(td_list_entry, + struct td, td_list_entry); + + if ((td->is_transferring) || (td->is_transfer_done)) { + /* the selected struct td was already transferring + or completed to transfer. + we should decide how to control this case.*/ + goto end_sched_perio_transfer; + } + + otg_dbg(OTG_DBG_SCHEDULE, + "the struct td to be scheduled :%d", (int)td); + + alloc_ch = oci_start_transfer(otghost, &td->cur_stransfer); + + if (alloc_ch < total_chnum_threshold) { + + td->cur_stransfer.alloc_chnum = alloc_ch; + transferring_td_array[alloc_ch] = (u32)td; + + scheduling_ed->ed_status.is_in_transferring = true; + scheduling_ed->ed_status.is_in_transfer_ready_q = false; + scheduling_ed->ed_status.in_transferring_td = (u32)td; + + td->is_transferring = true; + + } else { + /* we should insert the ed_t to TransferReadyQ, + because the USB Transfer of current ed is failed.*/ + scheduling_ed->ed_status.is_in_transferring = false; + scheduling_ed->ed_status.is_in_transfer_ready_q = true; + scheduling_ed->ed_status.in_transferring_td = 0; + + insert_ed_to_ready_q(scheduling_ed, true); + + scheduling_ed->is_need_to_insert_scheduler = false; + goto end_sched_perio_transfer; + } + + } + +end_sched_perio_transfer: + + return; +} + + +/******************************************************************************/ +/*! + * @name int do_nonperiodic_schedule(struct sec_otghost *otghost) + * + * @brief this function start to schedule thie NonPeriodicTransferReadyQ. + * + * this function checks whether NonPeriodicTransferReadyQ has some ed. + * if there are some ed on NonPeriodicTransferReadyQ, + * this function request to start USB Trasnfer to S3C6400OCI. + * + * @param void + * @return void + */ +/******************************************************************************/ +static void do_nonperiodic_schedule(struct sec_otghost *otghost) +{ + struct ed *scheduling_ed; + int err_sched; + + otg_list_head *td_list_entry; + struct td *td; + u8 alloc_ch; + + if (total_used_chnum >= total_chnum_threshold) { + pr_info("shost nonperiodic: used_chnum >= total_chnum\n"); + return; + } + + while (1) { + +start_nonperiodic: + /* check there is available channel resource + for Non-Periodic Transfer. */ + if (total_used_chnum == total_chnum_threshold) { + pr_info("shost nonperiodic: used_chnum == total_chnum\n"); + goto end_nonperiodic; + } + + err_sched = get_ed_from_ready_q(&scheduling_ed, false); + + if (err_sched != USB_ERR_SUCCESS) { + /* pr_info("shost: non: nothing to schedule\n"); */ + goto end_nonperiodic; + } + + td_list_entry = scheduling_ed->td_list_entry.next; + + if (otg_list_empty(&scheduling_ed->td_list_entry)) { + /* scheduling_ed has no td. + so we schedules another ed + on PeriodicTransferReadyQ.*/ + goto start_nonperiodic; + } + + if (scheduling_ed->ed_status.is_in_transferring) { + /* scheduling_ed is already Scheduled. + so we schedules another ed + on PeriodicTransferReadyQ.*/ + goto start_nonperiodic; + } + + td = otg_list_get_node(td_list_entry, struct td, td_list_entry); + + if ((td->is_transferring) || (td->is_transfer_done)) { + pr_info("shost nonperiodic: td is busy\n"); + goto end_nonperiodic; + } + + alloc_ch = oci_start_transfer(otghost, &td->cur_stransfer); + + if (alloc_ch < total_chnum_threshold) { + + td->cur_stransfer.alloc_chnum = alloc_ch; + transferring_td_array[alloc_ch] = (u32)td; + + inc_non_perio_chnum(); + + scheduling_ed->ed_status.is_in_transferring = true; + scheduling_ed->ed_status.is_in_transfer_ready_q = false; + scheduling_ed->ed_status.in_transferring_td = (u32)td; + td->is_transferring = true; + + } else { + /* we must insert the ed to queue, + because the USB Transfer of the ed is failed.*/ + scheduling_ed->ed_status.is_in_transferring = false; + scheduling_ed->ed_status.in_transferring_td = 0; + insert_ed_to_ready_q(scheduling_ed, true); + scheduling_ed->ed_status.is_in_transfer_ready_q = true; + + goto end_nonperiodic; + } + } /* while */ + +end_nonperiodic: + return; +} + +/******************************************************************************/ +/*! + * @name int insert_ed_to_scheduler(struct sec_otghost *otghost, + * ed_t *insert_ed) + * + * @brief this function transfers the insert_ed to S3C6400Scheduler, + * and after that, the insert_ed is inserted to TransferReadyQ + * and scheduled by Scheduler. + * + * @param [IN] insert_ed + * = indicates pointer of ed_t to be inserted to TransferReadyQ. + * + * @return + * USB_ERR_ALREADY_EXIST - if the insert_ed is already existed. + * USB_ERR_SUCCESS - if success to insert insert_ed to S3CScheduler. + */ +/******************************************************************************/ +int insert_ed_to_scheduler(struct sec_otghost *otghost, struct ed *insert_ed) +{ + if (!insert_ed->is_need_to_insert_scheduler) + return USB_ERR_ALREADY_EXIST; + + otg_dbg(OTG_DBG_SCHEDULE_ED, "ed_id %d, td %d\n", + insert_ed->ed_id, insert_ed->num_td); + + insert_ed_to_ready_q(insert_ed, false); + insert_ed->is_need_to_insert_scheduler = false; + insert_ed->ed_status.is_in_transfer_ready_q = true; + + do_periodic_schedule(otghost); + do_nonperiodic_schedule(otghost); + + return USB_ERR_SUCCESS; +} + |