/*-----------------------------------------------------------------------------
 * drivers/usb/mcd/usb_mcd_otg.c
 * 
 * USB controller driver
 * 
 * Copyright 2005,2006,2008 Sony Corporation
 *
 * This file is part of the Inventra Controller Driver for Linux.
 * 
 * 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., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 *---------------------------------------------------------------------------*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/interrupt.h>

#include <linux/usb.h>
#include <linux/usb_gadget.h>
#include <linux/version.h>
#include <linux/usb/mcd/usb_otg_control.h>
#include "../core/hub.h"
#include "../core/hcd.h"

#include "usb_mcd_cnf.h"
#include "usb_mcd_debug.h"
#include "usb_mcd_dma.h"
#include "usb_mcd_otg.h"
#include "usb_mcd_gadget.h"
#include "usb_mcd_host.h"
#include "usb_mcd_device.h"

/******************************* INTERNALS *******************************/

static struct usb_otg_control *registered_otgc;

#define MCD_OTGC_T_A_AIDL_BDIS 250
#define MCD_OTGC_T_B_ASE0_BRST 4

/******************************* FORWARDS ********************************/

/******************************* GLOBALS *********************************/

/****************************** FUNCTIONS ********************************/

/*************************** INTERNAL FUNCTIONS **************************/

static void expire_a_aidl_bdis_timer(unsigned long ptr)
{
	struct usb_otg_control *otg_control;
	struct mcd_otg_intdat *otgc_intdat;
	union usb_otg_event otg_event;

	otg_control = (struct usb_otg_control *)ptr;
	otgc_intdat = otgc_to_otgi(otg_control);
	if (otgc_intdat->timer.data) {
		otgc_intdat->timer.data = (unsigned long)NULL;
	}

	otg_event.type = USB_OTG_EVENT_TYPE_RCGADGET;
	otg_event.rcgadget.status = USB_OTG_FAIL_T_A_AIDL_BDIS;
	mcd_otg_control_signal(otg_control, &otg_event);
}

static void expire_b_ase0_brst_timer(unsigned long ptr)
{
	struct usb_otg_control *otg_control;
	struct mcd_otg_intdat *otgc_intdat;
	union usb_otg_event otg_event;

	otg_control = (struct usb_otg_control *)ptr;
	otgc_intdat = otgc_to_otgi(otg_control);
	if (otgc_intdat->timer.data) {
		otgc_intdat->timer.data = (unsigned long)NULL;
	}

	otg_event.type = USB_OTG_EVENT_TYPE_RCHOST;
	otg_event.rchost.status = USB_OTG_FAIL_T_B_ASE0_BRST;
	mcd_otg_control_signal(otg_control, &otg_event);
}

static void
restart_timer(struct usb_otg_control *otg_control,
	      void (*function) (unsigned long), unsigned long timeout)
{
	struct mcd_otg_intdat *otgc_intdat;

	otgc_intdat = otgc_to_otgi(otg_control);
	if (otgc_intdat->timer.data) {
		/* Error - 多重呼び出しされている */
		return;
	}
	otgc_intdat->timer.function = function;
	otgc_intdat->timer.data = (unsigned long)otg_control;
	mod_timer(&otgc_intdat->timer, jiffies + timeout * HZ / 1000);
}

inline void restart_a_aidl_bdis_timer(struct usb_otg_control *otg_control)
{
	restart_timer(otg_control, expire_a_aidl_bdis_timer,
		      MCD_OTGC_T_A_AIDL_BDIS);
}

inline void restart_b_ase0_brst_timer(struct usb_otg_control *otg_control)
{
	restart_timer(otg_control, expire_b_ase0_brst_timer,
		      MCD_OTGC_T_B_ASE0_BRST);
}

static void stop_timer(struct usb_otg_control *otg_control)
{
	struct mcd_otg_intdat *otgc_intdat;

	otgc_intdat = otgc_to_otgi(otg_control);
	if (otgc_intdat->timer.data) {
		otgc_intdat->timer.data = (unsigned long)NULL;
		del_timer(&otgc_intdat->timer);
	}
}

bool notify_cid(struct usb_otg_control *otg_control, u32 pn)
{
	int current_cid;
	union usb_otg_event otg_event;
	struct mcd_otg_intdat *otgc_intdat;
	struct mcd_otg_port *otgc_port;
	bool checking;

	checking = true;
	otgc_intdat = otgc_to_otgi(otg_control);
	otgc_port = &otgc_intdat->port[pn];

	if (otgc_port->check_cid) {
		current_cid = otgc_port->check_cid(otg_control, pn);
		if (current_cid) {
			/* A-Plug切断時即時通知 */
			if (current_cid != otgc_port->last_cid) {
				otgc_port->last_cid = current_cid;
				/* CID OTGイベントシグナリング */
				otg_event.type = USB_OTG_EVENT_TYPE_CID;
				otg_event.cid.value = current_cid;
				otg_event.cid.port = pn;
				mcd_otg_control_signal(otg_control, &otg_event);
			}
			otgc_port->cid_check_count = 0;
			checking = false;
		} else if (!current_cid
			   && current_cid == otgc_port->ref_cid
			   && current_cid != otgc_port->last_cid) {
			/* A-Plug接続時フィルター */
			otgc_port->cid_check_count++;
			if (otgc_port->cid_check_count >=
			    otgc_port->cid_filter_count) {
				otgc_port->last_cid = current_cid;
				otgc_port->cid_check_count = 0;
				/* CID OTGイベントシグナリング */
				otg_event.type = USB_OTG_EVENT_TYPE_CID;
				otg_event.cid.value = current_cid;
				otg_event.cid.port = pn;
				mcd_otg_control_signal(otg_control, &otg_event);
				checking = false;
			}
		} else {
			otgc_port->cid_check_count = 0;
			checking = false;
		}
		otgc_port->ref_cid = current_cid;
	}
	else {
		checking = false;
	}

	return checking;
}

bool notify_vbus(struct usb_otg_control *otg_control, unsigned int pn)
{
	struct mcd_otg_intdat *otgc_intdat;
	struct mcd_otg_port *otgc_port;
	union usb_otg_event otg_event;
	enum usb_otg_vbus_stat current_vbus;
	enum usb_otg_vbus_stat notify_vbus;
	bool checking;

	checking = true;
	otgc_intdat = otgc_to_otgi(otg_control);
	otgc_port = &otgc_intdat->port[pn];

	if (otgc_port->check_vbus) {
		current_vbus = otgc_port->check_vbus(otg_control, pn);
	} else {
		current_vbus = mcd_device_check_vbus(otgi_to_mdev(otgc_intdat));
	}
	if (otgc_port->last_vbus > current_vbus) {
		/* 切断時即時 */
		/* Vbus OTGイベントシグナリング */
		otg_event.type = USB_OTG_EVENT_TYPE_VBUS;
		notify_vbus = otgc_port->last_vbus;
		do {
			notify_vbus--;
			otg_event.vbus.vbus_stat = notify_vbus;
			otg_event.vbus.port = pn;
			mcd_otg_control_signal(otg_control, &otg_event);
		} while (notify_vbus > current_vbus);
		/* mcd_otg_control_signal内にlast_vbus参照あり */
		otgc_port->last_vbus = notify_vbus;
		otgc_port->vbus_check_count = 0;
		checking = false;
	} else if (otgc_port->last_vbus < current_vbus) {
		if (current_vbus == otgc_port->ref_vbus) {
			/* 接続時フィルター */
			otgc_port->vbus_check_count++;
			if (otgc_port->vbus_check_count
			    >= otgc_port->vbus_filter_count) {
				/* Vbus OTGイベントシグナリング */
				otg_event.type = USB_OTG_EVENT_TYPE_VBUS;
				notify_vbus = otgc_port->last_vbus;
				do {
					notify_vbus++;
					otg_event.vbus.vbus_stat = notify_vbus;
					otg_event.vbus.port = pn;
					mcd_otg_control_signal(otg_control,
							       &otg_event);
				} while (notify_vbus < current_vbus);
				/* mcd_otg_control_signal内にlast_vbus参照あり*/
				otgc_port->last_vbus = notify_vbus;
				otgc_port->vbus_check_count = 0;
				checking = false;
			}
		} else {
			otgc_port->vbus_check_count = 0;
		}
	} else {
		/* Vbusに変化無い場合はカウンタリセット */
		otgc_port->vbus_check_count = 0;
		checking = false;
	}
	otgc_port->ref_vbus = current_vbus;

	return checking;
}

/*
 * 関数：detector
 * CID/Vbus監視周期関数(タイマハンドラ)
 */

static void detector(unsigned long ptr)
{
	u32 i;
	unsigned long flags;
	bool checking_vbuscid;
	struct usb_otg_control *otg_control;
	struct mcd_otg_intdat *otgc_intdat;

	otg_control = (struct usb_otg_control *)ptr;
	otgc_intdat = otgc_to_otgi(otg_control);

	checking_vbuscid = false;

	for (i = 0; i < otgc_intdat->port_num; i++) {
		checking_vbuscid |= notify_cid(otg_control, i);
		checking_vbuscid |= notify_vbus(otg_control, i);
	}

	if (otgc_intdat->attributes & MCD_OTG_ATTR_ALL_INT_PORT &&
	    !checking_vbuscid) {
		return;
	}

	/* 排他制御開始 */
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	mod_timer(&otgc_intdat->detector,
                  jiffies + otgc_intdat->periodic_delay * HZ / 1000);

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);
}

/********************************* APIs **********************************/

/*
 * 関数：get_port_info
 * USBポート情報を取得する。
 */

static int
get_port_info(struct usb_otg_control *otg_control,
	      struct usb_otg_control_port_info *info)
{
	int retval;
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	/* Port情報取得 */
	if (otgc_intdat->ops->get_port_info) {

		retval = otgc_intdat->ops->get_port_info(otg_control, info);
	} else {
		retval = -ENODEV;
	}

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return retval;
}

/*
 * 関数：select_port
 * USBポートを選択する。
 */

static int select_port(struct usb_otg_control *otg_control, u32 pn)
{
	int retval;
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->ops->select_port) {

		retval = otgc_intdat->ops->select_port(otg_control, pn);
	} else {
		retval = -ENODEV;
	}

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return retval;
}

/*
 * 関数：mcd_otg_start_control
 * Controllerを開始する。
 */

static int start_control(struct usb_otg_control *otg_control)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->state != MCD_OTGC_S_STOP) {
		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);

		return -EBUSY;
	}

	otgc_intdat->state = MCD_OTGC_S_AB_IDLE;

	/* be sure interrupts are disabled before connecting ISR */
	mcd_device_start(otgi_to_mdev(otgc_intdat));

	if (otgc_intdat->ops->control_io) {
		int retval;

		retval =
		    otgc_intdat->ops->control_io(otg_control,
						 USB_OTG_IO_START_CONTROL);
		if (retval) {
			/* 排他制御終了 */
			spin_unlock_irqrestore(&otgc_intdat->lock, flags);

			return retval;
		}
	}

	/* 周期関数開始 */
	mod_timer(&otgc_intdat->detector, jiffies);

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return 0;
}

/*
 * 関数：stop_control
 * Controllerを停止する。
 */

static int stop_control(struct usb_otg_control *otg_control)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->state != MCD_OTGC_S_AB_IDLE) {
		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);

		return -EBUSY;
	}
	mcd_device_stop(otgi_to_mdev(otgc_intdat));

	/* 周期関数停止 */
	del_timer(&otgc_intdat->detector);

	if (otgc_intdat->ops->control_io) {
		int retval;

		retval =
		    otgc_intdat->ops->control_io(otg_control,
						 USB_OTG_IO_STOP_CONTROL);
		if (retval) {
			/* 排他制御終了 */
			spin_unlock_irqrestore(&otgc_intdat->lock, flags);

			return retval;
		}
	}

	otgc_intdat->state = MCD_OTGC_S_STOP;

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return 0;
}

/*
 * 関数：start_gadget
 * ControllerをGADGETモードにする。
 */

static int start_gadget(struct usb_otg_control *otg_control)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->state != MCD_OTGC_S_AB_IDLE) {
		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);

		return -EBUSY;
	}

	otgc_intdat->state = MCD_OTGC_S_B_PERIPH;

	if (otgc_intdat->ops->control_io) {
		int retval;

		retval =
		    otgc_intdat->ops->control_io(otg_control,
						 USB_OTG_IO_START_GADGET);
		if (retval) {
			/* 排他制御終了 */
			spin_unlock_irqrestore(&otgc_intdat->lock, flags);

			return retval;
		}
	}

	mcd_device_start_gadget(otgi_to_mdev(otgc_intdat));

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return 0;
}

/*
 * 関数：stop_gadget
 * ControllerをIDLEモードにする。
 */

static int stop_gadget(struct usb_otg_control *otg_control)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->state != MCD_OTGC_S_B_PERIPH &&
	    otgc_intdat->state != MCD_OTGC_S_B_WAIT_ACON) {
		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);

		return -EBUSY;
	}

	/* タイムアウトT_B_ASE0_BRST停止 */
	stop_timer(otg_control);

	mcd_device_stop_gadget(otgi_to_mdev(otgc_intdat));

	otgc_intdat->state = MCD_OTGC_S_AB_IDLE;

	if (otgc_intdat->ops->control_io) {
		otgc_intdat->ops->control_io(otg_control,
					     USB_OTG_IO_STOP_GADGET);
	}

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return 0;
}

/*
 * 関数：enable_rchost
 * ControllerにRCHOSTモードになることを許可する。
 */

static int enable_rchost(struct usb_otg_control *otg_control)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->state != MCD_OTGC_S_B_PERIPH) {
		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);

		return -EBUSY;
	}

	otgc_intdat->state = MCD_OTGC_S_B_WAIT_ACON;

	if (otgc_intdat->ops->control_io) {
		int retval;

		retval =
		    otgc_intdat->ops->control_io(otg_control,
						 USB_OTG_IO_ENABLE_RCHOST);
		if (retval) {
			/* 排他制御終了 */
			spin_unlock_irqrestore(&otgc_intdat->lock, flags);

			return retval;
		}
	}

#if !defined(_MCD2_LF1_)
	mcd_otga_enable_role_change(otgi_regs(otgc_intdat));
#endif

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return 0;
}

/*
 * 関数：disable_rchost
 * ControllerにRCHOSTモードになることを不許可とする。
 */

static int disable_rchost(struct usb_otg_control *otg_control)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->state != MCD_OTGC_S_B_PERIPH) {
		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);

		return -EBUSY;
	}

	/* タイムアウトT_B_ASE0_BRST設定 */
	restart_b_ase0_brst_timer(otg_control);

#if !defined(_MCD2_LF1_)
	mcd_otga_disable_role_change(otgi_regs(otgc_intdat));
#endif

	if (otgc_intdat->ops->control_io) {
		int retval;

		retval =
		    otgc_intdat->ops->control_io(otg_control,
						 USB_OTG_IO_DISABLE_RCHOST);
		if (retval) {
			/* 排他制御終了 */
			spin_unlock_irqrestore(&otgc_intdat->lock, flags);

			return retval;
		}
	}

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return 0;
}

/*
 * 関数：stop_rchost
 * ControllerをIDLEモードにする。
 */

static int stop_rchost(struct usb_otg_control *otg_control)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->state != MCD_OTGC_S_B_HOST &&
	    otgc_intdat->state != MCD_OTGC_S_B_WAIT_ACON) {
		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);

		return -EBUSY;
	}

	/* タイムアウトT_B_ASE0_BRST停止 */
	stop_timer(otg_control);

	otgc_intdat->state = MCD_OTGC_S_AB_IDLE;

#if !defined(_MCD2_LF1_)
	mcd_otga_suspend(otgi_regs(otgc_intdat));
#endif

	if (otgc_intdat->ops->control_io) {
		otgc_intdat->ops->control_io(otg_control,
					     USB_OTG_IO_STOP_RCHOST);
	}

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return 0;
}

/*
 * 関数：start_host
 * ControllerをHOSTモードにする。
 */

static int start_host(struct usb_otg_control *otg_control)
{
	int status;
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->state != MCD_OTGC_S_AB_IDLE) {
		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);

		return -EBUSY;
	}

	otgc_intdat->state = MCD_OTGC_S_A_HOST;

	if (otgc_intdat->ops->control_io) {
		int retval;

		retval =
		    otgc_intdat->ops->control_io(otg_control,
						 USB_OTG_IO_START_HOST);
		if (retval) {
			/* 排他制御終了 */
			spin_unlock_irqrestore(&otgc_intdat->lock, flags);

			return retval;
		}
	}

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	status = mcd_device_start_host(otgi_to_mdev(otgc_intdat));

	if (status) {
		/* 失敗時元に戻す */

		/* 排他制御開始 */
		spin_lock_irqsave(&otgc_intdat->lock, flags);

		otgc_intdat->state = MCD_OTGC_S_AB_IDLE;

		if (otgc_intdat->ops->control_io) {
			otgc_intdat->ops->control_io(otg_control,
						     USB_OTG_IO_STOP_HOST);
		}

		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);
	}

	return status;
}

/*
 * 関数：stop_host
 * ControllerをIDLEモードにする。
 */

static int stop_host(struct usb_otg_control *otg_control)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->state != MCD_OTGC_S_A_HOST) {
		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);

		return -EBUSY;
	}

	otgc_intdat->state = MCD_OTGC_S_AB_IDLE;

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	mcd_device_stop_host(otgi_to_mdev(otgc_intdat));

	/* 排他制御開始 */
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->ops->control_vbus &&
	    !(otgc_intdat->attributes & MCD_OTG_ATTR_USING_PHY_VBUS)) {
		otgc_intdat->ops->control_vbus(otg_control, USB_OTG_OFF);
	}

	if (otgc_intdat->ops->control_io) {
		otgc_intdat->ops->control_io(otg_control, USB_OTG_IO_STOP_HOST);
	}

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return 0;
}

/*
 * 関数：start_rcgadget
 * ControllerをRCGADGETモードにする。
 */

static int
start_rcgadget(struct usb_otg_control *otg_control, unsigned int port)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->state != MCD_OTGC_S_A_HOST) {
		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);

		return -EBUSY;
	}

	/* タイムアウトT_A_AIDL_BDIS設定 */
	restart_a_aidl_bdis_timer(otg_control);

	otgc_intdat->state = MCD_OTGC_S_A_PERIPH;

	if (otgc_intdat->ops->control_io) {
		int retval;

		retval =
		    otgc_intdat->ops->control_io(otg_control,
						 USB_OTG_IO_START_RCGADGET);
		if (retval) {
			/* 排他制御終了 */
			spin_unlock_irqrestore(&otgc_intdat->lock, flags);

			return retval;
		}
	}

#if !defined(_MCD2_LF1_)
	mcd_otga_suspend(otgi_regs(otgc_intdat));
#endif

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return 0;
}

/*
 * 関数：stop_rcgadget
 * ControllerをIDLEモードにする。
 */

static int stop_rcgadget(struct usb_otg_control *otg_control, unsigned int port)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->state != MCD_OTGC_S_A_PERIPH) {
		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);

		return -EBUSY;
	}

	/* タイムアウトT_A_AIDL_BDIS停止 */
	stop_timer(otg_control);

	if (otgc_intdat->ops->control_vbus &&
	    !(otgc_intdat->attributes & MCD_OTG_ATTR_USING_PHY_VBUS)) {
		otgc_intdat->ops->control_vbus(otg_control, USB_OTG_OFF);
	}

	otgc_intdat->state = MCD_OTGC_S_AB_IDLE;

#if !defined(_MCD2_LF1_)
	mcd_otga_end_session(otgi_regs(otgc_intdat));
#endif

	if (otgc_intdat->ops->control_io) {
		otgc_intdat->ops->control_io(otg_control,
					     USB_OTG_IO_STOP_RCGADGET);
	}

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return 0;
}

/*
 * 関数：get_mode
 * OTGモードを取得する。
 */

static int get_mode(struct usb_otg_control *otg_control)
{
	int state;
	struct mcd_otg_intdat *otgc_intdat;

	otgc_intdat = otgc_to_otgi(otg_control);
	switch (otgc_intdat->state) {
	case MCD_OTGC_S_STOP:
		state = USB_OTG_CONTROL_STOP;
		break;
	case MCD_OTGC_S_AB_IDLE:
		state = USB_OTG_CONTROL_IDLE;
		break;
	case MCD_OTGC_S_B_PERIPH:
		state = USB_OTG_CONTROL_GADGET;
		break;
	case MCD_OTGC_S_B_WAIT_ACON:
		state = USB_OTG_CONTROL_GADGET;
		break;
	case MCD_OTGC_S_B_HOST:
		state = USB_OTG_CONTROL_RCHOST;
		break;
	case MCD_OTGC_S_A_HOST:
		state = USB_OTG_CONTROL_HOST;
		break;
	case MCD_OTGC_S_A_PERIPH:
		state = USB_OTG_CONTROL_RCGADGET;
		break;
	default:
		/* error */
		state = 0;
		break;
	}

	return state;
}

/*
 * 関数：request_session
 * ControllerにSRPパルシングを要求する。
 */

static int request_session(struct usb_otg_control *otg_control)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->state != MCD_OTGC_S_AB_IDLE) {
		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);

		return -EBUSY;
	}

	if (otgc_intdat->ops->control_io) {
		int retval;

		retval = otgc_intdat->ops->control_io(otg_control,
						      USB_OTG_IO_INIT_SRP);
		if (retval) {
			/* 排他制御終了 */
			spin_unlock_irqrestore(&otgc_intdat->lock, flags);

			return retval;
		}
	}

	mcd_device_request_session(otgi_to_mdev(otgc_intdat));

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return 0;
}

inline static void finish_srp(struct usb_otg_control *otg_control)
{
	struct mcd_otg_intdat *otgc_intdat;

	otgc_intdat = otgc_to_otgi(otg_control);

	if (otgc_intdat->ops->control_io) {
		otgc_intdat->ops->control_io(otg_control, USB_OTG_IO_FINISH_SRP);
	}

	return;
}

inline static bool check_vpulse_start(struct usb_otg_control *otg_control,
				      u8 old_vbus, u8 new_vbus)
{
	struct mcd_otg_intdat *otgc_intdat;

	otgc_intdat = otgc_to_otgi(otg_control);

	if (otgc_intdat->state == MCD_OTGC_S_AB_IDLE &&
	    old_vbus < new_vbus &&
	    new_vbus == USB_OTG_VBUS_STAT_SESS)
	{
		return true;
	}

	return false;
}

inline static bool check_vpulse_end(struct usb_otg_control *otg_control,
				    u8 old_vbus, u8 new_vbus)
{
	struct mcd_otg_intdat *otgc_intdat;

	otgc_intdat = otgc_to_otgi(otg_control);

	if (otgc_intdat->state == MCD_OTGC_S_AB_IDLE &&
	    old_vbus > new_vbus &&
	    new_vbus == USB_OTG_VBUS_STAT_LOW)
	{
		return true;
	}

	return false;
}

/*
 * 関数：set_speed
 * 通信スピードを設定する。
 */

static int set_speed(struct usb_otg_control *otg_control, unsigned int speed)
{
	struct mcd_otg_intdat *otgc_intdat;

	if (speed != USB_OTG_HS_ENABLE && speed != USB_OTG_HS_DISABLE) {
		return -EINVAL;
	}

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock(&otgc_intdat->lock);

	mcd_device_set_speed(otgi_to_mdev(otgc_intdat), speed);

	/* 排他制御終了 */
	spin_unlock(&otgc_intdat->lock);

	return 0;
}

/*
 * 関数：get_speed
 * 通信スピードを取得する。
 */

static enum usb_otg_control_speed get_speed(struct usb_otg_control *otg_control)
{
	enum usb_otg_control_speed speed;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock(&otgc_intdat->lock);

	speed = mcd_device_get_speed(otgi_to_mdev(otgc_intdat));

	/* 排他制御終了 */
	spin_unlock(&otgc_intdat->lock);

	return speed;
}

/*
 * 関数：set_test_mode
 * テストモードを指定する。
 */

static int
set_test_mode(struct usb_otg_control *otg_control,
	      enum usb_otg_control_test_mode test_mode)
{
	int retval;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock(&otgc_intdat->lock);

	if (otgc_intdat->ops->control_test) {
		otgc_intdat->ops->control_test(otg_control, test_mode);
	}
	retval = mcd_device_set_test_mode(otgi_to_mdev(otgc_intdat), test_mode);

	/* 排他制御終了 */
	spin_unlock(&otgc_intdat->lock);

	return retval;
}

/*
 * 関数：get_test_mode
 * テストモードを取得する。
 */

static enum usb_otg_control_test_mode
get_test_mode(struct usb_otg_control *otg_control)
{
	enum usb_otg_control_test_mode test_mode;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock(&otgc_intdat->lock);

	test_mode = mcd_device_get_test_mode(otgi_to_mdev(otgc_intdat));

	/* 排他制御終了 */
	spin_unlock(&otgc_intdat->lock);

	return test_mode;
}

static struct usb_otg_control_ops otg_control_ops = {
	.get_port_info = get_port_info,
	.select_port = select_port,
	.start_control = start_control,
	.stop_control = stop_control,
	.start_gadget = start_gadget,
	.stop_gadget = stop_gadget,
	.enable_rchost = enable_rchost,
	.disable_rchost = disable_rchost,
	.stop_rchost = stop_rchost,
	.start_host = start_host,
	.stop_host = stop_host,
	.start_rcgadget = start_rcgadget,
	.stop_rcgadget = stop_rcgadget,
	.get_mode = get_mode,
	.request_session = request_session,
	.set_speed = set_speed,
	.get_speed = get_speed,
	.set_test_mode = set_test_mode,
	.get_test_mode = get_test_mode
};

/*
 * 関数：mcd_otg_control_signal
 * CIDおよびVbusの変化、Connect/Disconnect、SRP受信およびVbusError割り込みを
 * OTGステートマシンに入力する
 */

int
mcd_otg_control_signal(struct usb_otg_control *otg_control,
		       union usb_otg_event *otg_event)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;
	struct mcd_otg_port *otgc_port;
	struct usb_otg_control_port_info port_info;

	DBG(3, ">> mcd_otg_control_signal\n");

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	switch (otg_event->type) {
	case USB_OTG_EVENT_TYPE_CID:
		goto event_notify;
		break;
	case USB_OTG_EVENT_TYPE_VBUS:
		if (!get_port_info(otg_control, &port_info) &&
		    (otg_event->vbus.port == port_info.current_port)) {
			otgc_port = &otgc_intdat->port[otg_event->vbus.port];
			mcd_device_notify_vbus_change(otgi_to_mdev(otgc_intdat),
						      otgc_port->last_vbus,
						      otg_event->vbus.vbus_stat);
			DBG(3, "Vbus changing for gadget(port=%d, vbus=%d)\n",
			    otg_event->vbus.port, otgc_port->last_vbus);
			if (check_vpulse_start(otg_control,
					       otgc_port->last_vbus,
					       otg_event->vbus.vbus_stat)) {
				mcd_device_end_dpulse(otgi_to_mdev(otgc_intdat));
			}
			else if (check_vpulse_end(otg_control,
					     otgc_port->last_vbus,
					     otg_event->vbus.vbus_stat)) {
				finish_srp(otg_control);
			}
		}
		goto event_notify;
		break;
	case USB_OTG_EVENT_TYPE_CON:
		if (otgc_intdat->state == MCD_OTGC_S_A_HOST ||
		    (otgc_intdat->state == MCD_OTGC_S_B_HOST &&
		     otg_event->con.value == 0)) {
			goto event_notify;
		} else if (otgc_intdat->state == MCD_OTGC_S_A_PERIPH &&
			   otg_event->con.value == 0) {
			/* タイムアウトT_A_AIDL_BDIS停止 */
			stop_timer(otg_control);
			/* イベントタイプの入れ替え */
			otg_event->type = USB_OTG_EVENT_TYPE_RCGADGET;
			otg_event->rcgadget.status = USB_OTG_SUCCESS;
			goto event_notify;
		} else if (otgc_intdat->state == MCD_OTGC_S_B_WAIT_ACON &&
			   otg_event->con.value == 1) {
			/* タイムアウトT_B_ASE0_BRST停止 */
			stop_timer(otg_control);
			/* ステートの遷移 */
			otgc_intdat->state = MCD_OTGC_S_B_HOST;
			/* イベントタイプの入れ替え */
			otg_event->type = USB_OTG_EVENT_TYPE_RCHOST;
			otg_event->rchost.status = USB_OTG_SUCCESS;
			goto event_notify;
		} else {
			goto signal_ignore;
		}
		break;
	case USB_OTG_EVENT_TYPE_SRP:
		if (otgc_intdat->state == MCD_OTGC_S_AB_IDLE) {
			goto event_notify;
		} else {
			goto signal_error;
		}
		break;
	case USB_OTG_EVENT_TYPE_RCHOST:
		if (otgc_intdat->state == MCD_OTGC_S_B_WAIT_ACON) {
			if (otg_event->rchost.status == 0) {
				otgc_intdat->state = MCD_OTGC_S_B_HOST;
			} else {
				otgc_intdat->state = MCD_OTGC_S_B_PERIPH;
			}
			goto event_notify;
		} else {
			goto signal_error;
		}
		break;
	case USB_OTG_EVENT_TYPE_RCGADGET:
		if (otgc_intdat->state == MCD_OTGC_S_A_PERIPH) {
			goto event_notify;
		} else {
			goto signal_error;
		}
		break;
	case USB_OTG_EVENT_TYPE_VBUSERROR:
		goto event_notify;
		break;
	default:
		goto signal_error;
		break;
	}

	if (otgc_intdat->state != MCD_OTGC_S_AB_IDLE) {
		goto signal_error;
	}

      event_notify:
	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	if (!otg_control->otg_core) {
		return -EINVAL;
	}

	usb_otg_core_notify(otg_control->otg_core, otg_event);


	if (otgc_intdat->ops->control_io) {
		/* 排他制御開始 */
		spin_lock_irqsave(&otgc_intdat->lock, flags);

		if (otg_event->type == USB_OTG_EVENT_TYPE_CID) {
			otgc_intdat->ops->control_io(otg_control,
						     USB_OTG_IO_CHANGE_CID);
		}
		else if (otg_event->type == USB_OTG_EVENT_TYPE_CON &&
			 otgc_intdat->state == MCD_OTGC_S_A_HOST &&
			 otg_event->con.value == 0) {
			otgc_intdat->ops->control_io(otg_control,
						     USB_OTG_IO_DISCONN);
		}

		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);
	}

	return 0;

      signal_error:
	/* エラー処理 */
      signal_ignore:
	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return -EBUSY;
}

/*
 * 関数：event_signal_tasklet
 * Connect/Disconnect、SRP受信およびVbusError割り込みをTaskletとして
 * OTGステートマシンに入力する
 */

static void event_signal_tasklet(unsigned long data)
{
	unsigned long flags;
	struct usb_otg_control *otg_control;
	struct mcd_otg_intdat *otgc_intdat;
	struct mcd_otg_event_container *event_container;

	DBG(3, ">> event_signal_tasklet\n");

	/* 排他制御開始 */
	otg_control = (struct usb_otg_control *)data;
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	/* 呼び出し登録済みのすべてのイベントについて処理する */
	while (!list_empty(&otgc_intdat->event_list)) {
		event_container = list_entry(otgc_intdat->event_list.next,
					     struct mcd_otg_event_container,
					     list);
		/* コンテナリストからの削除 */
		list_del(&event_container->list);
		/* 排他制御一時停止 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);
		/* ステートマシン呼び出し */
		mcd_otg_control_signal(otg_control, &event_container->event);
		/* コンテナの破棄 */
		kfree(event_container);
		/* 排他制御再開 */
		spin_lock_irqsave(&otgc_intdat->lock, flags);
	}

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);
}

/* taskletの定義 */
DECLARE_TASKLET(event_signal, event_signal_tasklet, 0);

/*
 * 関数：mcd_otg_control_signal_interrupt
 * Connect/Disconnect、SRP受信およびVbusError割り込みをTaskletを介して
 * OTGステートマシンに入力する
 */

void
mcd_otg_control_signal_interrupt(struct usb_otg_control *otg_control,
				 union usb_otg_event *otg_event)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;
	struct mcd_otg_event_container *event_container;

	DBG(3, ">> mcd_otg_control_signal_interrupt\n");

	/* 排他制御開始 : 割り込み以外からも呼べる */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	/* イベントコンテナの確保 */
	event_container = (struct mcd_otg_event_container *)
	    kmalloc(sizeof(struct mcd_otg_event_container), GFP_ATOMIC);
	if (!event_container) {
		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);
		/* kmallocできなかった場合、割り込みの取りこぼしになる */
		return;
	}

	/* イベントのコピー */
	memcpy(&event_container->event, otg_event, sizeof(union usb_otg_event));

	INIT_LIST_HEAD(&event_container->list);
	list_add_tail(&event_container->list, &otgc_intdat->event_list);

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	/* taskletのスケジュール */
	event_signal.data = (unsigned long)otg_control;
	tasklet_schedule(&event_signal);
}
EXPORT_SYMBOL(mcd_otg_control_signal_interrupt);

/*
 * 関数：mcd_otg_control_start_rchost
 * Suspend信号をトリガとしてControllerをRCHOSTモードにする。
 */

int mcd_otg_control_start_rchost(struct usb_otg_control *otg_control)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->state != MCD_OTGC_S_B_PERIPH) {
		/* 排他制御終了 */
		spin_unlock_irqrestore(&otgc_intdat->lock, flags);

		return -EBUSY;
	}

	/* タイムアウトT_B_ASE0_BRST設定 */
	restart_b_ase0_brst_timer(otg_control);

	otgc_intdat->state = MCD_OTGC_S_B_WAIT_ACON;

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return 0;
}

/*
 * 関数：mcd_otg_control_create_object
 * usb_otg_control構造体のインスタンスオブジェクトの生成
 */

int mcd_otg_control_create_object(struct mcd_device *mdev,
				  unsigned int periodic_delay,
				  struct mcd_otg_intdat_ops *otgc_intdat_ops,
				  const char *name)
{
	struct usb_otg_control *otg_control;
	struct mcd_otg_intdat *otgc_intdat;
	u32 otg_name_len;

	DBG(3, "mcd_otg_control_create_object\n");

	otgc_intdat = &mdev->otgi;
	otg_control = &mdev->otgi.otgc;

	/* otg_controlを唯一のものとして扱う */
	registered_otgc = otg_control;

	/* コントローラ内部変数初期化 */
	/* otgc */
	otg_control->ops = &otg_control_ops;
	otg_control->otg_core = 0;
	otg_name_len = strnlen(name, MCD_OTG_MAX_NAME_LEN);
	otg_control->name = (char *)kmalloc(otg_name_len + 1, GFP_KERNEL);
	if (!otg_control->name) {
		DBG(2, "mcd_otg_control_create_object - no memory for name\n");
		return -ENOMEM;
	}
	strncpy((char *)otg_control->name, name, otg_name_len);
	((char *)otg_control->name)[otg_name_len] = '\0';
	/* otgc_intdat variables */
	spin_lock_init(&otgc_intdat->lock);
	otgc_intdat->state = MCD_OTGC_S_STOP;
	otgc_intdat->periodic_delay = periodic_delay;
	init_timer(&otgc_intdat->detector);
	otgc_intdat->detector.function = detector;
	otgc_intdat->detector.data = (unsigned long)otg_control;
	init_timer(&otgc_intdat->timer);
	otgc_intdat->timer.function = NULL;
	otgc_intdat->timer.data = (unsigned long)NULL;
	INIT_LIST_HEAD(&otgc_intdat->event_list);
	/* otgc_intdat functions */
	otgc_intdat->ops = otgc_intdat_ops;
	/* otgc_port allocation */
	otgc_intdat->port_num = 0;
	otgc_intdat->port =
	    (struct mcd_otg_port *)kzalloc(sizeof(struct mcd_otg_port)
					   * (mcd_board_get_max_port_num()),
					   GFP_KERNEL);
	if (!otgc_intdat->port) {
		DBG(2,
		    "mcd_otg_control_create_object - no memory for mcd_otg_port\n");
		kfree(otg_control->name);
		return -ENOMEM;
	}

	DBG(3, "finished otg_control allocate %s\n", name);

	return 0;
}

/*
 * 関数：mcd_otg_control_destroy_object
 * usb_otg_control構造体のインスタンスオブジェクトの生成
 */

int mcd_otg_control_destroy_object(struct mcd_device *mdev)
{
	struct usb_otg_control *otgc;
	struct mcd_otg_intdat *otgc_intdat;
	
	DBG(3, ">> mcd_otg_control_destroy_object\n");

	otgc_intdat = &mdev->otgi;
	otgc = &otgc_intdat->otgc;
	del_timer(&otgc_intdat->detector);
	del_timer(&otgc_intdat->timer);
	kfree(otgc_intdat->port);
	kfree(otgc->name);

	return 0;
}

/*
 * 関数：mcd_otg_control_attach_port
 * OTGコントローラへのUSBポート情報の設定
 */

int
mcd_otg_control_attach_port(struct usb_otg_control *otg_control,
			    u32 i, u32 cid_filter_count, u32 vbus_filter_count,
			    int (*check_cid) (struct usb_otg_control *, u32),
			    enum usb_otg_vbus_stat (*check_vbus) (
				    struct usb_otg_control *, u32))
{
	struct mcd_otg_intdat *otgc_intdat;
	struct mcd_otg_port *otgc_port;

	DBG(2, "<==\n");

	if (i < 0 || (mcd_board_get_max_port_num()) <= i) {
		return -EINVAL;
	}

	otgc_intdat = otgc_to_otgi(otg_control);
	otgc_port = &otgc_intdat->port[i];

	memset((void *)otgc_port, 0, sizeof(struct mcd_otg_port));

	/* CID初期値設定 */
	otgc_port->cid_check_count = 0;
	otgc_port->cid_filter_count = cid_filter_count;
	otgc_port->last_cid = otgc_port->ref_cid = -1;

	/* Vbus初期値確認 */
	otgc_port->vbus_filter_count = vbus_filter_count;
	otgc_port->vbus_check_count = 0;
	otgc_port->last_vbus = otgc_port->ref_vbus = USB_OTG_VBUS_STAT_UNKNOWN;

	/* ポート数の追加 */
	otgc_intdat->port_num++;

	/* チェック関数設定 */
	otgc_port->check_cid = check_cid;
	otgc_port->check_vbus = check_vbus;

	DBG(2, "==>\n");

	return 0;
}

/*
 * 関数：mcd_otg_control_detach_port
 * OTGコントローラへのUSBポート情報の設定解除
 */

int
mcd_otg_control_detach_port(struct usb_otg_control *otg_control, u32 i)
{
	struct mcd_otg_intdat *otgc_intdat;

	DBG(2, "<==\n");

	otgc_intdat = otgc_to_otgi(otg_control);

	if (otgc_intdat->port_num != i + 1) {
		return -EINVAL;
	}

	/* ポート数の削減 */
	otgc_intdat->port_num--;

	DBG(2, "==>\n");

	return 0;
}

/*
 * 関数：mcd_otg_control_notify_vbuserror
 * Vbusエラーを通知する。
 */

int mcd_otg_control_notify_vbuserror(struct usb_otg_control *otg_control)
{
	union usb_otg_event otg_event;
	struct mcd_otg_intdat *otgc_intdat;

	if (!otg_control) {
		return -EINVAL;
	}

	otgc_intdat = otgc_to_otgi(otg_control);

	if (!otg_control->otg_core) {
		return -EINVAL;
	}

	otg_event.type = USB_OTG_EVENT_TYPE_VBUSERROR;
	mcd_otg_control_signal_interrupt(otg_control, &otg_event);

	return 0;
}

EXPORT_SYMBOL(mcd_otg_control_notify_vbuserror);

/*
 * 関数：mcd_otg_control_io
 * IOを制御する
 */
int mcd_otg_control_io(struct usb_otg_control *otg_control, u32 control)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;
	int retval = 0;

	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->ops->control_io) {
		retval = otgc_intdat->ops->control_io(otg_control, control);
	}

	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return retval;
}

EXPORT_SYMBOL(mcd_otg_control_io);

/*
 * 関数：mcd_otg_control_force_vbus
 * Vbus入力を制御する。
 */
int
mcd_otg_control_force_vbus(struct usb_otg_control *otg_control, u32 control)
{
	struct mcd_otg_intdat *otgc_intdat;

	otgc_intdat = otgc_to_otgi(otg_control);

	return (otgc_intdat->ops->control_vbus
		? otgc_intdat->ops->control_vbus(otg_control, control)
		: 0);
}

/*
 * 関数：mcd_otg_control_check_suspend
 * デバイスのSuspend状態をチェックする
 */
bool
mcd_otg_control_check_suspend(struct usb_otg_control *otg_control, bool expected)
{
	struct mcd_otg_intdat *otgc_intdat;

	otgc_intdat = otgc_to_otgi(otg_control);

	return (otgc_intdat->ops->check_suspend
		? otgc_intdat->ops->check_suspend(otg_control)
		: expected);
}

/*
 * 関数：mcd_otg_control_start_detector
 * Vbus/CID検知タイマーdetectorを開始する。
 */
void mcd_otg_control_start_detector(struct usb_otg_control *otg_control)
{
	struct mcd_otg_intdat *otgc_intdat;

	DBG(2, "<==\n");
	otgc_intdat = otgc_to_otgi(otg_control);
	mod_timer(&otgc_intdat->detector,
		  jiffies + otgc_intdat->periodic_delay * HZ / 1000);
	DBG(2, "==>\n");
}


/*
 * 関数：mcd_otg_control_stop_detector
 * Vbus/CID検知タイマーdetectorを停止する。
 */
void mcd_otg_control_stop_detector(struct usb_otg_control *otg_control)
{
	struct mcd_otg_intdat *otgc_intdat;

	DBG(2, "<==\n");
	otgc_intdat = otgc_to_otgi(otg_control);
	del_timer(&otgc_intdat->detector);
	DBG(2, "==>\n");
}

/*
 * 関数：mcd_otg_control_suspend
 * コントローラをSuspendする
 */
int mcd_otg_control_suspend(struct usb_otg_control *otg_control,
			    pm_message_t message)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;
	int retval = 0;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->ops->control_io) {
		u32 control;

		if (message.event == PM_EVENT_SUSPEND) {
			control = USB_OTG_IO_SUSPEND;
		}
		else if (message.event == PM_EVENT_FREEZE) {
			control = USB_OTG_IO_FREEZE;
		}
		else if (message.event == PM_EVENT_PRETHAW) {
			control = USB_OTG_IO_PRETHAW;
		}
		else {
			goto no_control;
		}

		retval = otgc_intdat->ops->control_io(otg_control, control);
	}

no_control:
	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return retval;
}

/*
 * 関数：mcd_otg_control_resume
 * コントローラをResumeする
 */
int mcd_otg_control_resume(struct usb_otg_control *otg_control)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;
	int retval = 0;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->ops->control_io) {
		retval = otgc_intdat->ops->control_io(otg_control,
						      USB_OTG_IO_RESUME);
	}

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return retval;
}

/*
 * 関数：mcd_otg_control_shutdown
 * コントローラをShutdownする
 */
int mcd_otg_control_shutdown(struct usb_otg_control *otg_control)
{
	unsigned long flags;
	struct mcd_otg_intdat *otgc_intdat;

	/* 排他制御開始 */
	otgc_intdat = otgc_to_otgi(otg_control);
	spin_lock_irqsave(&otgc_intdat->lock, flags);

	if (otgc_intdat->ops->control_io) {
		int retval;

		retval = otgc_intdat->ops->control_io(otg_control,
						      USB_OTG_IO_SHUTDOWN);
		if (retval) {
			/* 排他制御終了 */
			spin_unlock_irqrestore(&otgc_intdat->lock, flags);

			return retval;
		}
	}

	/* 排他制御終了 */
	spin_unlock_irqrestore(&otgc_intdat->lock, flags);

	return 0;
}

/*
 * 関数：usb_otg_control_register_core
 * OTG Coreをコントローラへ登録する。
 */

int usb_otg_control_register_core(struct usb_otg_core *otg_core)
{
	DBG(3, ">> mcd_otg_control_register_core\n");

	registered_otgc->otg_core = otg_core;

	return usb_otg_core_bind(otg_core, registered_otgc);
}

EXPORT_SYMBOL(usb_otg_control_register_core);

/*
 * 関数：usb_otg_control_unregister_core
 * OTG Coreをコントローラへ登録解除する。
 */

int usb_otg_control_unregister_core(struct usb_otg_core *otg_core)
{
	DBG(3, ">> mcd_otg_control_unregister_core\n");

	if (registered_otgc->otg_core != otg_core) {
		return -EINVAL;
	}
	registered_otgc->otg_core = 0;

	return usb_otg_core_unbind(otg_core, registered_otgc);
}

EXPORT_SYMBOL(usb_otg_control_unregister_core);
