/* CNEN bit is required for function operation */ if (usbhs_get_dparam(priv, has_cnen)) {
mask |= CNEN;
val |= CNEN;
}
/* * if enable * * - select Function mode * - D+ Line Pull-up is disabled * When D+ Line Pull-up is enabled, * calling usbhs_sys_function_pullup(,1)
*/
usbhs_bset(priv, SYSCFG, mask, enable ? val : 0);
}
/* The first clock should exist */
priv->clks[0] = of_clk_get(dev_of_node(dev), 0); if (IS_ERR(priv->clks[0])) return PTR_ERR(priv->clks[0]);
/* * To backward compatibility with old DT, this driver checks the return * value if it's -ENOENT or not.
*/
priv->clks[1] = of_clk_get(dev_of_node(dev), 1); if (PTR_ERR(priv->clks[1]) == -ENOENT)
priv->clks[1] = NULL; elseif (IS_ERR(priv->clks[1])) {
clk_put(priv->clks[0]); return PTR_ERR(priv->clks[1]);
}
return 0;
}
staticvoid usbhsc_clk_put(struct usbhs_priv *priv)
{ int i;
if (!usbhsc_is_multi_clks(priv)) return;
for (i = 0; i < ARRAY_SIZE(priv->clks); i++)
clk_put(priv->clks[i]);
}
staticint usbhsc_clk_prepare_enable(struct usbhs_priv *priv)
{ int i, ret;
if (!usbhsc_is_multi_clks(priv)) return 0;
for (i = 0; i < ARRAY_SIZE(priv->clks); i++) {
ret = clk_prepare_enable(priv->clks[i]); if (ret) { while (--i >= 0)
clk_disable_unprepare(priv->clks[i]); return ret;
}
}
return ret;
}
staticvoid usbhsc_clk_disable_unprepare(struct usbhs_priv *priv)
{ int i;
if (!usbhsc_is_multi_clks(priv)) return;
for (i = 0; i < ARRAY_SIZE(priv->clks); i++)
clk_disable_unprepare(priv->clks[i]);
}
/* * hotplug
*/ staticvoid usbhsc_hotplug(struct usbhs_priv *priv)
{ struct platform_device *pdev = usbhs_priv_to_pdev(priv); struct usbhs_mod *mod = usbhs_mod_get_current(priv); int id; int enable; int cable; int ret;
/* * get vbus status from platform
*/
enable = usbhs_mod_info_call(priv, get_vbus, pdev);
/* * get id from platform
*/
id = usbhs_platform_call(priv, get_id, pdev);
if (enable && !mod) { if (priv->edev) {
cable = extcon_get_state(priv->edev, EXTCON_USB_HOST); if ((cable > 0 && id != USBHS_HOST) ||
(!cable && id != USBHS_GADGET)) {
dev_info(&pdev->dev, "USB cable plugged in doesn't match the selected role!\n"); return;
}
}
ret = usbhs_mod_change(priv, id); if (ret < 0) return;
dev_dbg(&pdev->dev, "%s enable\n", __func__);
/* power on */ if (usbhs_get_dparam(priv, runtime_pwctrl))
usbhsc_power_ctrl(priv, enable);
/* bus init */
usbhsc_set_buswait(priv);
usbhsc_bus_init(priv);
int usbhsc_schedule_notify_hotplug(struct platform_device *pdev)
{ struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); int delay = usbhs_get_dparam(priv, detection_delay);
/* * This functions will be called in interrupt. * To make sure safety context, * use workqueue for usbhs_notify_hotplug
*/
schedule_delayed_work(&priv->notify_hotplug_work,
msecs_to_jiffies(delay)); return 0;
}
info = of_device_get_match_data(dev); if (!info) {
info = dev_get_platdata(dev); if (!info) return dev_err_probe(dev, -EINVAL, "no platform info\n");
}
/* platform data */
irq = platform_get_irq(pdev, 0); if (irq < 0) return irq;
/* usb private data */
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM;
priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base);
if (of_property_present(dev_of_node(dev), "extcon")) {
priv->edev = extcon_get_edev_by_phandle(dev, 0); if (IS_ERR(priv->edev)) return PTR_ERR(priv->edev);
}
priv->rsts = devm_reset_control_array_get_optional_shared(dev); if (IS_ERR(priv->rsts)) return PTR_ERR(priv->rsts);
/* * care platform info
*/
priv->dparam = info->driver_param;
if (!info->platform_callback.get_id) {
dev_err(dev, "no platform callbacks\n"); return -EINVAL;
}
priv->pfunc = &info->platform_callback;
/* set default param if platform doesn't have */ if (usbhs_get_dparam(priv, has_new_pipe_configs)) {
priv->dparam.pipe_configs = usbhsc_new_pipe;
priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_new_pipe);
} elseif (!priv->dparam.pipe_configs) {
priv->dparam.pipe_configs = usbhsc_default_pipe;
priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_default_pipe);
} if (!priv->dparam.pio_dma_border)
priv->dparam.pio_dma_border = 64; /* 64byte */ if (!of_property_read_u32(dev_of_node(dev), "renesas,buswait", &tmp))
priv->dparam.buswait_bwait = tmp;
gpiod = devm_gpiod_get_optional(dev, "renesas,enable", GPIOD_IN); if (IS_ERR(gpiod)) return PTR_ERR(gpiod);
/* FIXME */ /* runtime power control ? */ if (priv->pfunc->get_vbus)
usbhs_get_dparam(priv, runtime_pwctrl) = 1;
/* * Acquire clocks and enable power management (PM) early in the * probe process, as the driver accesses registers during * initialization. Ensure the device is active before proceeding.
*/
pm_runtime_enable(dev);
ret = usbhsc_clk_get(dev, priv); if (ret) goto probe_pm_disable;
ret = pm_runtime_resume_and_get(dev); if (ret) goto probe_clk_put;
ret = usbhsc_clk_prepare_enable(priv); if (ret) goto probe_pm_put;
/* call pipe and module init */
ret = usbhs_pipe_probe(priv); if (ret < 0) goto probe_clk_dis_unprepare;
ret = usbhs_fifo_probe(priv); if (ret < 0) goto probe_end_pipe_exit;
ret = usbhs_mod_probe(priv); if (ret < 0) goto probe_end_fifo_exit;
/* platform_set_drvdata() should be called after usbhs_mod_probe() */
platform_set_drvdata(pdev, priv);
ret = reset_control_deassert(priv->rsts); if (ret) goto probe_fail_rst;
/* * device reset here because * USB device might be used in boot loader.
*/
usbhs_sys_clock_ctrl(priv, 0);
/* check GPIO determining if USB function should be enabled */ if (gpiod) {
ret = !gpiod_get_value(gpiod); if (ret) {
dev_warn(dev, "USB function not selected (GPIO)\n");
ret = -ENOTSUPP; goto probe_assert_rest;
}
}
/* * platform call * * USB phy setup might depend on CPU/Board. * If platform has its callback functions, * call it here.
*/
ret = usbhs_platform_call(priv, hardware_init, pdev); if (ret < 0) {
dev_err(dev, "platform init failed.\n"); goto probe_assert_rest;
}
/* reset phy for connection */
usbhs_platform_call(priv, phy_reset, pdev);
/* * Disable the clocks that were enabled earlier in the probe path, * and let the driver handle the clocks beyond this point.
*/
usbhsc_clk_disable_unprepare(priv);
pm_runtime_put(dev);
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.