diff options
| author | Alexander Shishkin <alexander.shishkin@linux.intel.com> | 2012-05-11 17:25:47 +0300 | 
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-11 16:52:10 -0700 | 
| commit | 5f36e231e9dbffb5264612e5b5817ab574a5e5db (patch) | |
| tree | a71027cded532334d3d51cbf737925240d34e7df | |
| parent | e443b333629f82ca0da91a05ca638050943bbedd (diff) | |
| download | olio-linux-3.10-5f36e231e9dbffb5264612e5b5817ab574a5e5db.tar.xz olio-linux-3.10-5f36e231e9dbffb5264612e5b5817ab574a5e5db.zip  | |
usb: chipidea: add support for roles
Add some generic code for roles and implement simple role switching
based on ID pin state and/or a sysfs file. At this, we also rename
the device to ci_hdrc, which is what it is.
The "manual" switch is made into a sysfs file and not debugfs, because
it might be useful even in non-debug context. For some boards, like
sheevaplug, it seems to be the only way to switch roles without modifying
the hardware, since the ID pin is always grounded.
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | drivers/usb/chipidea/bits.h | 18 | ||||
| -rw-r--r-- | drivers/usb/chipidea/ci.h | 64 | ||||
| -rw-r--r-- | drivers/usb/chipidea/ci13xxx_msm.c | 4 | ||||
| -rw-r--r-- | drivers/usb/chipidea/ci13xxx_pci.c | 4 | ||||
| -rw-r--r-- | drivers/usb/chipidea/core.c | 250 | ||||
| -rw-r--r-- | drivers/usb/chipidea/udc.c | 80 | ||||
| -rw-r--r-- | drivers/usb/chipidea/udc.h | 20 | 
7 files changed, 325 insertions, 115 deletions
diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h index 5fbff11cf22..62c35af1a5a 100644 --- a/drivers/usb/chipidea/bits.h +++ b/drivers/usb/chipidea/bits.h @@ -50,6 +50,24 @@  #define DEVLC_PSPD            (0x03UL << 25)  #define    DEVLC_PSPD_HS      (0x02UL << 25) +/* OTGSC */ +#define OTGSC_IDPU	      BIT(5) +#define OTGSC_ID	      BIT(8) +#define OTGSC_AVV	      BIT(9) +#define OTGSC_ASV	      BIT(10) +#define OTGSC_BSV	      BIT(11) +#define OTGSC_BSE	      BIT(12) +#define OTGSC_IDIS	      BIT(16) +#define OTGSC_AVVIS	      BIT(17) +#define OTGSC_ASVIS	      BIT(18) +#define OTGSC_BSVIS	      BIT(19) +#define OTGSC_BSEIS	      BIT(20) +#define OTGSC_IDIE	      BIT(24) +#define OTGSC_AVVIE	      BIT(25) +#define OTGSC_ASVIE	      BIT(26) +#define OTGSC_BSVIE	      BIT(27) +#define OTGSC_BSEIE	      BIT(28) +  /* USBMODE */  #define USBMODE_CM            (0x03UL <<  0)  #define    USBMODE_CM_IDLE    (0x00UL <<  0) diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index f5b3b8538a3..56cb73b1e90 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -14,6 +14,7 @@  #define __DRIVERS_USB_CHIPIDEA_CI_H  #include <linux/list.h> +#include <linux/irqreturn.h>  #include <linux/usb/gadget.h>  /****************************************************************************** @@ -47,6 +48,26 @@ struct ci13xxx_ep {  	struct dma_pool                       *td_pool;  }; +enum ci_role { +	CI_ROLE_HOST = 0, +	CI_ROLE_GADGET, +	CI_ROLE_END, +}; + +/** + * struct ci_role_driver - host/gadget role driver + * start: start this role + * stop: stop this role + * irq: irq handler for this role + * name: role name string (host/gadget) + */ +struct ci_role_driver { +	int		(*start)(struct ci13xxx *); +	void		(*stop)(struct ci13xxx *); +	irqreturn_t	(*irq)(struct ci13xxx *); +	const char	*name; +}; +  struct hw_bank {  	unsigned      lpm;    /* is LPM? */  	void __iomem *abs;    /* bus map offset */ @@ -85,8 +106,47 @@ struct ci13xxx {  	struct ci13xxx_udc_driver *udc_driver; /* device controller driver */  	int                        vbus_active; /* is VBUS active */  	struct usb_phy            *transceiver; /* Transceiver struct */ +	struct ci_role_driver     *roles[CI_ROLE_END]; +	enum ci_role               role; +	bool			   is_otg; +	struct work_struct	   work; +	struct workqueue_struct	  *wq;  }; +static inline struct ci_role_driver *ci_role(struct ci13xxx *ci) +{ +	BUG_ON(ci->role >= CI_ROLE_END || !ci->roles[ci->role]); +	return ci->roles[ci->role]; +} + +static inline int ci_role_start(struct ci13xxx *ci, enum ci_role role) +{ +	int ret; + +	if (role >= CI_ROLE_END) +		return -EINVAL; + +	if (!ci->roles[role]) +		return -ENXIO; + +	ret = ci->roles[role]->start(ci); +	if (!ret) +		ci->role = role; +	return ret; +} + +static inline void ci_role_stop(struct ci13xxx *ci) +{ +	enum ci_role role = ci->role; + +	if (role == CI_ROLE_END) +		return; + +	ci->role = CI_ROLE_END; + +	ci->roles[role]->stop(ci); +} +  /******************************************************************************   * REGISTERS   *****************************************************************************/ @@ -107,6 +167,7 @@ enum ci13xxx_regs {  	OP_ENDPTLISTADDR,  	OP_PORTSC,  	OP_DEVLC, +	OP_OTGSC,  	OP_USBMODE,  	OP_ENDPTSETUPSTAT,  	OP_ENDPTPRIME, @@ -118,7 +179,6 @@ enum ci13xxx_regs {  	OP_LAST = OP_ENDPTCTRL + ENDPT_MAX / 2,  }; -  /**   * ffs_nr: find first (least significant) bit set   * @x: the word to search @@ -193,8 +253,6 @@ static inline u32 hw_test_and_write(struct ci13xxx *udc, enum ci13xxx_regs reg,  	return (val & mask) >> ffs_nr(mask);  } -int hw_device_init(struct ci13xxx *udc, void __iomem *base, -		   uintptr_t cap_offset);  int hw_device_reset(struct ci13xxx *ci);  int hw_port_test_set(struct ci13xxx *ci, u8 mode); diff --git a/drivers/usb/chipidea/ci13xxx_msm.c b/drivers/usb/chipidea/ci13xxx_msm.c index 27427931b68..9b09f0cd3d5 100644 --- a/drivers/usb/chipidea/ci13xxx_msm.c +++ b/drivers/usb/chipidea/ci13xxx_msm.c @@ -62,9 +62,9 @@ static int ci13xxx_msm_probe(struct platform_device *pdev)  	dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n"); -	plat_ci = platform_device_alloc("ci_udc", -1); +	plat_ci = platform_device_alloc("ci_hdrc", -1);  	if (!plat_ci) { -		dev_err(&pdev->dev, "can't allocate ci_udc platform device\n"); +		dev_err(&pdev->dev, "can't allocate ci_hdrc platform device\n");  		return -ENOMEM;  	} diff --git a/drivers/usb/chipidea/ci13xxx_pci.c b/drivers/usb/chipidea/ci13xxx_pci.c index 84e8ab8d4f4..f190140cf61 100644 --- a/drivers/usb/chipidea/ci13xxx_pci.c +++ b/drivers/usb/chipidea/ci13xxx_pci.c @@ -69,9 +69,9 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,  	pci_set_master(pdev);  	pci_try_set_mwi(pdev); -	plat_ci = platform_device_alloc("ci_udc", -1); +	plat_ci = platform_device_alloc("ci_hdrc", -1);  	if (!plat_ci) { -		dev_err(&pdev->dev, "can't allocate ci_udc platform device\n"); +		dev_err(&pdev->dev, "can't allocate ci_hdrc platform device\n");  		retval = -ENOMEM;  		goto disable_device;  	} diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index f6eab327ffe..2342f35c807 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -75,7 +75,7 @@  /* MSM specific */  #define ABS_AHBBURST        (0x0090UL)  #define ABS_AHBMODE         (0x0098UL) -/* UDC register map */ +/* Controller register map */  static uintptr_t ci_regs_nolpm[] = {  	[CAP_CAPLENGTH]		= 0x000UL,  	[CAP_HCCPARAMS]		= 0x008UL, @@ -88,6 +88,7 @@ static uintptr_t ci_regs_nolpm[] = {  	[OP_ENDPTLISTADDR]	= 0x018UL,  	[OP_PORTSC]		= 0x044UL,  	[OP_DEVLC]		= 0x084UL, +	[OP_OTGSC]		= 0x064UL,  	[OP_USBMODE]		= 0x068UL,  	[OP_ENDPTSETUPSTAT]	= 0x06CUL,  	[OP_ENDPTPRIME]		= 0x070UL, @@ -109,6 +110,7 @@ static uintptr_t ci_regs_lpm[] = {  	[OP_ENDPTLISTADDR]	= 0x018UL,  	[OP_PORTSC]		= 0x044UL,  	[OP_DEVLC]		= 0x084UL, +	[OP_OTGSC]		= 0x0C4UL,  	[OP_USBMODE]		= 0x0C8UL,  	[OP_ENDPTSETUPSTAT]	= 0x0D8UL,  	[OP_ENDPTPRIME]		= 0x0DCUL, @@ -118,24 +120,24 @@ static uintptr_t ci_regs_lpm[] = {  	[OP_ENDPTCTRL]		= 0x0ECUL,  }; -static int hw_alloc_regmap(struct ci13xxx *udc, bool is_lpm) +static int hw_alloc_regmap(struct ci13xxx *ci, bool is_lpm)  {  	int i; -	kfree(udc->hw_bank.regmap); +	kfree(ci->hw_bank.regmap); -	udc->hw_bank.regmap = kzalloc((OP_LAST + 1) * sizeof(void *), -				      GFP_KERNEL); -	if (!udc->hw_bank.regmap) +	ci->hw_bank.regmap = kzalloc((OP_LAST + 1) * sizeof(void *), +				     GFP_KERNEL); +	if (!ci->hw_bank.regmap)  		return -ENOMEM;  	for (i = 0; i < OP_ENDPTCTRL; i++) -		udc->hw_bank.regmap[i] = -			(i <= CAP_LAST ? udc->hw_bank.cap : udc->hw_bank.op) + +		ci->hw_bank.regmap[i] = +			(i <= CAP_LAST ? ci->hw_bank.cap : ci->hw_bank.op) +  			(is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]);  	for (; i <= OP_LAST; i++) -		udc->hw_bank.regmap[i] = udc->hw_bank.op + +		ci->hw_bank.regmap[i] = ci->hw_bank.op +  			4 * (i - OP_ENDPTCTRL) +  			(is_lpm  			 ? ci_regs_lpm[OP_ENDPTCTRL] @@ -171,36 +173,35 @@ u8 hw_port_test_get(struct ci13xxx *ci)  	return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> ffs_nr(PORTSC_PTC);  } -int hw_device_init(struct ci13xxx *udc, void __iomem *base, -		   uintptr_t cap_offset) +static int hw_device_init(struct ci13xxx *ci, void __iomem *base)  {  	u32 reg;  	/* bank is a module variable */ -	udc->hw_bank.abs = base; +	ci->hw_bank.abs = base; -	udc->hw_bank.cap = udc->hw_bank.abs; -	udc->hw_bank.cap += cap_offset; -	udc->hw_bank.op = udc->hw_bank.cap + ioread8(udc->hw_bank.cap); +	ci->hw_bank.cap = ci->hw_bank.abs; +	ci->hw_bank.cap += ci->udc_driver->capoffset; +	ci->hw_bank.op = ci->hw_bank.cap + ioread8(ci->hw_bank.cap); -	hw_alloc_regmap(udc, false); -	reg = hw_read(udc, CAP_HCCPARAMS, HCCPARAMS_LEN) >> +	hw_alloc_regmap(ci, false); +	reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >>  		ffs_nr(HCCPARAMS_LEN); -	udc->hw_bank.lpm  = reg; -	hw_alloc_regmap(udc, !!reg); -	udc->hw_bank.size = udc->hw_bank.op - udc->hw_bank.abs; -	udc->hw_bank.size += OP_LAST; -	udc->hw_bank.size /= sizeof(u32); +	ci->hw_bank.lpm  = reg; +	hw_alloc_regmap(ci, !!reg); +	ci->hw_bank.size = ci->hw_bank.op - ci->hw_bank.abs; +	ci->hw_bank.size += OP_LAST; +	ci->hw_bank.size /= sizeof(u32); -	reg = hw_read(udc, CAP_DCCPARAMS, DCCPARAMS_DEN) >> +	reg = hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DEN) >>  		ffs_nr(DCCPARAMS_DEN); -	udc->hw_ep_max = reg * 2;   /* cache hw ENDPT_MAX */ +	ci->hw_ep_max = reg * 2;   /* cache hw ENDPT_MAX */ -	if (udc->hw_ep_max == 0 || udc->hw_ep_max > ENDPT_MAX) +	if (ci->hw_ep_max == 0 || ci->hw_ep_max > ENDPT_MAX)  		return -ENODEV; -	dev_dbg(udc->dev, "ChipIdea UDC found, lpm: %d; cap: %p op: %p\n", -		udc->hw_bank.lpm, udc->hw_bank.cap, udc->hw_bank.op); +	dev_dbg(ci->dev, "ChipIdea HDRC found, lpm: %d; cap: %p op: %p\n", +		ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op);  	/* setup lock mode ? */ @@ -250,16 +251,98 @@ int hw_device_reset(struct ci13xxx *ci)  	return 0;  } -static int __devinit ci_udc_probe(struct platform_device *pdev) +/** + * ci_otg_role - pick role based on ID pin state + * @ci: the controller + */ +static enum ci_role ci_otg_role(struct ci13xxx *ci) +{ +	u32 sts = hw_read(ci, OP_OTGSC, ~0); +	enum ci_role role = sts & OTGSC_ID +		? CI_ROLE_GADGET +		: CI_ROLE_HOST; + +	return role; +} + +/** + * ci_role_work - perform role changing based on ID pin + * @work: work struct + */ +static void ci_role_work(struct work_struct *work) +{ +	struct ci13xxx *ci = container_of(work, struct ci13xxx, work); +	enum ci_role role = ci_otg_role(ci); + +	hw_write(ci, OP_OTGSC, OTGSC_IDIS, OTGSC_IDIS); + +	if (role != ci->role) { +		dev_dbg(ci->dev, "switching from %s to %s\n", +			ci_role(ci)->name, ci->roles[role]->name); + +		ci_role_stop(ci); +		ci_role_start(ci, role); +	} +} + +static ssize_t show_role(struct device *dev, struct device_attribute *attr, +			 char *buf) +{ +	struct ci13xxx *ci = dev_get_drvdata(dev); + +	return sprintf(buf, "%s\n", ci_role(ci)->name); +} + +static ssize_t store_role(struct device *dev, struct device_attribute *attr, +			  const char *buf, size_t count) +{ +	struct ci13xxx *ci = dev_get_drvdata(dev); +	enum ci_role role; +	int ret; + +	for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++) +		if (ci->roles[role] && !strcmp(buf, ci->roles[role]->name)) +			break; + +	if (role == CI_ROLE_END || role == ci->role) +		return -EINVAL; + +	ci_role_stop(ci); +	ret = ci_role_start(ci, role); +	if (ret) +		return ret; + +	return count; +} + +static DEVICE_ATTR(role, S_IRUSR | S_IWUSR, show_role, store_role); + +static irqreturn_t ci_irq(int irq, void *data) +{ +	struct ci13xxx *ci = data; +	irqreturn_t ret = IRQ_NONE; + +	if (ci->is_otg) { +		u32 sts = hw_read(ci, OP_OTGSC, ~0); + +		if (sts & OTGSC_IDIS) { +			queue_work(ci->wq, &ci->work); +			ret = IRQ_HANDLED; +		} +	} + +	return ci->role == CI_ROLE_END ? ret : ci_role(ci)->irq(ci); +} + +static int __devinit ci_hdrc_probe(struct platform_device *pdev)  {  	struct device	*dev = &pdev->dev; -	struct ci13xxx_udc_driver *driver = dev->platform_data; -	struct ci13xxx	*udc; +	struct ci13xxx	*ci;  	struct resource	*res;  	void __iomem	*base;  	int		ret; -	if (!driver) { +	if (!dev->platform_data) {  		dev_err(dev, "platform data missing\n");  		return -ENODEV;  	} @@ -276,49 +359,112 @@ static int __devinit ci_udc_probe(struct platform_device *pdev)  		return -ENOMEM;  	} -	ret = udc_probe(driver, dev, base, &udc); -	if (ret) -		return ret; +	ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL); +	if (!ci) { +		dev_err(dev, "can't allocate device\n"); +		return -ENOMEM; +	} + +	ci->dev = dev; +	ci->udc_driver = dev->platform_data; + +	ret = hw_device_init(ci, base); +	if (ret < 0) { +		dev_err(dev, "can't initialize hardware\n"); +		return -ENODEV; +	} -	udc->irq = platform_get_irq(pdev, 0); -	if (udc->irq < 0) { +	ci->irq = platform_get_irq(pdev, 0); +	if (ci->irq < 0) {  		dev_err(dev, "missing IRQ\n"); +		return -ENODEV; +	} + +	INIT_WORK(&ci->work, ci_role_work); +	ci->wq = create_singlethread_workqueue("ci_otg"); +	if (!ci->wq) { +		dev_err(dev, "can't create workqueue\n"); +		return -ENODEV; +	} + +	/* initialize role(s) before the interrupt is requested */ +	ret = ci_hdrc_gadget_init(ci); +	if (ret) +		dev_info(dev, "doesn't support gadget\n"); + +	if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) { +		dev_err(dev, "no supported roles\n"); +		ret = -ENODEV; +		goto rm_wq; +	} + +	if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { +		ci->is_otg = true; +		ci->role = ci_otg_role(ci); +	} else { +		ci->role = ci->roles[CI_ROLE_HOST] +			? CI_ROLE_HOST +			: CI_ROLE_GADGET; +	} + +	ret = ci_role_start(ci, ci->role); +	if (ret) { +		dev_err(dev, "can't start %s role\n", ci_role(ci)->name);  		ret = -ENODEV; -		goto out; +		goto rm_wq;  	} -	platform_set_drvdata(pdev, udc); -	ret = request_irq(udc->irq, udc_irq, IRQF_SHARED, driver->name, udc); +	platform_set_drvdata(pdev, ci); +	ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->udc_driver->name, +			  ci); +	if (ret) +		goto stop; -out: +	ret = device_create_file(dev, &dev_attr_role);  	if (ret) -		udc_remove(udc); +		goto rm_attr; + +	if (ci->is_otg) +		hw_write(ci, OP_OTGSC, OTGSC_IDIE, OTGSC_IDIE); + +	return ret; + +rm_attr: +	device_remove_file(dev, &dev_attr_role); +stop: +	ci_role_stop(ci); +rm_wq: +	flush_workqueue(ci->wq); +	destroy_workqueue(ci->wq);  	return ret;  } -static int __devexit ci_udc_remove(struct platform_device *pdev) +static int __devexit ci_hdrc_remove(struct platform_device *pdev)  { -	struct ci13xxx *udc = platform_get_drvdata(pdev); +	struct ci13xxx *ci = platform_get_drvdata(pdev); -	free_irq(udc->irq, udc); -	udc_remove(udc); +	flush_workqueue(ci->wq); +	destroy_workqueue(ci->wq); +	device_remove_file(ci->dev, &dev_attr_role); +	free_irq(ci->irq, ci); +	ci_role_stop(ci);  	return 0;  } -static struct platform_driver ci_udc_driver = { -	.probe	= ci_udc_probe, -	.remove	= __devexit_p(ci_udc_remove), +static struct platform_driver ci_hdrc_driver = { +	.probe	= ci_hdrc_probe, +	.remove	= __devexit_p(ci_hdrc_remove),  	.driver	= { -		.name	= "ci_udc", +		.name	= "ci_hdrc",  	},  }; -module_platform_driver(ci_udc_driver); +module_platform_driver(ci_hdrc_driver); -MODULE_ALIAS("platform:ci_udc"); +MODULE_ALIAS("platform:ci_hdrc");  MODULE_ALIAS("platform:ci13xxx");  MODULE_LICENSE("GPL v2");  MODULE_AUTHOR("David Lopo <dlopo@chipidea.mips.com>"); -MODULE_DESCRIPTION("ChipIdea UDC Driver"); +MODULE_DESCRIPTION("ChipIdea HDRC Driver"); diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 6866ef08539..9133a59450f 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1592,14 +1592,13 @@ static int ci13xxx_stop(struct usb_gadget *gadget,   * BUS block   *****************************************************************************/  /** - * udc_irq: global interrupt handler + * udc_irq: udc interrupt handler   *   * This function returns IRQ_HANDLED if the IRQ has been handled   * It locks access to registers   */ -irqreturn_t udc_irq(int irq, void *data) +static irqreturn_t udc_irq(struct ci13xxx *udc)  { -	struct ci13xxx *udc = data;  	irqreturn_t retval;  	u32 intr; @@ -1666,38 +1665,24 @@ static void udc_release(struct device *dev)  }  /** - * udc_probe: parent probe must call this to initialize UDC - * @dev:  parent device - * @regs: registers base address - * @name: driver name - * - * This function returns an error code - * No interrupts active, the IRQ has not been requested yet - * Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask + * udc_start: initialize gadget role + * @udc: chipidea controller   */ -int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, -	      void __iomem *regs, struct ci13xxx **_udc) +static int udc_start(struct ci13xxx *udc)  { -	struct ci13xxx *udc; +	struct device *dev = udc->dev;  	int retval = 0; -	if (dev == NULL || regs == NULL || driver == NULL || -			driver->name == NULL) +	if (!udc)  		return -EINVAL; -	udc = kzalloc(sizeof(struct ci13xxx), GFP_KERNEL); -	if (udc == NULL) -		return -ENOMEM; -  	spin_lock_init(&udc->lock); -	udc->regs = regs; -	udc->udc_driver = driver;  	udc->gadget.ops          = &usb_gadget_ops;  	udc->gadget.speed        = USB_SPEED_UNKNOWN;  	udc->gadget.max_speed    = USB_SPEED_HIGH;  	udc->gadget.is_otg       = 0; -	udc->gadget.name         = driver->name; +	udc->gadget.name         = udc->udc_driver->name;  	INIT_LIST_HEAD(&udc->gadget.ep_list); @@ -1707,16 +1692,12 @@ int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,  	udc->gadget.dev.parent   = dev;  	udc->gadget.dev.release  = udc_release; -	udc->dev = dev; -  	/* alloc resources */  	udc->qh_pool = dma_pool_create("ci13xxx_qh", dev,  				       sizeof(struct ci13xxx_qh),  				       64, CI13XXX_PAGE_SIZE); -	if (udc->qh_pool == NULL) { -		retval = -ENOMEM; -		goto free_udc; -	} +	if (udc->qh_pool == NULL) +		return -ENOMEM;  	udc->td_pool = dma_pool_create("ci13xxx_td", dev,  				       sizeof(struct ci13xxx_td), @@ -1726,10 +1707,6 @@ int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,  		goto free_qh_pool;  	} -	retval = hw_device_init(udc, regs, driver->capoffset); -	if (retval < 0) -		goto free_pools; -  	retval = init_eps(udc);  	if (retval)  		goto free_pools; @@ -1775,7 +1752,6 @@ int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,  	pm_runtime_no_callbacks(&udc->gadget.dev);  	pm_runtime_enable(&udc->gadget.dev); -	*_udc = udc;  	return retval;  remove_trans: @@ -1796,9 +1772,6 @@ free_pools:  	dma_pool_destroy(udc->td_pool);  free_qh_pool:  	dma_pool_destroy(udc->qh_pool); -free_udc: -	kfree(udc); -	*_udc = NULL;  	return retval;  } @@ -1807,7 +1780,7 @@ free_udc:   *   * No interrupts active, the IRQ has been released   */ -void udc_remove(struct ci13xxx *udc) +static void udc_stop(struct ci13xxx *udc)  {  	int i; @@ -1826,12 +1799,37 @@ void udc_remove(struct ci13xxx *udc)  	dma_pool_destroy(udc->qh_pool);  	if (udc->transceiver) { -		otg_set_peripheral(udc->transceiver->otg, &udc->gadget); +		otg_set_peripheral(udc->transceiver->otg, NULL);  		usb_put_transceiver(udc->transceiver);  	}  	dbg_remove_files(&udc->gadget.dev);  	device_unregister(&udc->gadget.dev); +	/* my kobject is dynamic, I swear! */ +	memset(&udc->gadget, 0, sizeof(udc->gadget)); +} + +/** + * ci_hdrc_gadget_init - initialize device related bits + * ci: the controller + * + * This function enables the gadget role, if the device is "device capable". + */ +int ci_hdrc_gadget_init(struct ci13xxx *ci) +{ +	struct ci_role_driver *rdrv; + +	if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC)) +		return -ENXIO; + +	rdrv = devm_kzalloc(ci->dev, sizeof(struct ci_role_driver), GFP_KERNEL); +	if (!rdrv) +		return -ENOMEM; + +	rdrv->start	= udc_start; +	rdrv->stop	= udc_stop; +	rdrv->irq	= udc_irq; +	rdrv->name	= "gadget"; +	ci->roles[CI_ROLE_GADGET] = rdrv; -	kfree(udc->hw_bank.regmap); -	kfree(udc); +	return 0;  } diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h index 82c9e3e772f..3a9e6694f32 100644 --- a/drivers/usb/chipidea/udc.h +++ b/drivers/usb/chipidea/udc.h @@ -71,26 +71,16 @@ struct ci13xxx_req {  };  #ifdef CONFIG_USB_CHIPIDEA_UDC -irqreturn_t udc_irq(int irq, void *data); -int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, -	      void __iomem *regs, struct ci13xxx **_udc); -void udc_remove(struct ci13xxx *udc); + +int ci_hdrc_gadget_init(struct ci13xxx *ci); +  #else -static inline irqreturn_t udc_irq(int irq, void *data) -{ -	return IRQ_NONE; -} -static inline -int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev, -	      void __iomem *regs, struct ci13xxx **_udc) +static inline int ci_hdrc_gadget_init(struct ci13xxx *ci)  { -	return -ENODEV; +	return -ENXIO;  } -static inline void udc_remove(struct ci13xxx *udc) -{ -}  #endif  #endif /* __DRIVERS_USB_CHIPIDEA_UDC_H */  |