static int bcmpmu_otg_xceiv_set_host(struct otg_transceiver *otg,
                                     struct usb_bus *host)
{
    struct bcmpmu_otg_xceiv_data *xceiv_data = dev_get_drvdata(otg->dev);
    int status = 0;

    dev_dbg(xceiv_data->dev, "Setting Host\n");
    otg->host = host;

    if (host) {
        if (xceiv_data->otg_enabled) {
            /* Wake lock forever in OTG build */
            wake_lock(&xceiv_data->otg_xceiver.xceiver_wake_lock);
            /* Do calibration probe */
            bcm_otg_do_adp_calibration_probe(xceiv_data);
        }

        if (bcmpmu_otg_xceiv_check_id_gnd(xceiv_data) ||
                bcmpmu_otg_xceiv_check_id_rid_a(xceiv_data)) {
            bcm_hsotgctrl_phy_set_id_stat(false);
            bcm_hsotgctrl_phy_set_non_driving(false);
        } else
            bcm_hsotgctrl_phy_set_id_stat(true);
    }
    return status;
}
static void bcmpmu_otg_xceiv_id_change_handler(struct work_struct *work)
{
	struct bcmpmu_otg_xceiv_data *xceiv_data =
	    container_of(work, struct bcmpmu_otg_xceiv_data,
			 bcm_otg_id_status_change_work);
	bool id_gnd = false;
	bool id_rid_a = false;
	bool id_rid_c = false;

	dev_info(xceiv_data->dev, "ID change detected\n");
	id_gnd = bcmpmu_otg_xceiv_check_id_gnd(xceiv_data);
	id_rid_a = bcmpmu_otg_xceiv_check_id_rid_a(xceiv_data);
	id_rid_c = bcmpmu_otg_xceiv_check_id_rid_c(xceiv_data);

	bcm_hsotgctrl_phy_set_id_stat(!(id_gnd || id_rid_a));

	/* If ID is gnd, we need to turn on Vbus within 200ms
	 * If ID is RID_A/B/C/FLOAT then we should not turn it on
	 */
	bcmpmu_otg_xceiv_set_vbus(&xceiv_data->otg_xceiver.
		    xceiver, id_gnd ? true : false);

	if (!id_rid_c)
		msleep(HOST_TO_PERIPHERAL_DELAY_MS);

	if (id_gnd || id_rid_a || id_rid_c) {
		bcm_hsotgctrl_phy_deinit();
		xceiv_data->otg_xceiver.xceiver.state = OTG_STATE_UNDEFINED;
		atomic_notifier_call_chain(&xceiv_data->otg_xceiver.xceiver.
					   notifier, USB_EVENT_ID, NULL);
	}
}
static void bcmpmu_otg_xceiv_select_host_mode(struct bcmpmu_otg_xceiv_data
        *xceiv_data, bool enable)
{
    if (enable) {
        dev_info(xceiv_data->dev, "Switching to Host\n");
        xceiv_data->host = true;
        bcm_hsotgctrl_set_phy_off(false);
        msleep(PERIPHERAL_TO_HOST_DELAY_MS);
        bcm_hsotgctrl_phy_set_id_stat(false);
    } else {
        dev_info(xceiv_data->dev, "Switching to Peripheral\n");
        bcm_hsotgctrl_phy_set_id_stat(true);
        if (xceiv_data->host) {
            xceiv_data->host = false;
            msleep(HOST_TO_PERIPHERAL_DELAY_MS);
        }
    }
}
static void bcmpmu_otg_xceiv_id_change_handler(struct work_struct *work)
{
	struct bcmpmu_otg_xceiv_data *xceiv_data =
	    container_of(work, struct bcmpmu_otg_xceiv_data,
			 bcm_otg_id_status_change_work);
	unsigned int new_id;
	bool id_gnd = false;
	bool id_rid_a = false;
	bool id_rid_c = false;

	dev_info(xceiv_data->dev, "ID change detected\n");

	bcmpmu_usb_get(xceiv_data->bcmpmu,
		BCMPMU_USB_CTRL_GET_ID_VALUE,
		&new_id);

	if (xceiv_data->prev_otg_id != new_id) {
		id_gnd = bcmpmu_otg_xceiv_check_id_gnd(xceiv_data);
		id_rid_a = bcmpmu_otg_xceiv_check_id_rid_a(xceiv_data);
		id_rid_c = bcmpmu_otg_xceiv_check_id_rid_c(xceiv_data);

		bcm_hsotgctrl_phy_set_id_stat(!(id_gnd || id_rid_a));

		if (id_gnd) {
			/* If ID is gnd, we need to turn on
			 * Vbus within 200ms
			 */
			bcmpmu_otg_xceiv_set_vbus(&xceiv_data->otg_xceiver.
				    xceiver, true);
		}

		if (!id_rid_c)
			msleep(HOST_TO_PERIPHERAL_DELAY_MS);

		if (id_gnd || id_rid_a || id_rid_c) {
			bcm_hsotgctrl_phy_deinit();
			xceiv_data->otg_xceiver.xceiver.state =
				OTG_STATE_UNDEFINED;
			atomic_notifier_call_chain(&xceiv_data->
				otg_xceiver.xceiver.notifier,
				USB_EVENT_ID, NULL);
		}
	}

	/* Update local ID copy */
	xceiv_data->prev_otg_id = new_id;

}
static int bcmpmu_otg_xceiv_set_peripheral(struct otg_transceiver *otg,
					   struct usb_gadget *gadget)
{
	struct bcmpmu_otg_xceiv_data *xceiv_data = dev_get_drvdata(otg->dev);
	int status = 0;
	bool id_default_host = false;

	dev_dbg(xceiv_data->dev, "Setting Peripheral\n");
	otg->gadget = gadget;

	id_default_host = bcmpmu_otg_xceiv_check_id_gnd(xceiv_data) ||
		  bcmpmu_otg_xceiv_check_id_rid_a(xceiv_data);

	if (!id_default_host) {
		if (xceiv_data->otg_enabled &&
			(bcmpmu_otg_xceiv_check_id_rid_b(xceiv_data) ==
			    false)) { /* No SRP if RID_B */
			/* REVISIT. Shutdown uses sequence for lowest power
			 * and does not meet timing so don't do that in OTG mode
			 * for now. Just do SRP for ADP startup */
			bcmpmu_otg_xceiv_do_srp(xceiv_data);
		} else {
			int data;
			bcmpmu_usb_get(xceiv_data->bcmpmu,
				       BCMPMU_USB_CTRL_GET_USB_TYPE, &data);
			if ((data != PMU_USB_TYPE_SDP)
			    && (data != PMU_USB_TYPE_CDP)) {
				/* Shutdown the core */
				atomic_notifier_call_chain(&xceiv_data->
							   otg_xceiver.xceiver.
							   notifier,
							   USB_EVENT_NONE,
							   NULL);
			}
		}

	} else {
		bcm_hsotgctrl_phy_set_id_stat(false);
		/* Come up connected  */
		bcm_hsotgctrl_phy_set_non_driving(false);
	}

	return status;
}
static void bcmpmu_otg_xceiv_id_change_handler(struct work_struct *work)
{
    struct bcmpmu_otg_xceiv_data *xceiv_data =
        container_of(work, struct bcmpmu_otg_xceiv_data,
                     bcm_otg_id_status_change_work);
    unsigned int new_id;

    bool id_gnd = false;
    bool id_rid_a = false;
    bool id_rid_c = false;

    dev_info(xceiv_data->dev, "ID change detected\n");

    bcmpmu_usb_get(xceiv_data->bcmpmu,
                   BCMPMU_USB_CTRL_GET_ID_VALUE,
                   &new_id);

    if (xceiv_data->otg_enabled) {
        /* Stop any stale ADP probe/sense attempts */
        bcm_otg_do_adp_probe(xceiv_data, false);
        bcm_otg_do_adp_sense(xceiv_data, false);

        /* Use n and n-1 comparison method */
        bcmpmu_usb_set(xceiv_data->bcmpmu,
                       BCMPMU_USB_CTRL_SET_ADP_COMP_METHOD, 1);
    }

    if ((xceiv_data->prev_otg_id != new_id) ||
            xceiv_data->otg_enabled) {
        id_gnd = bcmpmu_otg_xceiv_check_id_gnd(xceiv_data);
        id_rid_a = bcmpmu_otg_xceiv_check_id_rid_a(xceiv_data);
        id_rid_c = bcmpmu_otg_xceiv_check_id_rid_c(xceiv_data);

        bcm_hsotgctrl_phy_set_id_stat(!(id_gnd || id_rid_a));

        atomic_notifier_call_chain(&xceiv_data->otg_xceiver.
                                   xceiver.notifier, USB_EVENT_ID, NULL);
    }

    /* Update local ID copy */
    xceiv_data->prev_otg_id = new_id;

}
int bcm_hsotgctrl_phy_init(bool id_device)
{
	int val;
	struct bcm_hsotgctrl_drv_data *bcm_hsotgctrl_handle =
		local_hsotgctrl_handle;

	if (NULL == local_hsotgctrl_handle)
		return -ENODEV;

	if ((!bcm_hsotgctrl_handle->hsotg_ctrl_base) ||
		  (!bcm_hsotgctrl_handle->dev))
		return -EIO;

	bcm_hsotgctrl_en_clock(true);
	mdelay(HSOTGCTRL_STEP_DELAY_IN_MS);
	/* clear bit 15 RDB error */
	val = readl(bcm_hsotgctrl_handle->hsotg_ctrl_base +
		HSOTG_CTRL_PHY_P1CTL_OFFSET);
	val &= ~HSOTG_CTRL_PHY_P1CTL_USB11_OEB_IS_TXEB_MASK;
	writel(val, bcm_hsotgctrl_handle->hsotg_ctrl_base +
			HSOTG_CTRL_PHY_P1CTL_OFFSET);

	mdelay(HSOTGCTRL_STEP_DELAY_IN_MS);

	/* Enable software control of PHY-PM */
	bcm_hsotgctrl_set_soft_ldo_pwrdn(true);

	/* Put PHY in reset state */
	bcm_hsotgctrl_set_phy_resetb(false);

	/* Reset PHY and AHB clock domain */
	bcm_hsotgctrl_reset_clk_domain();

	/* Power up ALDO */
	bcm_hsotgctrl_set_aldo_pdn(true);
	mdelay(PHY_PM_DELAY_IN_MS);

	/* Enable pad, internal PLL etc */
	bcm_hsotgctrl_set_phy_off(false);

	bcm_hsotgctrl_set_ldo_suspend_mask();

	/* Remove PHY isolation */
	bcm_hsotgctrl_set_phy_iso(false);
	mdelay(PHY_PM_DELAY_IN_MS);

	/* PHY clock request */
	bcm_hsotgctrl_set_phy_clk_request(true);
	mdelay(PHY_PLL_DELAY_MS);

	/* Bring Put PHY out of reset state */
	bcm_hsotgctrl_set_phy_resetb(true);

	/* Don't disable software control of PHY-PM
	 * We want to control the PHY LDOs from software
	 */
	bcm_hsotgctrl_phy_mdio_initialization();

	if (id_device) {
		/* Set correct ID value */
		bcm_hsotgctrl_phy_set_id_stat(true);

		/* Set Vbus valid state */
		bcm_hsotgctrl_phy_set_vbus_stat(true);
	} else {
		/* Set correct ID value */
		bcm_hsotgctrl_phy_set_id_stat(false);
		/* Clear non-driving */
		bcm_hsotgctrl_phy_set_non_driving(false);
	}

	msleep(HSOTGCTRL_ID_CHANGE_DELAY_IN_MS);

	return 0;

}