forked from Minki/linux
USB: pxa2xx_udc understands GPIO based VBUS sensing
This updates the PXA 25x UDC board-independent infrastructure for VBUS sensing and the D+ pullup. The original code evolved from rather bizarre support on Intel's "Lubbock" reference hardware, so that on more sensible hardware it doesn't work as well as it could/should. The change is just to teach the UDC driver how to use built-in PXA GPIO pins directly. This reduces the amount of board-specfic object code needed, and enables the use of a VBUS sensing IRQ on boards (like Gumstix) that have one. With VBUS sensing, the UDC is unclocked until a host is actually connected. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
3a16f7b4a7
commit
b2bbb20b37
@ -284,21 +284,9 @@ static struct pxaficp_platform_data corgi_ficp_platform_data = {
|
|||||||
/*
|
/*
|
||||||
* USB Device Controller
|
* USB Device Controller
|
||||||
*/
|
*/
|
||||||
static void corgi_udc_command(int cmd)
|
|
||||||
{
|
|
||||||
switch(cmd) {
|
|
||||||
case PXA2XX_UDC_CMD_CONNECT:
|
|
||||||
GPSR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP);
|
|
||||||
break;
|
|
||||||
case PXA2XX_UDC_CMD_DISCONNECT:
|
|
||||||
GPCR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct pxa2xx_udc_mach_info udc_info __initdata = {
|
static struct pxa2xx_udc_mach_info udc_info __initdata = {
|
||||||
/* no connect GPIO; corgi can't tell connection status */
|
/* no connect GPIO; corgi can't tell connection status */
|
||||||
.udc_command = corgi_udc_command,
|
.gpio_pullup = CORGI_GPIO_USB_PULLUP,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -350,7 +338,6 @@ static void __init corgi_init(void)
|
|||||||
corgi_ssp_set_machinfo(&corgi_ssp_machinfo);
|
corgi_ssp_set_machinfo(&corgi_ssp_machinfo);
|
||||||
|
|
||||||
pxa_gpio_mode(CORGI_GPIO_IR_ON | GPIO_OUT);
|
pxa_gpio_mode(CORGI_GPIO_IR_ON | GPIO_OUT);
|
||||||
pxa_gpio_mode(CORGI_GPIO_USB_PULLUP | GPIO_OUT);
|
|
||||||
pxa_gpio_mode(CORGI_GPIO_HSYNC | GPIO_IN);
|
pxa_gpio_mode(CORGI_GPIO_HSYNC | GPIO_IN);
|
||||||
|
|
||||||
pxa_set_udc_info(&udc_info);
|
pxa_set_udc_info(&udc_info);
|
||||||
|
@ -150,6 +150,39 @@ MODULE_PARM_DESC (fifo_mode, "pxa2xx udc fifo mode");
|
|||||||
static void pxa2xx_ep_fifo_flush (struct usb_ep *ep);
|
static void pxa2xx_ep_fifo_flush (struct usb_ep *ep);
|
||||||
static void nuke (struct pxa2xx_ep *, int status);
|
static void nuke (struct pxa2xx_ep *, int status);
|
||||||
|
|
||||||
|
/* one GPIO should be used to detect VBUS from the host */
|
||||||
|
static int is_vbus_present(void)
|
||||||
|
{
|
||||||
|
struct pxa2xx_udc_mach_info *mach = the_controller->mach;
|
||||||
|
|
||||||
|
if (mach->gpio_vbus)
|
||||||
|
return pxa_gpio_get(mach->gpio_vbus);
|
||||||
|
if (mach->udc_is_connected)
|
||||||
|
return mach->udc_is_connected();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* one GPIO should control a D+ pullup, so host sees this device (or not) */
|
||||||
|
static void pullup_off(void)
|
||||||
|
{
|
||||||
|
struct pxa2xx_udc_mach_info *mach = the_controller->mach;
|
||||||
|
|
||||||
|
if (mach->gpio_pullup)
|
||||||
|
pxa_gpio_set(mach->gpio_pullup, 0);
|
||||||
|
else if (mach->udc_command)
|
||||||
|
mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pullup_on(void)
|
||||||
|
{
|
||||||
|
struct pxa2xx_udc_mach_info *mach = the_controller->mach;
|
||||||
|
|
||||||
|
if (mach->gpio_pullup)
|
||||||
|
pxa_gpio_set(mach->gpio_pullup, 1);
|
||||||
|
else if (mach->udc_command)
|
||||||
|
mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
|
||||||
|
}
|
||||||
|
|
||||||
static void pio_irq_enable(int bEndpointAddress)
|
static void pio_irq_enable(int bEndpointAddress)
|
||||||
{
|
{
|
||||||
bEndpointAddress &= 0xf;
|
bEndpointAddress &= 0xf;
|
||||||
@ -1721,6 +1754,16 @@ lubbock_vbus_irq(int irq, void *_dev, struct pt_regs *r)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static irqreturn_t
|
||||||
|
udc_vbus_irq(int irq, void *_dev, struct pt_regs *r)
|
||||||
|
{
|
||||||
|
struct pxa2xx_udc *dev = _dev;
|
||||||
|
int vbus = pxa_gpio_get(dev->mach->gpio_vbus);
|
||||||
|
|
||||||
|
pxa2xx_udc_vbus_session(&dev->gadget, vbus);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
@ -2438,7 +2481,7 @@ static struct pxa2xx_udc memory = {
|
|||||||
static int __init pxa2xx_udc_probe(struct platform_device *pdev)
|
static int __init pxa2xx_udc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct pxa2xx_udc *dev = &memory;
|
struct pxa2xx_udc *dev = &memory;
|
||||||
int retval, out_dma = 1;
|
int retval, out_dma = 1, vbus_irq;
|
||||||
u32 chiprev;
|
u32 chiprev;
|
||||||
|
|
||||||
/* insist on Intel/ARM/XScale */
|
/* insist on Intel/ARM/XScale */
|
||||||
@ -2502,6 +2545,16 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
|
|||||||
/* other non-static parts of init */
|
/* other non-static parts of init */
|
||||||
dev->dev = &pdev->dev;
|
dev->dev = &pdev->dev;
|
||||||
dev->mach = pdev->dev.platform_data;
|
dev->mach = pdev->dev.platform_data;
|
||||||
|
if (dev->mach->gpio_vbus) {
|
||||||
|
vbus_irq = IRQ_GPIO(dev->mach->gpio_vbus & GPIO_MD_MASK_NR);
|
||||||
|
pxa_gpio_mode((dev->mach->gpio_vbus & GPIO_MD_MASK_NR)
|
||||||
|
| GPIO_IN);
|
||||||
|
set_irq_type(vbus_irq, IRQT_BOTHEDGE);
|
||||||
|
} else
|
||||||
|
vbus_irq = 0;
|
||||||
|
if (dev->mach->gpio_pullup)
|
||||||
|
pxa_gpio_mode((dev->mach->gpio_pullup & GPIO_MD_MASK_NR)
|
||||||
|
| GPIO_OUT | GPIO_DFLT_LOW);
|
||||||
|
|
||||||
init_timer(&dev->timer);
|
init_timer(&dev->timer);
|
||||||
dev->timer.function = udc_watchdog;
|
dev->timer.function = udc_watchdog;
|
||||||
@ -2557,8 +2610,19 @@ lubbock_fail0:
|
|||||||
HEX_DISPLAY(dev->stats.irqs);
|
HEX_DISPLAY(dev->stats.irqs);
|
||||||
LUB_DISC_BLNK_LED &= 0xff;
|
LUB_DISC_BLNK_LED &= 0xff;
|
||||||
#endif
|
#endif
|
||||||
}
|
} else
|
||||||
#endif
|
#endif
|
||||||
|
if (vbus_irq) {
|
||||||
|
retval = request_irq(vbus_irq, udc_vbus_irq,
|
||||||
|
SA_INTERRUPT | SA_SAMPLE_RANDOM,
|
||||||
|
driver_name, dev);
|
||||||
|
if (retval != 0) {
|
||||||
|
printk(KERN_ERR "%s: can't get irq %i, err %d\n",
|
||||||
|
driver_name, vbus_irq, retval);
|
||||||
|
free_irq(IRQ_USB, dev);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
}
|
||||||
create_proc_files();
|
create_proc_files();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -2587,6 +2651,8 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
|
|||||||
free_irq(LUBBOCK_USB_IRQ, dev);
|
free_irq(LUBBOCK_USB_IRQ, dev);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (dev->mach->gpio_vbus)
|
||||||
|
free_irq(IRQ_GPIO(dev->mach->gpio_vbus), dev);
|
||||||
platform_set_drvdata(pdev, NULL);
|
platform_set_drvdata(pdev, NULL);
|
||||||
the_controller = NULL;
|
the_controller = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -177,27 +177,19 @@ struct pxa2xx_udc {
|
|||||||
|
|
||||||
static struct pxa2xx_udc *the_controller;
|
static struct pxa2xx_udc *the_controller;
|
||||||
|
|
||||||
/* one GPIO should be used to detect VBUS from the host */
|
static inline int pxa_gpio_get(unsigned gpio)
|
||||||
static inline int is_vbus_present(void)
|
|
||||||
{
|
{
|
||||||
if (!the_controller->mach->udc_is_connected)
|
return (GPLR(gpio) & GPIO_bit(gpio)) != 0;
|
||||||
return 1;
|
|
||||||
return the_controller->mach->udc_is_connected();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* one GPIO should control a D+ pullup, so host sees this device (or not) */
|
static inline void pxa_gpio_set(unsigned gpio, int is_on)
|
||||||
static inline void pullup_off(void)
|
|
||||||
{
|
{
|
||||||
if (!the_controller->mach->udc_command)
|
int mask = GPIO_bit(gpio);
|
||||||
return;
|
|
||||||
the_controller->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void pullup_on(void)
|
if (is_on)
|
||||||
{
|
GPSR(gpio) = mask;
|
||||||
if (!the_controller->mach->udc_command)
|
else
|
||||||
return;
|
GPCR(gpio) = mask;
|
||||||
the_controller->mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
@ -12,6 +12,14 @@ struct pxa2xx_udc_mach_info {
|
|||||||
void (*udc_command)(int cmd);
|
void (*udc_command)(int cmd);
|
||||||
#define PXA2XX_UDC_CMD_CONNECT 0 /* let host see us */
|
#define PXA2XX_UDC_CMD_CONNECT 0 /* let host see us */
|
||||||
#define PXA2XX_UDC_CMD_DISCONNECT 1 /* so host won't see us */
|
#define PXA2XX_UDC_CMD_DISCONNECT 1 /* so host won't see us */
|
||||||
|
|
||||||
|
/* Boards following the design guidelines in the developer's manual,
|
||||||
|
* with on-chip GPIOs not Lubbock's wierd hardware, can have a sane
|
||||||
|
* VBUS IRQ and omit the methods above. Store the GPIO number
|
||||||
|
* here; for GPIO 0, also mask in one of the pxa_gpio_mode() bits.
|
||||||
|
*/
|
||||||
|
u16 gpio_vbus; /* high == vbus present */
|
||||||
|
u16 gpio_pullup; /* high == pullup activated */
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void pxa_set_udc_info(struct pxa2xx_udc_mach_info *info);
|
extern void pxa_set_udc_info(struct pxa2xx_udc_mach_info *info);
|
||||||
|
Loading…
Reference in New Issue
Block a user