aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/shost/shost_oci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/shost/shost_oci.c')
-rw-r--r--drivers/usb/host/shost/shost_oci.c809
1 files changed, 809 insertions, 0 deletions
diff --git a/drivers/usb/host/shost/shost_oci.c b/drivers/usb/host/shost/shost_oci.c
new file mode 100644
index 0000000..4f47055
--- /dev/null
+++ b/drivers/usb/host/shost/shost_oci.c
@@ -0,0 +1,809 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : OCI.c
+ * [Description] : The file implement the external
+ * and internal functions of OCI
+ * [Author] : Jang Kyu Hyeok { kyuhyeok.jang@samsung.com }
+ * [Department] : System LSI Division/Embedded S/W Platform
+ * [Created Date]: 2009/02/10
+ * [Revision History]
+ * (1) 2008/06/12 by Jang Kyu Hyeok { kyuhyeok.jang@samsung.com }
+ * - Created this file and Implement functions of OCI
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * 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
+ ****************************************************************************/
+
+
+static bool ch_enable[16];
+
+/* OCI Internal Functions */
+
+static void oci_set_global_interrupt(bool set)
+{
+ gahbcfg_t ahbcfg;
+
+ otg_dbg(OTG_DBG_OCI, "oci_set_global_interrupt\n");
+
+ ahbcfg.d32 = 0;
+ ahbcfg.b.glblintrmsk = 1;
+
+ if (set)
+ update_reg_32(GAHBCFG, ahbcfg.d32);
+ else
+ clear_reg_32(GAHBCFG, ahbcfg.d32);
+}
+
+static int oci_channel_alloc(u8 *ch_num)
+{
+ hcchar_t hcchar = {.d32 = 0};
+ u8 ch;
+
+ for (ch = 0 ; ch < 16 ; ch++) {
+ if (ch_enable[ch] == true) {
+
+ hcchar.d32 = read_reg_32(HCCHAR(ch));
+
+ if (hcchar.b.chdis == 0) {
+ *ch_num = ch;
+ ch_enable[ch] = false;
+ return USB_ERR_SUCCESS;
+ }
+ }
+ }
+
+ return USB_ERR_FAIL;
+}
+
+
+
+static void oci_flush_tx_fifo(u32 num)
+{
+ grstctl_t greset = {.d32 = 0};
+ u32 count = 0;
+
+ otg_dbg(OTG_DBG_OCI, "oci_flush_tx_fifo\n");
+
+ greset.b.txfflsh = 1;
+ greset.b.txfnum = num;
+ write_reg_32(GRSTCTL, greset.d32);
+
+ /* wait for flush to end */
+ while (greset.b.txfflsh == 1) {
+ greset.d32 = read_reg_32(GRSTCTL);
+ if (++count > MAX_COUNT)
+ break;
+ };
+
+ /* Wait for 3 PHY Clocks*/
+ udelay(30);
+}
+
+static void oci_flush_rx_fifo(void)
+{
+ grstctl_t greset = {.d32 = 0};
+ u32 count = 0;
+
+ otg_dbg(OTG_DBG_OCI, "oci_flush_rx_fifo\n");
+
+ greset.b.rxfflsh = 1;
+ write_reg_32(GRSTCTL, greset.d32);
+
+ do {
+ greset.d32 = read_reg_32(GRSTCTL);
+ if (++count > MAX_COUNT)
+ break;
+ } while (greset.b.rxfflsh == 1);
+
+ /* Wait for 3 PHY Clocks*/
+ udelay(30);
+}
+
+static void oci_config_flush_fifo(u32 mode)
+{
+ ghwcfg2_t hwcfg2 = {.d32 = 0};
+
+ otg_dbg(OTG_DBG_OCI, "oci_config_flush_fifo\n");
+
+ hwcfg2.d32 = read_reg_32(GHWCFG2);
+
+ /* Configure data FIFO sizes */
+ if (hwcfg2.b.dynamic_fifo) {
+ /* Rx FIFO */
+ write_reg_32(GRXFSIZ, 0x0000010D);
+
+ /* Non-periodic Tx FIFO */
+ write_reg_32(GNPTXFSIZ, 0x0080010D);
+
+ if (mode == OTG_HOST_MODE) {
+ /* For Periodic transactions, */
+ /* program HPTXFSIZ */
+ }
+ }
+
+ /* Flush the FIFOs */
+ oci_flush_tx_fifo(0);
+
+ oci_flush_rx_fifo();
+}
+
+static int oci_core_reset(void)
+{
+ grstctl_t greset = {.d32 = 0};
+ u32 count = 0;
+
+ otg_dbg(OTG_DBG_OCI, "oci_core_reset\n");
+
+ /* Wait for AHB master IDLE state. */
+ do {
+ greset.d32 = read_reg_32(GRSTCTL);
+ mdelay(50);
+
+ if (++count > 100) {
+ otg_dbg(OTG_DBG_OCI, "AHB status is not IDLE\n");
+ return USB_ERR_FAIL;
+ }
+
+ } while (greset.b.ahbidle != 1);
+
+ /* Core Soft Reset */
+ greset.b.csftrst = 1;
+ write_reg_32(GRSTCTL, greset.d32);
+
+ /* Wait for 3 PHY Clocks*/
+ mdelay(50);
+ return USB_ERR_SUCCESS;
+}
+
+/**
+ * int oci_core_init(void)
+ *
+ * @brief process core initialize as s5pc110 otg spec
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+static int oci_core_init(void)
+{
+ gahbcfg_t ahbcfg = {.d32 = 0};
+ gusbcfg_t usbcfg = {.d32 = 0};
+ ghwcfg2_t hwcfg2 = {.d32 = 0};
+ gintmsk_t gintmsk = {.d32 = 0};
+
+ otg_dbg(OTG_DBG_OCI, "oci_core_init\n");
+
+ usbcfg.d32 = read_reg_32(GUSBCFG);
+ otg_dbg(OTG_DBG_OCI, "before - GUSBCFG=0x%x, GOTGCTL=0x%x\n",
+ usbcfg.d32, read_reg_32(GOTGCTL));
+
+ /* PHY parameters */
+ usbcfg.b.physel = 0;
+ usbcfg.b.phyif = 1; /* 16 bit */
+ usbcfg.b.ulpi_utmi_sel = 0; /* UTMI */
+ /* usbcfg.b.ddrsel = 1; */ /* DDR */
+ usbcfg.b.usbtrdtim = 5; /* 16 bit UTMI */
+ usbcfg.b.toutcal = 7;
+ usbcfg.b.forcehstmode = 1;
+ write_reg_32(GUSBCFG, usbcfg.d32);
+
+ otg_dbg(OTG_DBG_OCI,
+ "after - GUSBCFG=0x%x, GOTGCTL=0x%x\n",
+ read_reg_32(GUSBCFG),
+ read_reg_32(GOTGCTL));
+
+ /* Reset after setting the PHY parameters */
+ if (oci_core_reset() == USB_ERR_SUCCESS) {
+ /* Program the GAHBCFG Register.*/
+ hwcfg2.d32 = read_reg_32(GHWCFG2);
+
+ switch (hwcfg2.b.architecture) {
+ case HWCFG2_ARCH_SLAVE_ONLY:
+ otg_dbg(OTG_DBG_OCI, "Slave Only Mode\n");
+ ahbcfg.b.nptxfemplvl = 0;
+ ahbcfg.b.ptxfemplvl = 0;
+ break;
+
+ case HWCFG2_ARCH_EXT_DMA:
+ otg_dbg(OTG_DBG_OCI, "External DMA Mode - TBD!\n");
+ break;
+
+ case HWCFG2_ARCH_INT_DMA:
+ otg_dbg(OTG_DBG_OCI, "Internal DMA Setting\n");
+ ahbcfg.b.dmaenable = true;
+ ahbcfg.b.hburstlen = INT_DMA_MODE_INCR4;
+ break;
+
+ default:
+ otg_dbg(OTG_DBG_OCI, "ERR> hwcfg2\n ");
+ break;
+ }
+ write_reg_32(GAHBCFG, ahbcfg.d32);
+
+ /* Program the GUSBCFG register.*/
+ switch (hwcfg2.b.op_mode) {
+ case MODE_HNP_SRP_CAPABLE:
+ otg_dbg(OTG_DBG_OCI,
+ "GHWCFG2 OP Mode : MODE_HNP_SRP_CAPABLE\n");
+ usbcfg.b.hnpcap = 1;
+ usbcfg.b.srpcap = 1;
+
+ otg_dbg(OTG_DBG_OCI,
+ "OTG_DBG_OCI : use HNP and SRP\n");
+ break;
+
+ case MODE_SRP_ONLY_CAPABLE:
+ otg_dbg(OTG_DBG_OCI,
+ "GHWCFG2 OP Mode : MODE_SRP_ONLY_CAPABLE\n");
+ usbcfg.b.srpcap = 1;
+ break;
+
+ case MODE_NO_HNP_SRP_CAPABLE:
+ otg_dbg(OTG_DBG_OCI,
+ "GHWCFG2 OP Mode : MODE_NO_HNP_SRP_CAPABLE\n");
+ usbcfg.b.hnpcap = 0;
+ break;
+
+ case MODE_SRP_CAPABLE_DEVICE:
+ otg_dbg(OTG_DBG_OCI,
+ "GHWCFG2 OP Mode : MODE_SRP_CAPABLE_DEVICE\n");
+ usbcfg.b.srpcap = 1;
+ break;
+
+ case MODE_NO_SRP_CAPABLE_DEVICE:
+ otg_dbg(OTG_DBG_OCI,
+ "GHWCFG2 OP Mode : MODE_NO_SRP_CAPABLE_DEVICE\n");
+ usbcfg.b.srpcap = 0;
+ break;
+
+ case MODE_SRP_CAPABLE_HOST:
+ otg_dbg(OTG_DBG_OCI,
+ "GHWCFG2 OP Mode : MODE_SRP_CAPABLE_HOST\n");
+ usbcfg.b.srpcap = 1;
+ break;
+
+ case MODE_NO_SRP_CAPABLE_HOST:
+ otg_dbg(OTG_DBG_OCI,
+ "GHWCFG2 OP Mode : MODE_NO_SRP_CAPABLE_HOST\n");
+ usbcfg.b.srpcap = 0;
+ break;
+ default:
+ otg_err(OTG_DBG_OCI, "ERR> hwcfg2\n");
+ break;
+ }
+ write_reg_32(GUSBCFG, usbcfg.d32);
+
+ /* Program the GINTMSK register.*/
+ gintmsk.b.modemismatch = 1;
+ gintmsk.b.sofintr = 1;
+ /*gintmsk.b.otgintr = 1; */
+ gintmsk.b.conidstschng = 1;
+ /*gintmsk.b.wkupintr = 1;*/
+ gintmsk.b.disconnect = 1;
+ /*gintmsk.b.usbsuspend = 1;*/
+ /*gintmsk.b.sessreqintr = 1;*/
+ /*gintmsk.b.portintr = 1;*/
+ /*gintmsk.b.hcintr = 1; */
+ write_reg_32(GINTMSK, gintmsk.d32);
+
+ return USB_ERR_SUCCESS;
+
+ } else {
+ otg_err(OTG_DBG_OCI, "Core Reset FAIL\n");
+ return USB_ERR_FAIL;
+ }
+}
+
+/**
+ * int oci_host_init(void)
+ *
+ * @brief Process host initialize as s5pc110 spec
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+
+static int oci_host_init(void)
+{
+ gintmsk_t gintmsk = {.d32 = 0};
+ hcfg_t hcfg = {.d32 = 0};
+#if 0
+/*#ifdef CONFIG_USB_S3C_OTG_HOST_DTGDRVVBUS*/
+ hprt_t hprt;
+ hprt.d32 = read_reg_32(HPRT);
+#endif
+
+ otg_dbg(OTG_DBG_OCI, "oci_host_init\n");
+
+ gintmsk.b.portintr = 1;
+ update_reg_32(GINTMSK, gintmsk.d32);
+
+ hcfg.b.fslspclksel = HCFG_30_60_MHZ;
+ update_reg_32(HCFG, hcfg.d32);
+
+#if 0
+/*#ifdef CONFIG_USB_S3C_OTG_HOST_DTGDRVVBUS*/
+ /* turn on vbus */
+ if (!hprt.b.prtpwr) {
+ hprt.b.prtpwr = 1;
+ write_reg_32(HPRT, hprt.d32);
+
+ otg_dbg(true, "turn on Vbus\n");
+ }
+#endif
+
+ oci_config_flush_fifo(OTG_HOST_MODE);
+
+ return USB_ERR_SUCCESS;
+}
+
+/**
+ * int oci_channel_init( u8 ch_num, struct stransfer *transfer)
+ *
+ * @brief Process channel initialize to prepare starting transfer
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+static int oci_channel_init(u8 ch_num, struct stransfer *transfer)
+{
+ u32 intr_enable = 0;
+ gintmsk_t gintmsk = {.d32 = 0};
+ hcchar_t hcchar = {.d32 = 0};
+ hctsiz_t hctsiz = {.d32 = 0};
+
+ otg_dbg(OTG_DBG_OCI, "oci_channel_init\n");
+
+ /* Clear channel information */
+ write_reg_32(HCTSIZ(ch_num), 0);
+ write_reg_32(HCCHAR(ch_num), 0);
+ write_reg_32(HCINTMSK(ch_num), 0);
+ write_reg_32(HCINT(ch_num), INT_ALL);/*write clear*/
+ write_reg_32(HCDMA(ch_num), 0);
+
+ /* enable host channel interrupt in GINTSTS */
+ gintmsk.b.hcintr = 1;
+ update_reg_32(GINTMSK, gintmsk.d32);
+ /* Enable the top level host channel interrupt in HAINT */
+ intr_enable = (1 << ch_num);
+ update_reg_32(HAINTMSK, intr_enable);
+ /* unmask the down level host channel interrupt in HCINT */
+ write_reg_32(HCINTMSK(ch_num), transfer->hc_reg.hc_int_msk.d32);
+
+ /* Program the HCSIZn register with the endpoint characteristics for */
+ hctsiz.b.xfersize = transfer->buf_size;
+ hctsiz.b.pktcnt = transfer->packet_cnt;
+
+ /* Program the HCCHARn register with the endpoint characteristics for */
+ hcchar.b.mps = transfer->ed_desc_p->max_packet_size;
+ hcchar.b.epnum = transfer->ed_desc_p->endpoint_num;
+ hcchar.b.epdir = transfer->ed_desc_p->is_ep_in;
+ hcchar.b.lspddev = (transfer->ed_desc_p->dev_speed == LOW_SPEED_OTG);
+ hcchar.b.eptype = transfer->ed_desc_p->endpoint_type;
+ hcchar.b.multicnt = transfer->ed_desc_p->mc;
+ hcchar.b.devaddr = transfer->ed_desc_p->device_addr;
+
+ if (transfer->ed_desc_p->endpoint_type == INT_TRANSFER ||
+ transfer->ed_desc_p->endpoint_type == ISOCH_TRANSFER) {
+ u32 uiFrameNum = 0;
+ uiFrameNum = oci_get_frame_num();
+
+ hcchar.b.oddfrm = (uiFrameNum % 2) ? 1 : 0;
+
+ /*
+ * if transfer type is periodic transfer,
+ * must support sof interrupt
+ */
+
+ /*
+ gintmsk.b.sofintr = 1;
+ update_reg_32(GINTMSK, gintmsk.d32);
+ */
+ }
+
+ if (transfer->ed_desc_p->endpoint_type == CONTROL_TRANSFER) {
+
+ struct td *td_p;
+ td_p = (struct td *)transfer->parent_td;
+
+ switch (td_p->standard_dev_req_info.conrol_transfer_stage) {
+
+ case SETUP_STAGE:
+ hctsiz.b.pid = transfer->ed_status_p->
+ control_data_toggle.setup;
+ hcchar.b.epdir = EP_OUT;
+ break;
+
+ case DATA_STAGE:
+ hctsiz.b.pid =
+ transfer->ed_status_p->control_data_toggle.data;
+ hcchar.b.epdir = transfer->ed_desc_p->is_ep_in;
+ break;
+
+ case STATUS_STAGE:
+ hctsiz.b.pid = transfer->ed_status_p->
+ control_data_toggle.status;
+
+ if (td_p->standard_dev_req_info.is_data_stage) {
+ hcchar.b.epdir =
+ ~(transfer->ed_desc_p->is_ep_in);
+ } else
+ hcchar.b.epdir = EP_IN;
+ break;
+ default:
+ break;
+ }
+
+ } else
+ hctsiz.b.pid = transfer->ed_status_p->data_toggle;
+
+ otg_dbg(OTG_DBG_OCI, "eptype %d, stage %d, dir %d\n",
+ transfer->ed_desc_p->endpoint_type,
+ ((struct td *)transfer->parent_td)->
+ standard_dev_req_info.conrol_transfer_stage,
+ hcchar.b.epdir);
+
+ hctsiz.b.dopng = transfer->ed_status_p->is_ping_enable;
+ write_reg_32(HCTSIZ(ch_num), hctsiz.d32);
+ transfer->ed_status_p->is_ping_enable = false;
+
+ /* Write DMA Address */
+ write_reg_32(HCDMA(ch_num), transfer->phy_addr);
+
+ /* Wrote HCCHAR Register */
+ write_reg_32(HCCHAR(ch_num), hcchar.d32);
+
+ return USB_ERR_SUCCESS;
+}
+
+static int oci_dev_init(void)
+{
+ otg_dbg(OTG_DBG_OCI, "oci_dev_init - do nothing.\n");
+ /* return USB_ERR_FAIL; */
+ return USB_ERR_SUCCESS;
+}
+
+static int oci_init_mode(void)
+{
+ gintsts_t gintsts;
+ gintsts.d32 = read_reg_32(GINTSTS);
+
+ otg_dbg(OTG_DBG_OCI,
+ "GINSTS = 0x%x,GINMSK = 0x%x.\n",
+ (unsigned int)gintsts.d32,
+ (unsigned int)read_reg_32(GINTMSK));
+
+ if (gintsts.b.curmode == OTG_HOST_MODE) {
+ otg_dbg(OTG_DBG_OCI, "HOST Mode\n");
+
+ if (oci_host_init() == USB_ERR_SUCCESS) {
+ return USB_ERR_SUCCESS;
+
+ } else {
+ otg_dbg(OTG_DBG_OCI, "oci_host_init() Fail\n");
+ return USB_ERR_FAIL;
+ }
+
+ } else { /* Device Mode */
+ otg_dbg(OTG_DBG_OCI, "DEVICE Mode\n");
+
+ if (oci_dev_init() == USB_ERR_SUCCESS) {
+ return USB_ERR_SUCCESS;
+
+ } else {
+ otg_err(OTG_DBG_OCI, "oci_dev_init() Fail\n");
+ return USB_ERR_FAIL;
+ }
+ }
+
+ return USB_ERR_SUCCESS;
+}
+
+
+
+/**
+ * int oci_start(void)
+ *
+ * @brief start to operate oci module by calling oci_core_init function
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+static int oci_start(void)
+{
+ otg_dbg(OTG_DBG_OCI, "oci_start\n");
+
+ if (oci_core_init() == USB_ERR_SUCCESS) {
+ mdelay(50);
+
+ if (oci_init_mode() == USB_ERR_SUCCESS) {
+ oci_set_global_interrupt(true);
+ return USB_ERR_SUCCESS;
+
+ } else {
+ otg_dbg(OTG_DBG_OCI, "oci_init_mode() Fail\n");
+ return USB_ERR_FAIL;
+ }
+
+ } else {
+ otg_dbg(OTG_DBG_OCI, "oci_core_init() Fail\n");
+ return USB_ERR_FAIL;
+ }
+}
+
+/**
+ * int oci_stop(void)
+ *
+ * @brief stop to opearte otg core
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+static int oci_stop(void)
+{
+ gintmsk_t gintmsk = {.d32 = 0};
+
+ otg_dbg(OTG_DBG_OCI, "oci_stop\n");
+
+ /* sometimes, port interrupt occured after removed
+ * otg host driver. so, we have to mask port interrupt. */
+ write_reg_32(GINTMSK, gintmsk.d32);
+
+ oci_set_global_interrupt(false);
+
+ return USB_ERR_SUCCESS;
+}
+
+
+static int oci_sys_init(struct sec_otghost *otghost)
+{
+ struct sec_otghost_data *pdata = otghost->otg_data;
+
+ if (pdata && pdata->phy_init && pdata->pdev) {
+ pr_info("otg phy_init\n");
+ clk_enable(pdata->clk);
+ pdata->phy_init(0);
+
+ writel(0, OTG_PHYPWR);
+ writel(0x87, OTG_PHYCLK);
+ } else
+ pr_info("otg phy_init failed\n");
+
+ return USB_ERR_SUCCESS;
+}
+
+/**
+ * int oci_init(struct sec_otghost *otghost)
+ *
+ * @brief Initialize oci module.
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+static int oci_init(struct sec_otghost *otghost)
+{
+ otg_mem_set((void *)ch_enable, true, sizeof(bool)*16);
+ otghost->ch_halt = false;
+
+ if (oci_sys_init(otghost) == USB_ERR_SUCCESS) {
+ if (oci_core_reset() == USB_ERR_SUCCESS) {
+
+ oci_set_global_interrupt(false);
+ return USB_ERR_SUCCESS;
+
+ } else {
+ otg_dbg(OTG_DBG_OCI, "oci_core_reset() Fail\n");
+ return USB_ERR_FAIL;
+ }
+ }
+
+ return USB_ERR_FAIL;
+}
+
+/***********************************************************************
+ * API
+ */
+
+/* also called by ischeduler.c */
+int oci_channel_dealloc(u8 ch_num)
+{
+ if (ch_num < 16 && ch_enable[ch_num] == false) {
+ ch_enable[ch_num] = true;
+
+ write_reg_32(HCTSIZ(ch_num), 0);
+ write_reg_32(HCCHAR(ch_num), 0);
+ write_reg_32(HCINTMSK(ch_num), 0);
+ write_reg_32(HCINT(ch_num), INT_ALL);
+ write_reg_32(HCDMA(ch_num), 0);
+ return USB_ERR_SUCCESS;
+ }
+
+ return USB_ERR_FAIL;
+}
+
+/**
+ * oci_start_transfer(struct sec_otghost *otghost, struct stransfer *transfer)
+ *
+ * @brief start transfer by using transfer information to receive from scheduler
+ *
+ * @param [IN] *transfer - information about transfer to write register by calling oci_channel_init function
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+
+u8 oci_start_transfer(struct sec_otghost *otghost, struct stransfer *transfer)
+{
+ hcchar_t hcchar = {.d32 = 0};
+
+ otg_dbg(OTG_DBG_OCI, "oci_start_transfer\n");
+
+ if (transfer->alloc_chnum == CH_NONE) {
+
+ if (oci_channel_alloc(&(transfer->alloc_chnum))
+ != USB_ERR_SUCCESS) {
+
+ otg_dbg(OTG_DBG_OCI,
+ "Fail - Channel Allocation Error\n");
+ return CH_NONE;
+ }
+ }
+
+ oci_channel_init(transfer->alloc_chnum, transfer);
+
+ hcchar.b.chen = 1;
+ update_reg_32(HCCHAR(transfer->alloc_chnum), hcchar.d32);
+
+ return transfer->alloc_chnum;
+}
+
+/**
+ * int oci_stop_transfer(struct sec_otghost *otghost, u8 ch_num)
+ *
+ * @brief stop to transfer even if transfering
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+int oci_stop_transfer(struct sec_otghost *otghost, u8 ch_num)
+{
+ hcchar_t hcchar = {.d32 = 0};
+ hcintmsk_t hcintmsk = {.d32 = 0};
+ int count = 0, max_error_count = 10000;
+
+ otg_dbg(OTG_DBG_OCI,
+ "step1: oci_stop_transfer ch=%d, hcchar=0x%x\n",
+ ch_num, read_reg_32(HCCHAR(ch_num)));
+
+ if (ch_num > 16)
+ return USB_ERR_FAIL;
+
+ otghost->ch_halt = true;
+
+ hcintmsk.b.chhltd = 1;
+ update_reg_32(HCINTMSK(ch_num), hcintmsk.d32);
+
+ hcchar.b.chdis = 1;
+ hcchar.b.chen = 1;
+ update_reg_32(HCCHAR(ch_num), hcchar.d32);
+
+ /* wait for Channel Disabled Interrupt */
+ do {
+ hcchar.d32 = read_reg_32(HCCHAR(ch_num));
+
+ if (count > max_error_count) {
+ otg_dbg(OTG_DBG_OCI,
+ "Warning!! oci_stop_transfer()"
+ "ChDis is not cleared! ch=%d, hcchar=0x%x\n",
+ ch_num, hcchar.d32);
+ break;
+ }
+ count++;
+
+ } while (hcchar.b.chdis);
+
+ oci_channel_dealloc(ch_num);
+
+ clear_reg_32(HAINTMSK, ch_num);
+ write_reg_32(HCINT(ch_num), INT_ALL);
+ clear_reg_32(HCINTMSK(ch_num), INT_ALL);
+
+ otghost->ch_halt = false;
+ otg_dbg(OTG_DBG_OCI,
+ "step2 : oci_stop_transfer ch=%d, hcchar=0x%x\n",
+ ch_num, read_reg_32(HCCHAR(ch_num)));
+
+ return USB_ERR_SUCCESS;
+}
+
+/**
+ * u32 oci_get_frame_num(void)
+ *
+ * @brief Get current frame number by reading register.
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+u32 oci_get_frame_num(void)
+{
+ hfnum_t hfnum;
+ hfnum.d32 = read_reg_32(HFNUM);
+
+ otg_dbg(OTG_DBG_OCI, "oci_get_frame_num=%d\n", hfnum.b.frnum);
+
+ return hfnum.b.frnum;
+}
+
+/**
+ * u16 oci_get_frame_interval(void)
+ *
+ * @brief Get current frame interval by reading register.
+ *
+ * @param None
+ *
+ * @return USB_ERR_SUCCESS : If success \n
+ * USB_ERR_FAIL : If call fail \n
+ * @remark
+ *
+ */
+u16 oci_get_frame_interval(void)
+{
+ hfir_t hfir;
+ hfir.d32 = read_reg_32(HFIR);
+ return hfir.b.frint;
+}
+
+void oci_set_frame_interval(u16 interval)
+{
+ hfir_t hfir = {.d32 = 0};
+ hfir.b.frint = interval;
+ write_reg_32(HFIR, hfir.d32);
+}
+