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