static void bcmpmu_otg_xceiv_chg_detect_handler(struct work_struct *work)
{
    struct bcmpmu_otg_xceiv_data *xceiv_data =
        container_of(work, struct bcmpmu_otg_xceiv_data,
                     bcm_otg_chg_detect_work);

    dev_info(xceiv_data->dev, "Charger detect event\n");

    /* Read and save USB charger type */
    bcmpmu_usb_get(xceiv_data->bcmpmu,
                   BCMPMU_USB_CTRL_GET_USB_TYPE,
                   (void *)&xceiv_data->usb_charger_type);

    if (xceiv_data->otg_enabled) {
        /* Core is already up so just set the Vbus status */
        bcm_hsotgctrl_phy_set_vbus_stat(true);

        /* Vbus is up so allow the core to connect */
        bcm_hsotgctrl_phy_set_non_driving(false);
    } else {
        bool id_default_host = false;

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

        if (!id_default_host && xceiv_data->otg_xceiver.xceiver.gadget)
            atomic_notifier_call_chain(&xceiv_data->otg_xceiver.
                                       xceiver.notifier,
                                       USB_EVENT_VBUS, NULL);
    }
}
static int bcmpmu_otg_xceiv_set_vbus(struct otg_transceiver *otg, bool enabled)
{
	struct bcmpmu_otg_xceiv_data *xceiv_data = dev_get_drvdata(otg->dev);
	int stat;

	/* The order of these operations has temporarily been
	 * swapped due to overcurrent issue caused by slow I2C
	 * operations. I2C operations take >200ms to complete */
	bcm_hsotgctrl_phy_set_vbus_stat(enabled);

	if (enabled && bcmpmu_otg_xceiv_check_id_gnd(xceiv_data)) {
		dev_info(xceiv_data->dev, "Turning on VBUS\n");
		xceiv_data->vbus_enabled = true;
		stat =
		    xceiv_data->bcmpmu->usb_set(xceiv_data->bcmpmu,
						BCMPMU_USB_CTRL_VBUS_ON_OFF, 1);
	} else {
		dev_info(xceiv_data->dev, "Turning off VBUS\n");
		xceiv_data->vbus_enabled = false;
		stat =
		    xceiv_data->bcmpmu->usb_set(xceiv_data->bcmpmu,
						BCMPMU_USB_CTRL_VBUS_ON_OFF, 0);
	}

	if (stat < 0)
		dev_warn(xceiv_data->dev, "Failed to set VBUS\n");

	return stat;
}
static int bcmpmu_otg_xceiv_a_invalid_notif_handler(
	struct notifier_block *nb, unsigned long value, void *data)
{
	struct bcmpmu_otg_xceiv_data *xceiv_data =
	    container_of(nb, struct bcmpmu_otg_xceiv_data,
			 bcm_otg_vbus_a_invalid_notifier);
	bool suspend_allowed = false;

	if (!xceiv_data)
		return -EINVAL;

	/* Check if system suspend is allowed */
	bcm_hsotgctrl_is_suspend_allowed(&suspend_allowed);

	if (suspend_allowed) {
		/* Clock must be off. Enable OTG AHB clock */
		bcm_hsotgctrl_en_clock(true);
	}

	/* Inform the core of session invalid level  */
	bcm_hsotgctrl_phy_set_vbus_stat(false);

	/* This triggers shutdown that will turn off the clock */
	atomic_notifier_call_chain(&xceiv_data->otg_xceiver.
				   xceiver.notifier,
				   USB_EVENT_NONE, NULL);

	return 0;
}
static void bcmpmu_otg_xceiv_vbus_a_invalid_handler(struct work_struct *work)
{
	struct bcmpmu_otg_xceiv_data *xceiv_data =
	    container_of(work, struct bcmpmu_otg_xceiv_data,
			 bcm_otg_vbus_a_invalid_work);

	dev_info(xceiv_data->dev, "A session invalid\n");

	if (!bcm_hsotgctrl_get_clk_count())
		bcm_hsotgctrl_en_clock(true);

	/* Inform the core of session invalid level  */
	bcm_hsotgctrl_phy_set_vbus_stat(false);

	if (xceiv_data->otg_enabled) {
		/* Stop Vbus discharge */
		bcmpmu_usb_set(xceiv_data->bcmpmu, BCMPMU_USB_CTRL_DISCHRG_VBUS,
			       0);

		if (bcmpmu_otg_xceiv_check_id_gnd(xceiv_data)) {
			/* Use n-1 method for ADP rise time comparison */
			bcmpmu_usb_set(xceiv_data->bcmpmu,
				       BCMPMU_USB_CTRL_SET_ADP_COMP_METHOD, 1);
			if (xceiv_data->otg_xceiver.otg_vbus_off)
				schedule_delayed_work(&xceiv_data->
					bcm_otg_delayed_adp_work,
					msecs_to_jiffies
					  (T_NO_ADP_DELAY_MIN_IN_MS));
			else
				bcm_otg_do_adp_probe(xceiv_data);
		} else if (!bcmpmu_otg_xceiv_check_id_rid_a(xceiv_data)) {
			if (xceiv_data->otg_xceiver.otg_srp_reqd) {
				/* Start Session End SRP timer */
				xceiv_data->otg_xceiver.sess_end_srp_timer.
				    expires =
				    jiffies +
				    msecs_to_jiffies
				    (T_SESS_END_SRP_START_IN_MS);
				add_timer(&xceiv_data->otg_xceiver.
					  sess_end_srp_timer);
			} else
				bcm_otg_do_adp_sense(xceiv_data);
		}
	} else {
		bool id_default_host = false;

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

		if (!id_default_host) {
			atomic_notifier_call_chain(&xceiv_data->otg_xceiver.
						   xceiver.notifier,
						   USB_EVENT_NONE, NULL);
		}
	}
}
static void bcmpmu_otg_xceiv_chg_detect_handler(struct work_struct *work)
{
	struct bcmpmu_otg_xceiv_data *xceiv_data =
	    container_of(work, struct bcmpmu_otg_xceiv_data,
			 bcm_otg_chg_detect_work);

	dev_info(xceiv_data->dev, "Charger detect event\n");

	if (xceiv_data->otg_enabled) {
		/* Core is already up so just set the Vbus status */
		bcm_hsotgctrl_phy_set_vbus_stat(true);
	} else {
		bool id_default_host = false;

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

		if (!id_default_host && xceiv_data->otg_xceiver.xceiver.gadget)
			atomic_notifier_call_chain(&xceiv_data->otg_xceiver.
						   xceiver.notifier,
						   USB_EVENT_VBUS, NULL);
	}
}
int bcm_hsotgctrl_phy_deinit(void)
{
	struct bcm_hsotgctrl_drv_data *bcm_hsotgctrl_handle =
		local_hsotgctrl_handle;

	if (NULL == local_hsotgctrl_handle)
		return -ENODEV;

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

	if (bcm_hsotgctrl_handle->irq_enabled) {

		/* We are shutting down USB so ensure wake IRQ
		 * is disabled
		 */
		disable_irq(bcm_hsotgctrl_handle->hsotgctrl_irq);
		bcm_hsotgctrl_handle->irq_enabled = false;

	}

	if (work_pending(&bcm_hsotgctrl_handle->wakeup_work.work)) {

		/* Cancel scheduled work */
		cancel_delayed_work(&bcm_hsotgctrl_handle->
			wakeup_work);

		/* Make sure work queue is flushed */
		flush_workqueue(bcm_hsotgctrl_handle->
			bcm_hsotgctrl_work_queue);

	}

	/* Disable wakeup condition */
	bcm_hsotgctrl_phy_wakeup_condition(false);

	/* Stay disconnected */
	bcm_hsotgctrl_wakeup_core();
	bcm_hsotgctrl_phy_set_non_driving(true);

	/* Disable pad, internal PLL etc. */
	bcm_hsotgctrl_set_phy_off(true);

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

	/* Isolate PHY */
	bcm_hsotgctrl_set_phy_iso(true);

	/* Power down ALDO */
	bcm_hsotgctrl_set_aldo_pdn(false);

	/* Clear PHY reference clock request */
	bcm_hsotgctrl_set_phy_clk_request(false);

	/* Clear Vbus valid state */
	bcm_hsotgctrl_phy_set_vbus_stat(false);

	/* Disable the OTG core AHB clock */
	bcm_hsotgctrl_en_clock(false);
	return 0;
}
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;

}