Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f01ef574 authored by Pavankumar Kondeti's avatar Pavankumar Kondeti Committed by Greg Kroah-Hartman
Browse files

USB: gadget: Introduce ci13xxx_udc_driver struct



Introduces ci13xxx_udc_driver struct for bus glue drivers to hint
ci13xxx_udc core about their special requirements.  The flags include
avoiding hardware register access when controller is not in peripheral
mode, enabling pull-up upon VBUS, disabling streaming mode and dependency
on transceiver driver.

Initialize gadget_ops in udc_probe so that transceiver can notify VBUS
presence even when no gadget driver is bounded.

A notify_event callback is embedded in the same struct. This patch implements
two events called CONTROLLER_RESET_EVENT and CONTROLLER_STOPPED_EVENT to
notify the bus glue driver after resetting and stopping the controller for
performing SoC specific quirks.

Signed-off-by: default avatarPavankumar Kondeti <pkondeti@codeaurora.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 61948ee4
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -38,6 +38,10 @@ static irqreturn_t ci13xxx_pci_irq(int irq, void *pdev)
	return udc_irq();
}

static struct ci13xxx_udc_driver ci13xxx_pci_udc_driver = {
	.name		= UDC_DRIVER_NAME,
};

/**
 * ci13xxx_pci_probe: PCI probe
 * @pdev: USB device controller being probed
@@ -82,7 +86,7 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
	pci_set_master(pdev);
	pci_try_set_mwi(pdev);

	retval = udc_probe(&pdev->dev, regs, UDC_DRIVER_NAME);
	retval = udc_probe(&ci13xxx_pci_udc_driver, &pdev->dev, regs);
	if (retval)
		goto iounmap;

+151 −54
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@
#include <linux/slab.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>

#include "ci13xxx_udc.h"

@@ -126,6 +127,9 @@ static struct {
	size_t        size;   /* bank size */
} hw_bank;

/* MSM specific */
#define ABS_AHBBURST        (0x0090UL)
#define ABS_AHBMODE         (0x0098UL)
/* UDC register map */
#define ABS_CAPLENGTH       (0x100UL)
#define ABS_HCCPARAMS       (0x108UL)
@@ -242,13 +246,7 @@ static u32 hw_ctest_and_write(u32 addr, u32 mask, u32 data)
	return (reg & mask) >> ffs_nr(mask);
}

/**
 * hw_device_reset: resets chip (execute without interruption)
 * @base: register base address
 *
 * This function returns an error code
 */
static int hw_device_reset(void __iomem *base)
static int hw_device_init(void __iomem *base)
{
	u32 reg;

@@ -265,6 +263,28 @@ static int hw_device_reset(void __iomem *base)
	hw_bank.size += CAP_LAST;
	hw_bank.size /= sizeof(u32);

	reg = hw_aread(ABS_DCCPARAMS, DCCPARAMS_DEN) >> ffs_nr(DCCPARAMS_DEN);
	if (reg == 0 || reg > ENDPT_MAX)
		return -ENODEV;

	hw_ep_max = reg;   /* cache hw ENDPT_MAX */

	/* setup lock mode ? */

	/* ENDPTSETUPSTAT is '0' by default */

	/* HCSPARAMS.bf.ppc SHOULD BE zero for device */

	return 0;
}
/**
 * hw_device_reset: resets chip (execute without interruption)
 * @base: register base address
 *
 * This function returns an error code
 */
static int hw_device_reset(struct ci13xxx *udc)
{
	/* should flush & stop before reset */
	hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0);
	hw_cwrite(CAP_USBCMD, USBCMD_RS, 0);
@@ -273,6 +293,14 @@ static int hw_device_reset(void __iomem *base)
	while (hw_cread(CAP_USBCMD, USBCMD_RST))
		udelay(10);             /* not RTOS friendly */


	if (udc->udc_driver->notify_event)
		udc->udc_driver->notify_event(udc,
			CI13XXX_CONTROLLER_RESET_EVENT);

	if (udc->udc_driver->flags && CI13XXX_DISABLE_STREAMING)
		hw_cwrite(CAP_USBMODE, USBMODE_SDIS, USBMODE_SDIS);

	/* USBMODE should be configured step by step */
	hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
	hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE);
@@ -284,18 +312,6 @@ static int hw_device_reset(void __iomem *base)
		return -ENODEV;
	}

	reg = hw_aread(ABS_DCCPARAMS, DCCPARAMS_DEN) >> ffs_nr(DCCPARAMS_DEN);
	if (reg == 0 || reg > ENDPT_MAX)
		return -ENODEV;

	hw_ep_max = reg;   /* cache hw ENDPT_MAX */

	/* setup lock mode ? */

	/* ENDPTSETUPSTAT is '0' by default */

	/* HCSPARAMS.bf.ppc SHOULD BE zero for device */

	return 0;
}

@@ -1551,8 +1567,6 @@ __acquires(mEp->lock)
 * Caller must hold lock
 */
static int _gadget_stop_activity(struct usb_gadget *gadget)
__releases(udc->lock)
__acquires(udc->lock)
{
	struct usb_ep *ep;
	struct ci13xxx    *udc = container_of(gadget, struct ci13xxx, gadget);
@@ -1564,8 +1578,6 @@ __acquires(udc->lock)
	if (gadget == NULL)
		return -EINVAL;

	spin_unlock(udc->lock);

	/* flush all endpoints */
	gadget_for_each_ep(ep, gadget) {
		usb_ep_fifo_flush(ep);
@@ -1585,8 +1597,6 @@ __acquires(udc->lock)
		mEp->status = NULL;
	}

	spin_lock(udc->lock);

	return 0;
}

@@ -1615,6 +1625,7 @@ __acquires(udc->lock)

	dbg_event(0xFF, "BUS RST", 0);

	spin_unlock(udc->lock);
	retval = _gadget_stop_activity(&udc->gadget);
	if (retval)
		goto done;
@@ -1623,7 +1634,6 @@ __acquires(udc->lock)
	if (retval)
		goto done;

	spin_unlock(udc->lock);
	retval = usb_ep_enable(&mEp->ep, &ctrl_endpt_desc);
	if (!retval) {
		mEp->status = usb_ep_alloc_request(&mEp->ep, GFP_ATOMIC);
@@ -2321,12 +2331,45 @@ static const struct usb_ep_ops usb_ep_ops = {
/******************************************************************************
 * GADGET block
 *****************************************************************************/
static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
{
	struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
	unsigned long flags;
	int gadget_ready = 0;

	if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS))
		return -EOPNOTSUPP;

	spin_lock_irqsave(udc->lock, flags);
	udc->vbus_active = is_active;
	if (udc->driver)
		gadget_ready = 1;
	spin_unlock_irqrestore(udc->lock, flags);

	if (gadget_ready) {
		if (is_active) {
			hw_device_reset(udc);
			hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma);
		} else {
			hw_device_state(0);
			if (udc->udc_driver->notify_event)
				udc->udc_driver->notify_event(udc,
				CI13XXX_CONTROLLER_STOPPED_EVENT);
			_gadget_stop_activity(&udc->gadget);
		}
	}

	return 0;
}

/**
 * Device operations part of the API to the USB controller hardware,
 * which don't involve endpoints (or i/o)
 * Check  "usb_gadget.h" for details
 */
static const struct usb_gadget_ops usb_gadget_ops;
static const struct usb_gadget_ops usb_gadget_ops = {
	.vbus_session	= ci13xxx_vbus_session,
};

/**
 * usb_gadget_probe_driver: register a gadget driver
@@ -2379,7 +2422,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
	info("hw_ep_max = %d", hw_ep_max);

	udc->driver = driver;
	udc->gadget.ops        = NULL;
	udc->gadget.dev.driver = NULL;

	retval = 0;
@@ -2420,7 +2462,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,

	/* bind gadget */
	driver->driver.bus     = NULL;
	udc->gadget.ops        = &usb_gadget_ops;
	udc->gadget.dev.driver = &driver->driver;

	spin_unlock_irqrestore(udc->lock, flags);
@@ -2428,11 +2469,19 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
	spin_lock_irqsave(udc->lock, flags);

	if (retval) {
		udc->gadget.ops        = NULL;
		udc->gadget.dev.driver = NULL;
		goto done;
	}

	if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) {
		if (udc->vbus_active) {
			if (udc->udc_driver->flags & CI13XXX_REGS_SHARED)
				hw_device_reset(udc);
		} else {
			goto done;
		}
	}

	retval = hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma);

 done:
@@ -2466,19 +2515,21 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)

	spin_lock_irqsave(udc->lock, flags);

	if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) ||
			udc->vbus_active) {
		hw_device_state(0);

	/* unbind gadget */
	if (udc->gadget.ops != NULL) {
		if (udc->udc_driver->notify_event)
			udc->udc_driver->notify_event(udc,
			CI13XXX_CONTROLLER_STOPPED_EVENT);
		_gadget_stop_activity(&udc->gadget);
	}

	/* unbind gadget */
	spin_unlock_irqrestore(udc->lock, flags);
	driver->unbind(&udc->gadget);               /* MAY SLEEP */
	spin_lock_irqsave(udc->lock, flags);

		udc->gadget.ops        = NULL;
	udc->gadget.dev.driver = NULL;
	}

	/* free resources */
	for (i = 0; i < hw_ep_max; i++) {
@@ -2535,6 +2586,14 @@ static irqreturn_t udc_irq(void)
	}

	spin_lock(udc->lock);

	if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) {
		if (hw_cread(CAP_USBMODE, USBMODE_CM) !=
				USBMODE_CM_DEVICE) {
			spin_unlock(udc->lock);
			return IRQ_NONE;
		}
	}
	intr = hw_test_and_clear_intr_active();
	if (intr) {
		isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intr;
@@ -2593,14 +2652,16 @@ static void udc_release(struct device *dev)
 * No interrupts active, the IRQ has not been requested yet
 * Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask
 */
static int udc_probe(struct device *dev, void __iomem *regs, const char *name)
static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
		void __iomem *regs)
{
	struct ci13xxx *udc;
	int retval = 0;

	trace("%p, %p, %p", dev, regs, name);

	if (dev == NULL || regs == NULL || name == NULL)
	if (dev == NULL || regs == NULL || driver == NULL ||
			driver->name == NULL)
		return -EINVAL;

	udc = kzalloc(sizeof(struct ci13xxx), GFP_KERNEL);
@@ -2608,16 +2669,14 @@ static int udc_probe(struct device *dev, void __iomem *regs, const char *name)
		return -ENOMEM;

	udc->lock = &udc_lock;
	udc->regs = regs;
	udc->udc_driver = driver;

	retval = hw_device_reset(regs);
	if (retval)
		goto done;

	udc->gadget.ops          = NULL;
	udc->gadget.ops          = &usb_gadget_ops;
	udc->gadget.speed        = USB_SPEED_UNKNOWN;
	udc->gadget.is_dualspeed = 1;
	udc->gadget.is_otg       = 0;
	udc->gadget.name         = name;
	udc->gadget.name         = driver->name;

	INIT_LIST_HEAD(&udc->gadget.ep_list);
	udc->gadget.ep0 = NULL;
@@ -2628,23 +2687,57 @@ static int udc_probe(struct device *dev, void __iomem *regs, const char *name)
	udc->gadget.dev.parent   = dev;
	udc->gadget.dev.release  = udc_release;

	retval = device_register(&udc->gadget.dev);
	retval = hw_device_init(regs);
	if (retval < 0)
		goto free_udc;

	udc->transceiver = otg_get_transceiver();

	if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) {
		if (udc->transceiver == NULL) {
			retval = -ENODEV;
			goto free_udc;
		}
	}

	if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) {
		retval = hw_device_reset(udc);
		if (retval)
		goto done;
			goto put_transceiver;
	}

	retval = device_register(&udc->gadget.dev);
	if (retval) {
		put_device(&udc->gadget.dev);
		goto put_transceiver;
	}

#ifdef CONFIG_USB_GADGET_DEBUG_FILES
	retval = dbg_create_files(&udc->gadget.dev);
#endif
	if (retval) {
		device_unregister(&udc->gadget.dev);
		goto done;
	if (retval)
		goto unreg_device;

	if (udc->transceiver) {
		retval = otg_set_peripheral(udc->transceiver, &udc->gadget);
		if (retval)
			goto remove_dbg;
	}

	_udc = udc;
	return retval;

 done:
	err("error = %i", retval);
remove_dbg:
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
	dbg_remove_files(&udc->gadget.dev);
#endif
unreg_device:
	device_unregister(&udc->gadget.dev);
put_transceiver:
	if (udc->transceiver)
		otg_put_transceiver(udc->transceiver);
free_udc:
	kfree(udc);
	_udc = NULL;
	return retval;
@@ -2664,6 +2757,10 @@ static void udc_remove(void)
		return;
	}

	if (udc->transceiver) {
		otg_set_peripheral(udc->transceiver, &udc->gadget);
		otg_put_transceiver(udc->transceiver);
	}
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
	dbg_remove_files(&udc->gadget.dev);
#endif
+19 −0
Original line number Diff line number Diff line
@@ -97,9 +97,24 @@ struct ci13xxx_ep {
	struct dma_pool                       *td_pool;
};

struct ci13xxx;
struct ci13xxx_udc_driver {
	const char	*name;
	unsigned long	 flags;
#define CI13XXX_REGS_SHARED		BIT(0)
#define CI13XXX_REQUIRE_TRANSCEIVER	BIT(1)
#define CI13XXX_PULLUP_ON_VBUS		BIT(2)
#define CI13XXX_DISABLE_STREAMING	BIT(3)

#define CI13XXX_CONTROLLER_RESET_EVENT		0
#define CI13XXX_CONTROLLER_STOPPED_EVENT	1
	void	(*notify_event) (struct ci13xxx *udc, unsigned event);
};

/* CI13XXX UDC descriptor & global resources */
struct ci13xxx {
	spinlock_t		  *lock;      /* ctrl register bank access */
	void __iomem              *regs;      /* registers address space */

	struct dma_pool           *qh_pool;   /* DMA pool for queue heads */
	struct dma_pool           *td_pool;   /* DMA pool for transfer descs */
@@ -108,6 +123,9 @@ struct ci13xxx {
	struct ci13xxx_ep          ci13xxx_ep[ENDPT_MAX]; /* extended endpts */

	struct usb_gadget_driver  *driver;     /* 3rd party gadget driver */
	struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
	int                        vbus_active; /* is VBUS active */
	struct otg_transceiver    *transceiver; /* Transceiver struct */
};

/******************************************************************************
@@ -157,6 +175,7 @@ struct ci13xxx {
#define    USBMODE_CM_DEVICE  (0x02UL <<  0)
#define    USBMODE_CM_HOST    (0x03UL <<  0)
#define USBMODE_SLOM          BIT(3)
#define USBMODE_SDIS          BIT(4)

/* ENDPTCTRL */
#define ENDPTCTRL_RXS         BIT(0)