usb: renesas_usbhs: Allow an OTG PHY driver to provide VBUS
These changes allow a PHY driver to trigger a VBUS interrupt and to provide the value of VBUS. Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
Родитель
9e832bf7c0
Коммит
b5a2875605
|
@ -21,6 +21,7 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/usb/ch9.h>
|
#include <linux/usb/ch9.h>
|
||||||
#include <linux/usb/gadget.h>
|
#include <linux/usb/gadget.h>
|
||||||
|
#include <linux/usb/otg.h>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -50,6 +51,8 @@ struct usbhsg_gpriv {
|
||||||
int uep_size;
|
int uep_size;
|
||||||
|
|
||||||
struct usb_gadget_driver *driver;
|
struct usb_gadget_driver *driver;
|
||||||
|
struct usb_phy *transceiver;
|
||||||
|
bool vbus_active;
|
||||||
|
|
||||||
u32 status;
|
u32 status;
|
||||||
#define USBHSG_STATUS_STARTED (1 << 0)
|
#define USBHSG_STATUS_STARTED (1 << 0)
|
||||||
|
@ -872,6 +875,27 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VBUS provided by the PHY
|
||||||
|
*/
|
||||||
|
static int usbhsm_phy_get_vbus(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
|
||||||
|
struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
|
||||||
|
|
||||||
|
return gpriv->vbus_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usbhs_mod_phy_mode(struct usbhs_priv *priv)
|
||||||
|
{
|
||||||
|
struct usbhs_mod_info *info = &priv->mod_info;
|
||||||
|
|
||||||
|
info->irq_vbus = NULL;
|
||||||
|
priv->pfunc.get_vbus = usbhsm_phy_get_vbus;
|
||||||
|
|
||||||
|
usbhs_irq_callback_update(priv, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* linux usb function
|
* linux usb function
|
||||||
|
@ -882,12 +906,28 @@ static int usbhsg_gadget_start(struct usb_gadget *gadget,
|
||||||
{
|
{
|
||||||
struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
|
struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
|
||||||
struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
|
struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
|
||||||
|
struct device *dev = usbhs_priv_to_dev(priv);
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!driver ||
|
if (!driver ||
|
||||||
!driver->setup ||
|
!driver->setup ||
|
||||||
driver->max_speed < USB_SPEED_FULL)
|
driver->max_speed < USB_SPEED_FULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* connect to bus through transceiver */
|
||||||
|
if (!IS_ERR_OR_NULL(gpriv->transceiver)) {
|
||||||
|
ret = otg_set_peripheral(gpriv->transceiver->otg,
|
||||||
|
&gpriv->gadget);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "%s: can't bind to transceiver\n",
|
||||||
|
gpriv->gadget.name);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get vbus using phy versions */
|
||||||
|
usbhs_mod_phy_mode(priv);
|
||||||
|
}
|
||||||
|
|
||||||
/* first hook up the driver ... */
|
/* first hook up the driver ... */
|
||||||
gpriv->driver = driver;
|
gpriv->driver = driver;
|
||||||
|
|
||||||
|
@ -900,6 +940,10 @@ static int usbhsg_gadget_stop(struct usb_gadget *gadget)
|
||||||
struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
|
struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
|
||||||
|
|
||||||
usbhsg_try_stop(priv, USBHSG_STATUS_REGISTERD);
|
usbhsg_try_stop(priv, USBHSG_STATUS_REGISTERD);
|
||||||
|
|
||||||
|
if (!IS_ERR_OR_NULL(gpriv->transceiver))
|
||||||
|
otg_set_peripheral(gpriv->transceiver->otg, NULL);
|
||||||
|
|
||||||
gpriv->driver = NULL;
|
gpriv->driver = NULL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -947,12 +991,26 @@ static int usbhsg_set_selfpowered(struct usb_gadget *gadget, int is_self)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int usbhsg_vbus_session(struct usb_gadget *gadget, int is_active)
|
||||||
|
{
|
||||||
|
struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
|
||||||
|
struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
|
||||||
|
struct platform_device *pdev = usbhs_priv_to_pdev(priv);
|
||||||
|
|
||||||
|
gpriv->vbus_active = !!is_active;
|
||||||
|
|
||||||
|
renesas_usbhs_call_notify_hotplug(pdev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct usb_gadget_ops usbhsg_gadget_ops = {
|
static const struct usb_gadget_ops usbhsg_gadget_ops = {
|
||||||
.get_frame = usbhsg_get_frame,
|
.get_frame = usbhsg_get_frame,
|
||||||
.set_selfpowered = usbhsg_set_selfpowered,
|
.set_selfpowered = usbhsg_set_selfpowered,
|
||||||
.udc_start = usbhsg_gadget_start,
|
.udc_start = usbhsg_gadget_start,
|
||||||
.udc_stop = usbhsg_gadget_stop,
|
.udc_stop = usbhsg_gadget_stop,
|
||||||
.pullup = usbhsg_pullup,
|
.pullup = usbhsg_pullup,
|
||||||
|
.vbus_session = usbhsg_vbus_session,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int usbhsg_start(struct usbhs_priv *priv)
|
static int usbhsg_start(struct usbhs_priv *priv)
|
||||||
|
@ -994,6 +1052,10 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
|
||||||
goto usbhs_mod_gadget_probe_err_gpriv;
|
goto usbhs_mod_gadget_probe_err_gpriv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED);
|
||||||
|
dev_info(dev, "%stransceiver found\n",
|
||||||
|
gpriv->transceiver ? "" : "no ");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CAUTION
|
* CAUTION
|
||||||
*
|
*
|
||||||
|
|
Загрузка…
Ссылка в новой задаче