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; }