static int __exit bcmpmu_otg_xceiv_remove(struct platform_device *pdev) { struct bcmpmu_otg_xceiv_data *xceiv_data = platform_get_drvdata(pdev); xceiv_data->otg_xceiver.xceiver.state = OTG_STATE_UNDEFINED; device_remove_file(xceiv_data->dev, &dev_attr_wake); device_remove_file(xceiv_data->dev, &dev_attr_vbus); device_remove_file(xceiv_data->dev, &dev_attr_host); pm_runtime_disable(&pdev->dev); if (wake_lock_active(&xceiv_data->otg_xceiver.xceiver_wake_lock)) { wake_unlock(&xceiv_data->otg_xceiver.xceiver_wake_lock); wake_lock_destroy(&xceiv_data->otg_xceiver.xceiver_wake_lock); } bcmpmu_remove_notifier(BCMPMU_USB_EVENT_VBUS_VALID, &xceiv_data->bcm_otg_vbus_validity_notifier); bcmpmu_remove_notifier(BCMPMU_USB_EVENT_SESSION_INVALID, &xceiv_data->bcm_otg_vbus_validity_notifier); bcmpmu_remove_notifier(BCMPMU_USB_EVENT_ID_CHANGE, &xceiv_data->bcm_otg_id_chg_notifier); bcmpmu_remove_notifier(BCMPMU_USB_EVENT_USB_DETECTION, &xceiv_data->bcm_otg_chg_detection_notifier); destroy_workqueue(xceiv_data->bcm_otg_work_queue); bcmpmu_otg_free_regulator(xceiv_data); kfree(xceiv_data); bcm_hsotgctrl_phy_deinit(); local_otg_xceiver = NULL; return 0; }
static void bcmpmu_otg_xceiv_shutdown(struct otg_transceiver *otg) { struct bcmpmu_otg_xceiv_data *xceiv_data = dev_get_drvdata(otg->dev); if (xceiv_data) { /* De-initialize OTG core and PHY */ bcm_hsotgctrl_phy_deinit(); xceiv_data->otg_xceiver.xceiver.state = OTG_STATE_UNDEFINED; if (!xceiv_data->otg_enabled) { if (wake_lock_active (&xceiv_data->otg_xceiver.xceiver_wake_lock)) wake_unlock(&xceiv_data->otg_xceiver. xceiver_wake_lock); if (xceiv_data->bcm_hsotg_regulator && xceiv_data->regulator_enabled) { /* This should have no effect for most of * our platforms as "always on" parameter is set */ regulator_disable(xceiv_data-> bcm_hsotg_regulator); xceiv_data->regulator_enabled = false; } } } }
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_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; }