[PATCH] USB: isp116x-hcd: cleanup
The attached patch makes a cleanup of isp116x-hcd. Most of the volume of the patch comes from 2 sources: moving the code around to get rid of a few function prototypes and reworking register dumping functions/macros. Among other things, switched over from using procfs to debugfs. Cleanup. The following changes were made: - Rework register dumping code so it can be used for dumping to both syslog and debugfs. - Switch from procfs to debugfs.. - Die gracefully on Unrecoverable Error interrupt. - Fix memory leak in isp116x_urb_enqueue(), if HC happens to die in a narrow time window. - Fix a 'sparce' warning (unnecessary cast). - Report Devices Removable for root hub ports by default (was Devices Permanently Attached). - Move bus suspend/resume functions down in code to get rid of a few function prototypes. - A number of one-line cleanups. - Add an entry to MAINTAINERS. Signed-off-by: Olav Kongas <ok@artecdesign.ee> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> MAINTAINERS | 6 drivers/usb/host/isp116x-hcd.c | 429 ++++++++++++++++------------------------- drivers/usb/host/isp116x.h | 83 +++++-- 3 files changed, 230 insertions(+), 288 deletions(-)
This commit is contained in:
parent
535488fcf1
commit
959eea2191
@ -2640,6 +2640,12 @@ L: linux-usb-users@lists.sourceforge.net
|
||||
L: linux-usb-devel@lists.sourceforge.net
|
||||
S: Maintained
|
||||
|
||||
USB ISP116X DRIVER
|
||||
P: Olav Kongas
|
||||
M: ok@artecdesign.ee
|
||||
L: linux-usb-devel@lists.sourceforge.net
|
||||
S: Maintained
|
||||
|
||||
USB KAWASAKI LSI DRIVER
|
||||
P: Oliver Neukum
|
||||
M: oliver@neukum.name
|
||||
|
@ -55,19 +55,13 @@
|
||||
/* enqueuing/finishing log of urbs */
|
||||
//#define URB_TRACE
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb_isp116x.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -77,14 +71,10 @@
|
||||
#include <asm/system.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#ifndef DEBUG
|
||||
# define STUB_DEBUG_FILE
|
||||
#endif
|
||||
|
||||
#include "../core/hcd.h"
|
||||
#include "isp116x.h"
|
||||
|
||||
#define DRIVER_VERSION "05 Aug 2005"
|
||||
#define DRIVER_VERSION "03 Nov 2005"
|
||||
#define DRIVER_DESC "ISP116x USB Host Controller Driver"
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
@ -305,9 +295,8 @@ static void postproc_atl_queue(struct isp116x *isp116x)
|
||||
udev = urb->dev;
|
||||
ptd = &ep->ptd;
|
||||
cc = PTD_GET_CC(ptd);
|
||||
|
||||
spin_lock(&urb->lock);
|
||||
short_not_ok = 1;
|
||||
spin_lock(&urb->lock);
|
||||
|
||||
/* Data underrun is special. For allowed underrun
|
||||
we clear the error and continue as normal. For
|
||||
@ -420,7 +409,7 @@ static void postproc_atl_queue(struct isp116x *isp116x)
|
||||
ep->nextpid = 0;
|
||||
break;
|
||||
default:
|
||||
BUG_ON(1);
|
||||
BUG();
|
||||
}
|
||||
spin_unlock(&urb->lock);
|
||||
}
|
||||
@ -628,8 +617,12 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs)
|
||||
u32 intstat = isp116x_read_reg32(isp116x, HCINTSTAT);
|
||||
isp116x_write_reg32(isp116x, HCINTSTAT, intstat);
|
||||
if (intstat & HCINT_UE) {
|
||||
ERR("Unrecoverable error\n");
|
||||
/* What should we do here? Reset? */
|
||||
ERR("Unrecoverable error, HC is dead!\n");
|
||||
/* IRQ's are off, we do no DMA,
|
||||
perfectly ready to die ... */
|
||||
hcd->state = HC_STATE_HALT;
|
||||
ret = IRQ_HANDLED;
|
||||
goto done;
|
||||
}
|
||||
if (intstat & HCINT_RHSC)
|
||||
/* When root hub or any of its ports is going
|
||||
@ -640,7 +633,6 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs)
|
||||
if (intstat & HCINT_RD) {
|
||||
DBG("---- remote wakeup\n");
|
||||
usb_hcd_resume_root_hub(hcd);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
irqstat &= ~HCuPINT_OPR;
|
||||
ret = IRQ_HANDLED;
|
||||
@ -651,6 +643,7 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs)
|
||||
}
|
||||
|
||||
isp116x_write_reg16(isp116x, HCuPINTENB, isp116x->irqenb);
|
||||
done:
|
||||
spin_unlock(&isp116x->lock);
|
||||
return ret;
|
||||
}
|
||||
@ -724,6 +717,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd,
|
||||
|
||||
spin_lock_irqsave(&isp116x->lock, flags);
|
||||
if (!HC_IS_RUNNING(hcd->state)) {
|
||||
kfree(ep);
|
||||
ret = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
@ -888,7 +882,7 @@ static void isp116x_endpoint_disable(struct usb_hcd *hcd,
|
||||
struct usb_host_endpoint *hep)
|
||||
{
|
||||
int i;
|
||||
struct isp116x_ep *ep = hep->hcpriv;;
|
||||
struct isp116x_ep *ep = hep->hcpriv;
|
||||
|
||||
if (!ep)
|
||||
return;
|
||||
@ -916,8 +910,6 @@ static int isp116x_get_frame(struct usb_hcd *hcd)
|
||||
return (int)fmnum;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
Adapted from ohci-hub.c. Currently we don't support autosuspend.
|
||||
*/
|
||||
@ -968,11 +960,10 @@ static void isp116x_hub_descriptor(struct isp116x *isp116x,
|
||||
desc->bHubContrCurrent = 0;
|
||||
desc->bNbrPorts = (u8) (reg & 0x3);
|
||||
/* Power switching, device type, overcurrent. */
|
||||
desc->wHubCharacteristics =
|
||||
(__force __u16) cpu_to_le16((u16) ((reg >> 8) & 0x1f));
|
||||
desc->wHubCharacteristics = cpu_to_le16((u16) ((reg >> 8) & 0x1f));
|
||||
desc->bPwrOn2PwrGood = (u8) ((reg >> 24) & 0xff);
|
||||
/* two bitmaps: ports removable, and legacy PortPwrCtrlMask */
|
||||
desc->bitmap[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1;
|
||||
desc->bitmap[0] = 0;
|
||||
desc->bitmap[1] = ~0;
|
||||
}
|
||||
|
||||
@ -1159,145 +1150,9 @@ static int isp116x_hub_control(struct usb_hcd *hcd,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int isp116x_bus_suspend(struct usb_hcd *hcd)
|
||||
{
|
||||
struct isp116x *isp116x = hcd_to_isp116x(hcd);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&isp116x->lock, flags);
|
||||
|
||||
val = isp116x_read_reg32(isp116x, HCCONTROL);
|
||||
switch (val & HCCONTROL_HCFS) {
|
||||
case HCCONTROL_USB_OPER:
|
||||
hcd->state = HC_STATE_QUIESCING;
|
||||
val &= (~HCCONTROL_HCFS & ~HCCONTROL_RWE);
|
||||
val |= HCCONTROL_USB_SUSPEND;
|
||||
if (hcd->remote_wakeup)
|
||||
val |= HCCONTROL_RWE;
|
||||
/* Wait for usb transfers to finish */
|
||||
mdelay(2);
|
||||
isp116x_write_reg32(isp116x, HCCONTROL, val);
|
||||
hcd->state = HC_STATE_SUSPENDED;
|
||||
/* Wait for devices to suspend */
|
||||
mdelay(5);
|
||||
case HCCONTROL_USB_SUSPEND:
|
||||
break;
|
||||
case HCCONTROL_USB_RESUME:
|
||||
isp116x_write_reg32(isp116x, HCCONTROL,
|
||||
(val & ~HCCONTROL_HCFS) |
|
||||
HCCONTROL_USB_RESET);
|
||||
case HCCONTROL_USB_RESET:
|
||||
ret = -EBUSY;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&isp116x->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get rid of these declarations later in cleanup */
|
||||
static int isp116x_reset(struct usb_hcd *hcd);
|
||||
static int isp116x_start(struct usb_hcd *hcd);
|
||||
|
||||
static int isp116x_bus_resume(struct usb_hcd *hcd)
|
||||
{
|
||||
struct isp116x *isp116x = hcd_to_isp116x(hcd);
|
||||
u32 val;
|
||||
|
||||
msleep(5);
|
||||
spin_lock_irq(&isp116x->lock);
|
||||
|
||||
val = isp116x_read_reg32(isp116x, HCCONTROL);
|
||||
switch (val & HCCONTROL_HCFS) {
|
||||
case HCCONTROL_USB_SUSPEND:
|
||||
val &= ~HCCONTROL_HCFS;
|
||||
val |= HCCONTROL_USB_RESUME;
|
||||
isp116x_write_reg32(isp116x, HCCONTROL, val);
|
||||
case HCCONTROL_USB_RESUME:
|
||||
break;
|
||||
case HCCONTROL_USB_OPER:
|
||||
spin_unlock_irq(&isp116x->lock);
|
||||
/* Without setting power_state here the
|
||||
SUSPENDED state won't be removed from
|
||||
sysfs/usbN/power.state as a response to remote
|
||||
wakeup. Maybe in the future. */
|
||||
hcd->self.root_hub->dev.power.power_state = PMSG_ON;
|
||||
return 0;
|
||||
default:
|
||||
/* HCCONTROL_USB_RESET: this may happen, when during
|
||||
suspension the HC lost power. Reinitialize completely */
|
||||
spin_unlock_irq(&isp116x->lock);
|
||||
DBG("Chip has been reset while suspended. Reinit from scratch.\n");
|
||||
isp116x_reset(hcd);
|
||||
isp116x_start(hcd);
|
||||
isp116x_hub_control(hcd, SetPortFeature,
|
||||
USB_PORT_FEAT_POWER, 1, NULL, 0);
|
||||
if ((isp116x->rhdesca & RH_A_NDP) == 2)
|
||||
isp116x_hub_control(hcd, SetPortFeature,
|
||||
USB_PORT_FEAT_POWER, 2, NULL, 0);
|
||||
hcd->self.root_hub->dev.power.power_state = PMSG_ON;
|
||||
return 0;
|
||||
}
|
||||
|
||||
val = isp116x->rhdesca & RH_A_NDP;
|
||||
while (val--) {
|
||||
u32 stat =
|
||||
isp116x_read_reg32(isp116x, val ? HCRHPORT2 : HCRHPORT1);
|
||||
/* force global, not selective, resume */
|
||||
if (!(stat & RH_PS_PSS))
|
||||
continue;
|
||||
DBG("%s: Resuming port %d\n", __func__, val);
|
||||
isp116x_write_reg32(isp116x, RH_PS_POCI, val
|
||||
? HCRHPORT2 : HCRHPORT1);
|
||||
}
|
||||
spin_unlock_irq(&isp116x->lock);
|
||||
|
||||
hcd->state = HC_STATE_RESUMING;
|
||||
mdelay(20);
|
||||
|
||||
/* Go operational */
|
||||
spin_lock_irq(&isp116x->lock);
|
||||
val = isp116x_read_reg32(isp116x, HCCONTROL);
|
||||
isp116x_write_reg32(isp116x, HCCONTROL,
|
||||
(val & ~HCCONTROL_HCFS) | HCCONTROL_USB_OPER);
|
||||
spin_unlock_irq(&isp116x->lock);
|
||||
/* see analogous comment above */
|
||||
hcd->self.root_hub->dev.power.power_state = PMSG_ON;
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
#define isp116x_bus_suspend NULL
|
||||
#define isp116x_bus_resume NULL
|
||||
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------*/
|
||||
|
||||
#ifdef STUB_DEBUG_FILE
|
||||
|
||||
static inline void create_debug_file(struct isp116x *isp116x)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void remove_debug_file(struct isp116x *isp116x)
|
||||
{
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
static void dump_irq(struct seq_file *s, char *label, u16 mask)
|
||||
{
|
||||
@ -1321,13 +1176,9 @@ static void dump_int(struct seq_file *s, char *label, u32 mask)
|
||||
mask & HCINT_SF ? " sof" : "", mask & HCINT_SO ? " so" : "");
|
||||
}
|
||||
|
||||
static int proc_isp116x_show(struct seq_file *s, void *unused)
|
||||
static int isp116x_show_dbg(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct isp116x *isp116x = s->private;
|
||||
struct isp116x_ep *ep;
|
||||
struct urb *urb;
|
||||
unsigned i;
|
||||
char *str;
|
||||
|
||||
seq_printf(s, "%s\n%s version %s\n",
|
||||
isp116x_to_hcd(isp116x)->product_desc, hcd_name,
|
||||
@ -1343,105 +1194,50 @@ static int proc_isp116x_show(struct seq_file *s, void *unused)
|
||||
}
|
||||
|
||||
spin_lock_irq(&isp116x->lock);
|
||||
|
||||
dump_irq(s, "hc_irq_enable", isp116x_read_reg16(isp116x, HCuPINTENB));
|
||||
dump_irq(s, "hc_irq_status", isp116x_read_reg16(isp116x, HCuPINT));
|
||||
dump_int(s, "hc_int_enable", isp116x_read_reg32(isp116x, HCINTENB));
|
||||
dump_int(s, "hc_int_status", isp116x_read_reg32(isp116x, HCINTSTAT));
|
||||
|
||||
list_for_each_entry(ep, &isp116x->async, schedule) {
|
||||
|
||||
switch (ep->nextpid) {
|
||||
case USB_PID_IN:
|
||||
str = "in";
|
||||
break;
|
||||
case USB_PID_OUT:
|
||||
str = "out";
|
||||
break;
|
||||
case USB_PID_SETUP:
|
||||
str = "setup";
|
||||
break;
|
||||
case USB_PID_ACK:
|
||||
str = "status";
|
||||
break;
|
||||
default:
|
||||
str = "?";
|
||||
break;
|
||||
};
|
||||
seq_printf(s, "%p, ep%d%s, maxpacket %d:\n", ep,
|
||||
ep->epnum, str, ep->maxpacket);
|
||||
list_for_each_entry(urb, &ep->hep->urb_list, urb_list) {
|
||||
seq_printf(s, " urb%p, %d/%d\n", urb,
|
||||
urb->actual_length,
|
||||
urb->transfer_buffer_length);
|
||||
}
|
||||
}
|
||||
if (!list_empty(&isp116x->async))
|
||||
seq_printf(s, "\n");
|
||||
|
||||
seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE);
|
||||
|
||||
for (i = 0; i < PERIODIC_SIZE; i++) {
|
||||
ep = isp116x->periodic[i];
|
||||
if (!ep)
|
||||
continue;
|
||||
seq_printf(s, "%2d [%3d]:\n", i, isp116x->load[i]);
|
||||
|
||||
/* DUMB: prints shared entries multiple times */
|
||||
do {
|
||||
seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n",
|
||||
ep->period, ep,
|
||||
(ep->udev->speed ==
|
||||
USB_SPEED_FULL) ? "" : "ls ",
|
||||
ep->udev->devnum, ep->epnum,
|
||||
(ep->epnum ==
|
||||
0) ? "" : ((ep->nextpid ==
|
||||
USB_PID_IN) ? "in" : "out"),
|
||||
ep->maxpacket);
|
||||
ep = ep->next;
|
||||
} while (ep);
|
||||
}
|
||||
isp116x_show_regs_seq(isp116x, s);
|
||||
spin_unlock_irq(&isp116x->lock);
|
||||
seq_printf(s, "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_isp116x_open(struct inode *inode, struct file *file)
|
||||
static int isp116x_open_seq(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, proc_isp116x_show, PDE(inode)->data);
|
||||
return single_open(file, isp116x_show_dbg, inode->u.generic_ip);
|
||||
}
|
||||
|
||||
static struct file_operations proc_ops = {
|
||||
.open = proc_isp116x_open,
|
||||
static struct file_operations isp116x_debug_fops = {
|
||||
.open = isp116x_open_seq,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/* expect just one isp116x per system */
|
||||
static const char proc_filename[] = "driver/isp116x";
|
||||
|
||||
static void create_debug_file(struct isp116x *isp116x)
|
||||
static int create_debug_file(struct isp116x *isp116x)
|
||||
{
|
||||
struct proc_dir_entry *pde;
|
||||
|
||||
pde = create_proc_entry(proc_filename, 0, NULL);
|
||||
if (pde == NULL)
|
||||
return;
|
||||
|
||||
pde->proc_fops = &proc_ops;
|
||||
pde->data = isp116x;
|
||||
isp116x->pde = pde;
|
||||
isp116x->dentry = debugfs_create_file(hcd_name,
|
||||
S_IRUGO, NULL, isp116x,
|
||||
&isp116x_debug_fops);
|
||||
if (!isp116x->dentry)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void remove_debug_file(struct isp116x *isp116x)
|
||||
{
|
||||
if (isp116x->pde)
|
||||
remove_proc_entry(proc_filename, NULL);
|
||||
debugfs_remove(isp116x->dentry);
|
||||
}
|
||||
|
||||
#endif
|
||||
#else
|
||||
|
||||
#define create_debug_file(d) 0
|
||||
#define remove_debug_file(d) do{}while(0)
|
||||
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
/*-----------------------------------------------------------------*/
|
||||
|
||||
@ -1476,7 +1272,7 @@ static int isp116x_reset(struct usb_hcd *hcd)
|
||||
struct isp116x *isp116x = hcd_to_isp116x(hcd);
|
||||
unsigned long t;
|
||||
u16 clkrdy = 0;
|
||||
int ret = 0, timeout = 15 /* ms */ ;
|
||||
int ret, timeout = 15 /* ms */ ;
|
||||
|
||||
ret = isp116x_sw_reset(isp116x);
|
||||
if (ret)
|
||||
@ -1492,7 +1288,7 @@ static int isp116x_reset(struct usb_hcd *hcd)
|
||||
break;
|
||||
}
|
||||
if (!clkrdy) {
|
||||
ERR("Clock not ready after 20ms\n");
|
||||
ERR("Clock not ready after %dms\n", timeout);
|
||||
/* After sw_reset the clock won't report to be ready, if
|
||||
H_WAKEUP pin is high. */
|
||||
ERR("Please make sure that the H_WAKEUP pin is pulled low!\n");
|
||||
@ -1610,12 +1406,128 @@ static int isp116x_start(struct usb_hcd *hcd)
|
||||
isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS);
|
||||
isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS);
|
||||
|
||||
isp116x_show_regs(isp116x);
|
||||
isp116x_show_regs_log(isp116x);
|
||||
spin_unlock_irqrestore(&isp116x->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------*/
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int isp116x_bus_suspend(struct usb_hcd *hcd)
|
||||
{
|
||||
struct isp116x *isp116x = hcd_to_isp116x(hcd);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&isp116x->lock, flags);
|
||||
|
||||
val = isp116x_read_reg32(isp116x, HCCONTROL);
|
||||
switch (val & HCCONTROL_HCFS) {
|
||||
case HCCONTROL_USB_OPER:
|
||||
hcd->state = HC_STATE_QUIESCING;
|
||||
val &= (~HCCONTROL_HCFS & ~HCCONTROL_RWE);
|
||||
val |= HCCONTROL_USB_SUSPEND;
|
||||
if (hcd->remote_wakeup)
|
||||
val |= HCCONTROL_RWE;
|
||||
/* Wait for usb transfers to finish */
|
||||
mdelay(2);
|
||||
isp116x_write_reg32(isp116x, HCCONTROL, val);
|
||||
hcd->state = HC_STATE_SUSPENDED;
|
||||
/* Wait for devices to suspend */
|
||||
mdelay(5);
|
||||
case HCCONTROL_USB_SUSPEND:
|
||||
break;
|
||||
case HCCONTROL_USB_RESUME:
|
||||
isp116x_write_reg32(isp116x, HCCONTROL,
|
||||
(val & ~HCCONTROL_HCFS) |
|
||||
HCCONTROL_USB_RESET);
|
||||
case HCCONTROL_USB_RESET:
|
||||
ret = -EBUSY;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&isp116x->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int isp116x_bus_resume(struct usb_hcd *hcd)
|
||||
{
|
||||
struct isp116x *isp116x = hcd_to_isp116x(hcd);
|
||||
u32 val;
|
||||
|
||||
msleep(5);
|
||||
spin_lock_irq(&isp116x->lock);
|
||||
|
||||
val = isp116x_read_reg32(isp116x, HCCONTROL);
|
||||
switch (val & HCCONTROL_HCFS) {
|
||||
case HCCONTROL_USB_SUSPEND:
|
||||
val &= ~HCCONTROL_HCFS;
|
||||
val |= HCCONTROL_USB_RESUME;
|
||||
isp116x_write_reg32(isp116x, HCCONTROL, val);
|
||||
case HCCONTROL_USB_RESUME:
|
||||
break;
|
||||
case HCCONTROL_USB_OPER:
|
||||
spin_unlock_irq(&isp116x->lock);
|
||||
/* Without setting power_state here the
|
||||
SUSPENDED state won't be removed from
|
||||
sysfs/usbN/power.state as a response to remote
|
||||
wakeup. Maybe in the future. */
|
||||
hcd->self.root_hub->dev.power.power_state = PMSG_ON;
|
||||
return 0;
|
||||
default:
|
||||
/* HCCONTROL_USB_RESET: this may happen, when during
|
||||
suspension the HC lost power. Reinitialize completely */
|
||||
spin_unlock_irq(&isp116x->lock);
|
||||
DBG("Chip has been reset while suspended. Reinit from scratch.\n");
|
||||
isp116x_reset(hcd);
|
||||
isp116x_start(hcd);
|
||||
isp116x_hub_control(hcd, SetPortFeature,
|
||||
USB_PORT_FEAT_POWER, 1, NULL, 0);
|
||||
if ((isp116x->rhdesca & RH_A_NDP) == 2)
|
||||
isp116x_hub_control(hcd, SetPortFeature,
|
||||
USB_PORT_FEAT_POWER, 2, NULL, 0);
|
||||
hcd->self.root_hub->dev.power.power_state = PMSG_ON;
|
||||
return 0;
|
||||
}
|
||||
|
||||
val = isp116x->rhdesca & RH_A_NDP;
|
||||
while (val--) {
|
||||
u32 stat =
|
||||
isp116x_read_reg32(isp116x, val ? HCRHPORT2 : HCRHPORT1);
|
||||
/* force global, not selective, resume */
|
||||
if (!(stat & RH_PS_PSS))
|
||||
continue;
|
||||
DBG("%s: Resuming port %d\n", __func__, val);
|
||||
isp116x_write_reg32(isp116x, RH_PS_POCI, val
|
||||
? HCRHPORT2 : HCRHPORT1);
|
||||
}
|
||||
spin_unlock_irq(&isp116x->lock);
|
||||
|
||||
hcd->state = HC_STATE_RESUMING;
|
||||
msleep(20);
|
||||
|
||||
/* Go operational */
|
||||
spin_lock_irq(&isp116x->lock);
|
||||
val = isp116x_read_reg32(isp116x, HCCONTROL);
|
||||
isp116x_write_reg32(isp116x, HCCONTROL,
|
||||
(val & ~HCCONTROL_HCFS) | HCCONTROL_USB_OPER);
|
||||
spin_unlock_irq(&isp116x->lock);
|
||||
/* see analogous comment above */
|
||||
hcd->self.root_hub->dev.power.power_state = PMSG_ON;
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define isp116x_bus_suspend NULL
|
||||
#define isp116x_bus_resume NULL
|
||||
|
||||
#endif
|
||||
|
||||
static struct hc_driver isp116x_hc_driver = {
|
||||
.description = hcd_name,
|
||||
@ -1745,12 +1657,19 @@ static int __init isp116x_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ret = usb_add_hcd(hcd, irq, SA_INTERRUPT);
|
||||
if (ret != 0)
|
||||
if (ret)
|
||||
goto err6;
|
||||
|
||||
create_debug_file(isp116x);
|
||||
ret = create_debug_file(isp116x);
|
||||
if (ret) {
|
||||
ERR("Couldn't create debugfs entry\n");
|
||||
goto err7;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err7:
|
||||
usb_remove_hcd(hcd);
|
||||
err6:
|
||||
usb_put_hcd(hcd);
|
||||
err5:
|
||||
@ -1772,13 +1691,9 @@ static int __init isp116x_probe(struct platform_device *pdev)
|
||||
*/
|
||||
static int isp116x_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
VDBG("%s: state %x\n", __func__, state);
|
||||
|
||||
VDBG("%s: state %x\n", __func__, state.event);
|
||||
dev->dev.power.power_state = state;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1786,13 +1701,9 @@ static int isp116x_suspend(struct platform_device *dev, pm_message_t state)
|
||||
*/
|
||||
static int isp116x_resume(struct platform_device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
VDBG("%s: state %x\n", __func__, dev->dev.power.power_state);
|
||||
|
||||
VDBG("%s: state %x\n", __func__, dev->power.power_state.event);
|
||||
dev->dev.power.power_state = PMSG_ON;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -259,7 +259,7 @@ struct isp116x {
|
||||
|
||||
struct isp116x_platform_data *board;
|
||||
|
||||
struct proc_dir_entry *pde;
|
||||
struct dentry *dentry;
|
||||
unsigned long stat1, stat2, stat4, stat8, stat16;
|
||||
|
||||
/* HC registers */
|
||||
@ -450,7 +450,7 @@ static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg,
|
||||
isp116x_write_data32(isp116x, (u32) val);
|
||||
}
|
||||
|
||||
#define isp116x_show_reg(d,r) { \
|
||||
#define isp116x_show_reg_log(d,r,s) { \
|
||||
if ((r) < 0x20) { \
|
||||
DBG("%-12s[%02x]: %08x\n", #r, \
|
||||
r, isp116x_read_reg32(d, r)); \
|
||||
@ -459,35 +459,60 @@ static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg,
|
||||
r, isp116x_read_reg16(d, r)); \
|
||||
} \
|
||||
}
|
||||
#define isp116x_show_reg_seq(d,r,s) { \
|
||||
if ((r) < 0x20) { \
|
||||
seq_printf(s, "%-12s[%02x]: %08x\n", #r, \
|
||||
r, isp116x_read_reg32(d, r)); \
|
||||
} else { \
|
||||
seq_printf(s, "%-12s[%02x]: %04x\n", #r, \
|
||||
r, isp116x_read_reg16(d, r)); \
|
||||
} \
|
||||
}
|
||||
|
||||
static inline void isp116x_show_regs(struct isp116x *isp116x)
|
||||
#define isp116x_show_regs(d,type,s) { \
|
||||
isp116x_show_reg_##type(d, HCREVISION, s); \
|
||||
isp116x_show_reg_##type(d, HCCONTROL, s); \
|
||||
isp116x_show_reg_##type(d, HCCMDSTAT, s); \
|
||||
isp116x_show_reg_##type(d, HCINTSTAT, s); \
|
||||
isp116x_show_reg_##type(d, HCINTENB, s); \
|
||||
isp116x_show_reg_##type(d, HCFMINTVL, s); \
|
||||
isp116x_show_reg_##type(d, HCFMREM, s); \
|
||||
isp116x_show_reg_##type(d, HCFMNUM, s); \
|
||||
isp116x_show_reg_##type(d, HCLSTHRESH, s); \
|
||||
isp116x_show_reg_##type(d, HCRHDESCA, s); \
|
||||
isp116x_show_reg_##type(d, HCRHDESCB, s); \
|
||||
isp116x_show_reg_##type(d, HCRHSTATUS, s); \
|
||||
isp116x_show_reg_##type(d, HCRHPORT1, s); \
|
||||
isp116x_show_reg_##type(d, HCRHPORT2, s); \
|
||||
isp116x_show_reg_##type(d, HCHWCFG, s); \
|
||||
isp116x_show_reg_##type(d, HCDMACFG, s); \
|
||||
isp116x_show_reg_##type(d, HCXFERCTR, s); \
|
||||
isp116x_show_reg_##type(d, HCuPINT, s); \
|
||||
isp116x_show_reg_##type(d, HCuPINTENB, s); \
|
||||
isp116x_show_reg_##type(d, HCCHIPID, s); \
|
||||
isp116x_show_reg_##type(d, HCSCRATCH, s); \
|
||||
isp116x_show_reg_##type(d, HCITLBUFLEN, s); \
|
||||
isp116x_show_reg_##type(d, HCATLBUFLEN, s); \
|
||||
isp116x_show_reg_##type(d, HCBUFSTAT, s); \
|
||||
isp116x_show_reg_##type(d, HCRDITL0LEN, s); \
|
||||
isp116x_show_reg_##type(d, HCRDITL1LEN, s); \
|
||||
}
|
||||
|
||||
/*
|
||||
Dump registers for debugfs.
|
||||
*/
|
||||
static inline void isp116x_show_regs_seq(struct isp116x *isp116x,
|
||||
struct seq_file *s)
|
||||
{
|
||||
isp116x_show_reg(isp116x, HCREVISION);
|
||||
isp116x_show_reg(isp116x, HCCONTROL);
|
||||
isp116x_show_reg(isp116x, HCCMDSTAT);
|
||||
isp116x_show_reg(isp116x, HCINTSTAT);
|
||||
isp116x_show_reg(isp116x, HCINTENB);
|
||||
isp116x_show_reg(isp116x, HCFMINTVL);
|
||||
isp116x_show_reg(isp116x, HCFMREM);
|
||||
isp116x_show_reg(isp116x, HCFMNUM);
|
||||
isp116x_show_reg(isp116x, HCLSTHRESH);
|
||||
isp116x_show_reg(isp116x, HCRHDESCA);
|
||||
isp116x_show_reg(isp116x, HCRHDESCB);
|
||||
isp116x_show_reg(isp116x, HCRHSTATUS);
|
||||
isp116x_show_reg(isp116x, HCRHPORT1);
|
||||
isp116x_show_reg(isp116x, HCRHPORT2);
|
||||
isp116x_show_reg(isp116x, HCHWCFG);
|
||||
isp116x_show_reg(isp116x, HCDMACFG);
|
||||
isp116x_show_reg(isp116x, HCXFERCTR);
|
||||
isp116x_show_reg(isp116x, HCuPINT);
|
||||
isp116x_show_reg(isp116x, HCuPINTENB);
|
||||
isp116x_show_reg(isp116x, HCCHIPID);
|
||||
isp116x_show_reg(isp116x, HCSCRATCH);
|
||||
isp116x_show_reg(isp116x, HCITLBUFLEN);
|
||||
isp116x_show_reg(isp116x, HCATLBUFLEN);
|
||||
isp116x_show_reg(isp116x, HCBUFSTAT);
|
||||
isp116x_show_reg(isp116x, HCRDITL0LEN);
|
||||
isp116x_show_reg(isp116x, HCRDITL1LEN);
|
||||
isp116x_show_regs(isp116x, seq, s);
|
||||
}
|
||||
|
||||
/*
|
||||
Dump registers to syslog.
|
||||
*/
|
||||
static inline void isp116x_show_regs_log(struct isp116x *isp116x)
|
||||
{
|
||||
isp116x_show_regs(isp116x, log, NULL);
|
||||
}
|
||||
|
||||
#if defined(URB_TRACE)
|
||||
|
Loading…
Reference in New Issue
Block a user