/* ensure that above write is through */
readl(base + offset);
}
/* * TODO: Make the in-core role switching code invoke dwc3_qcom_vbus_override_enable(), * validate that the in-core extcon support is functional, and drop extcon * handling from the glue
*/ staticvoid dwc3_qcom_vbus_override_enable(struct dwc3_qcom *qcom, bool enable)
{ if (enable) {
dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_SS_PHY_CTRL,
LANE0_PWR_PRESENT);
dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_HS_PHY_CTRL,
UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL);
} else {
dwc3_qcom_clrbits(qcom->qscratch_base, QSCRATCH_SS_PHY_CTRL,
LANE0_PWR_PRESENT);
dwc3_qcom_clrbits(qcom->qscratch_base, QSCRATCH_HS_PHY_CTRL,
UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL);
}
}
/* Update initial VBUS override based on extcon state */ if (extcon_get_state(qcom->edev, EXTCON_USB) ||
!extcon_get_state(host_edev, EXTCON_USB_HOST))
dwc3_qcom_vbus_notifier(&qcom->vbus_nb, true, qcom->edev); else
dwc3_qcom_vbus_notifier(&qcom->vbus_nb, false, qcom->edev);
return 0;
}
staticint dwc3_qcom_interconnect_enable(struct dwc3_qcom *qcom)
{ int ret;
ret = icc_enable(qcom->icc_path_ddr); if (ret) return ret;
ret = icc_enable(qcom->icc_path_apps); if (ret)
icc_disable(qcom->icc_path_ddr);
return ret;
}
staticint dwc3_qcom_interconnect_disable(struct dwc3_qcom *qcom)
{ int ret;
ret = icc_disable(qcom->icc_path_ddr); if (ret) return ret;
ret = icc_disable(qcom->icc_path_apps); if (ret)
icc_enable(qcom->icc_path_ddr);
return ret;
}
/** * dwc3_qcom_interconnect_init() - Get interconnect path handles * and set bandwidth. * @qcom: Pointer to the concerned usb core. *
*/ staticint dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom)
{ enum usb_device_speed max_speed; struct device *dev = qcom->dev; int ret;
qcom->icc_path_ddr = of_icc_get(dev, "usb-ddr"); if (IS_ERR(qcom->icc_path_ddr)) { return dev_err_probe(dev, PTR_ERR(qcom->icc_path_ddr), "failed to get usb-ddr path\n");
}
qcom->icc_path_apps = of_icc_get(dev, "apps-usb"); if (IS_ERR(qcom->icc_path_apps)) {
ret = dev_err_probe(dev, PTR_ERR(qcom->icc_path_apps), "failed to get apps-usb path\n"); goto put_path_ddr;
}
max_speed = usb_get_maximum_speed(qcom->dwc.dev); if (max_speed >= USB_SPEED_SUPER || max_speed == USB_SPEED_UNKNOWN) {
ret = icc_set_bw(qcom->icc_path_ddr,
USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW);
} else {
ret = icc_set_bw(qcom->icc_path_ddr,
USB_MEMORY_AVG_HS_BW, USB_MEMORY_PEAK_HS_BW);
} if (ret) {
dev_err(dev, "failed to set bandwidth for usb-ddr path: %d\n", ret); goto put_path_apps;
}
ret = icc_set_bw(qcom->icc_path_apps, APPS_USB_AVG_BW, APPS_USB_PEAK_BW); if (ret) {
dev_err(dev, "failed to set bandwidth for apps-usb path: %d\n", ret); goto put_path_apps;
}
/** * dwc3_qcom_interconnect_exit() - Release interconnect path handles * @qcom: Pointer to the concerned usb core. * * This function is used to release interconnect path handle.
*/ staticvoid dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom)
{
icc_put(qcom->icc_path_ddr);
icc_put(qcom->icc_path_apps);
}
/* Only usable in contexts where the role can not change. */ staticbool dwc3_qcom_is_host(struct dwc3_qcom *qcom)
{ return qcom->dwc.xhci;
}
/* * Configure DP/DM line interrupts based on the USB2 device attached to * the root hub port. When HS/FS device is connected, configure the DP line * as falling edge to detect both disconnect and remote wakeup scenarios. When * LS device is connected, configure DM line as falling edge to detect both * disconnect and remote wakeup. When no device is connected, configure both * DP and DM lines as rising edge to detect HS/HS/LS device connect scenario.
*/
staticvoid dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
{ int i;
for (i = 0; i < qcom->num_ports; i++)
dwc3_qcom_disable_port_interrupts(&qcom->ports[i]);
}
staticvoid dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
{ int i;
for (i = 0; i < qcom->num_ports; i++)
dwc3_qcom_enable_port_interrupts(&qcom->ports[i]);
}
staticint dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
{
u32 val; int i, ret;
if (qcom->is_suspended) return 0;
for (i = 0; i < qcom->num_ports; i++) {
val = readl(qcom->qscratch_base + pwr_evnt_irq_stat_reg[i]); if (!(val & PWR_EVNT_LPM_IN_L2_MASK))
dev_err(qcom->dev, "port-%d HS-PHY not in L2\n", i + 1);
}
clk_bulk_disable_unprepare(qcom->num_clocks, qcom->clks);
ret = dwc3_qcom_interconnect_disable(qcom); if (ret)
dev_warn(qcom->dev, "failed to disable interconnect: %d\n", ret);
/* * The role is stable during suspend as role switching is done from a * freezable workqueue.
*/ if (dwc3_qcom_is_host(qcom) && wakeup) { for (i = 0; i < qcom->num_ports; i++)
qcom->ports[i].usb2_speed = dwc3_qcom_read_usb2_speed(qcom, i);
dwc3_qcom_enable_interrupts(qcom);
}
qcom->is_suspended = true;
return 0;
}
staticint dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup)
{ int ret; int i;
if (!qcom->is_suspended) return 0;
if (dwc3_qcom_is_host(qcom) && wakeup)
dwc3_qcom_disable_interrupts(qcom);
ret = clk_bulk_prepare_enable(qcom->num_clocks, qcom->clks); if (ret < 0) return ret;
ret = dwc3_qcom_interconnect_enable(qcom); if (ret)
dev_warn(qcom->dev, "failed to enable interconnect: %d\n", ret);
/* Clear existing events from PHY related to L2 in/out */ for (i = 0; i < qcom->num_ports; i++) {
dwc3_qcom_setbits(qcom->qscratch_base,
pwr_evnt_irq_stat_reg[i],
PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK);
}
/* If pm_suspended then let pm_resume take care of resuming h/w */ if (qcom->pm_suspended) return IRQ_HANDLED;
/* * This is safe as role switching is done from a freezable workqueue * and the wakeup interrupts are disabled as part of resume.
*/ if (dwc3_qcom_is_host(qcom))
pm_runtime_resume(&dwc->xhci->dev);
return IRQ_HANDLED;
}
staticvoid dwc3_qcom_select_utmi_clk(struct dwc3_qcom *qcom)
{ /* Configure dwc3 to use UTMI clock as PIPE clock not present */
dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG,
PIPE_UTMI_CLK_DIS);
qcom = devm_kzalloc(&pdev->dev, sizeof(*qcom), GFP_KERNEL); if (!qcom) return -ENOMEM;
qcom->dev = &pdev->dev;
qcom->resets = devm_reset_control_array_get_optional_exclusive(dev); if (IS_ERR(qcom->resets)) { return dev_err_probe(&pdev->dev, PTR_ERR(qcom->resets), "failed to get resets\n");
}
ret = devm_clk_bulk_get_all(&pdev->dev, &qcom->clks); if (ret < 0) return dev_err_probe(dev, ret, "failed to get clocks\n");
qcom->num_clocks = ret;
ret = reset_control_assert(qcom->resets); if (ret) {
dev_err(&pdev->dev, "failed to assert resets, err=%d\n", ret); return ret;
}
usleep_range(10, 1000);
ret = reset_control_deassert(qcom->resets); if (ret) {
dev_err(&pdev->dev, "failed to deassert resets, err=%d\n", ret); return ret;
}
ret = clk_bulk_prepare_enable(qcom->num_clocks, qcom->clks); if (ret < 0) return ret;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) {
ret = -EINVAL; goto clk_disable;
}
res = *r;
res.end = res.start + SDM845_QSCRATCH_BASE_OFFSET;
qcom->qscratch_base = devm_ioremap(dev, res.end, SDM845_QSCRATCH_SIZE); if (!qcom->qscratch_base) {
dev_err(dev, "failed to map qscratch region\n");
ret = -ENOMEM; goto clk_disable;
}
ret = dwc3_qcom_setup_irq(qcom, pdev); if (ret) {
dev_err(dev, "failed to setup IRQs, err=%d\n", ret); goto clk_disable;
}
/* * Disable pipe_clk requirement if specified. Used when dwc3 * operates without SSPHY and only HS/FS/LS modes are supported.
*/
ignore_pipe_clk = device_property_read_bool(dev, "qcom,select-utmi-as-pipe-clk"); if (ignore_pipe_clk)
dwc3_qcom_select_utmi_clk(qcom);
qcom->dwc.dev = dev;
probe_data.dwc = &qcom->dwc;
probe_data.res = &res;
probe_data.ignore_clocks_and_resets = true;
ret = dwc3_core_probe(&probe_data); if (ret) {
ret = dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); goto clk_disable;
}
ret = dwc3_qcom_interconnect_init(qcom); if (ret) goto remove_core;
qcom->mode = usb_get_dr_mode(dev);
/* enable vbus override for device mode */ if (qcom->mode != USB_DR_MODE_HOST)
dwc3_qcom_vbus_override_enable(qcom, true);
/* register extcon to override sw_vbus on Vbus change later */
ret = dwc3_qcom_register_extcon(qcom); if (ret) goto interconnect_exit;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.