mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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:
		
							parent
							
								
									9e832bf7c0
								
							
						
					
					
						commit
						b5a2875605
					
				
					 1 changed files with 62 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -21,6 +21,7 @@
 | 
			
		|||
#include <linux/platform_device.h>
 | 
			
		||||
#include <linux/usb/ch9.h>
 | 
			
		||||
#include <linux/usb/gadget.h>
 | 
			
		||||
#include <linux/usb/otg.h>
 | 
			
		||||
#include "common.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +51,8 @@ struct usbhsg_gpriv {
 | 
			
		|||
	int			 uep_size;
 | 
			
		||||
 | 
			
		||||
	struct usb_gadget_driver	*driver;
 | 
			
		||||
	struct usb_phy		*transceiver;
 | 
			
		||||
	bool			 vbus_active;
 | 
			
		||||
 | 
			
		||||
	u32	status;
 | 
			
		||||
#define USBHSG_STATUS_STARTED		(1 << 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -872,6 +875,27 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status)
 | 
			
		|||
	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
 | 
			
		||||
| 
						 | 
				
			
			@ -882,12 +906,28 @@ static int usbhsg_gadget_start(struct usb_gadget *gadget,
 | 
			
		|||
{
 | 
			
		||||
	struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
 | 
			
		||||
	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
 | 
			
		||||
	struct device *dev = usbhs_priv_to_dev(priv);
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (!driver		||
 | 
			
		||||
	    !driver->setup	||
 | 
			
		||||
	    driver->max_speed < USB_SPEED_FULL)
 | 
			
		||||
		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 ... */
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	usbhsg_try_stop(priv, USBHSG_STATUS_REGISTERD);
 | 
			
		||||
 | 
			
		||||
	if (!IS_ERR_OR_NULL(gpriv->transceiver))
 | 
			
		||||
		otg_set_peripheral(gpriv->transceiver->otg, NULL);
 | 
			
		||||
 | 
			
		||||
	gpriv->driver = NULL;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -947,12 +991,26 @@ static int usbhsg_set_selfpowered(struct usb_gadget *gadget, int is_self)
 | 
			
		|||
	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 = {
 | 
			
		||||
	.get_frame		= usbhsg_get_frame,
 | 
			
		||||
	.set_selfpowered	= usbhsg_set_selfpowered,
 | 
			
		||||
	.udc_start		= usbhsg_gadget_start,
 | 
			
		||||
	.udc_stop		= usbhsg_gadget_stop,
 | 
			
		||||
	.pullup			= usbhsg_pullup,
 | 
			
		||||
	.vbus_session		= usbhsg_vbus_session,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED);
 | 
			
		||||
	dev_info(dev, "%stransceiver found\n",
 | 
			
		||||
		 gpriv->transceiver ? "" : "no ");
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * CAUTION
 | 
			
		||||
	 *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue