diff options
Diffstat (limited to 'usb/usb-ehci-ehci-1.1-addendum-preparation.patch')
| -rw-r--r-- | usb/usb-ehci-ehci-1.1-addendum-preparation.patch | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/usb/usb-ehci-ehci-1.1-addendum-preparation.patch b/usb/usb-ehci-ehci-1.1-addendum-preparation.patch new file mode 100644 index 00000000000000..21ad12d6232f20 --- /dev/null +++ b/usb/usb-ehci-ehci-1.1-addendum-preparation.patch @@ -0,0 +1,323 @@ +From alek.du@intel.com Wed Jun 16 13:27:54 2010 +From: Alek Du <alek.du@intel.com> +Date: Fri, 4 Jun 2010 15:47:54 +0800 +Subject: USB: EHCI: EHCI 1.1 addendum: preparation +To: greg@kroah.com +Cc: david-b@pacbell.net, linux-usb@vger.kernel.org, oneukum@suse.de, Alek Du <alek.du@intel.com>, Jacob Pan <jacob.jun.pan@intel.com> +Message-ID: <1275637676-24880-2-git-send-email-alek.du@intel.com> + + +From: Alek Du <alek.du@intel.com> + +EHCI 1.1 addendum introduced several energy efficiency extensions for +EHCI USB host controllers: +1. LPM (link power management) +2. Per-port change +3. Shorter periodic frame list +4. Hardware prefetching + +This patch is intended to define the HW bits and debug interface for +EHCI 1.1 addendum. The LPM and Per-port change patches will be sent out +after this patch. + +Signed-off-by: Jacob Pan <jacob.jun.pan@intel.com> +Signed-off-by: Alek Du <alek.du@intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/host/ehci-dbg.c | 144 ++++++++++++++++++++++++++++++++++++++++--- + drivers/usb/host/ehci.h | 1 + include/linux/usb/ehci_def.h | 23 ++++++ + 3 files changed, 161 insertions(+), 7 deletions(-) + +--- a/drivers/usb/host/ehci-dbg.c ++++ b/drivers/usb/host/ehci-dbg.c +@@ -98,13 +98,18 @@ static void dbg_hcc_params (struct ehci_ + HCC_64BIT_ADDR(params) ? " 64 bit addr" : ""); + } else { + ehci_dbg (ehci, +- "%s hcc_params %04x thresh %d uframes %s%s%s\n", ++ "%s hcc_params %04x thresh %d uframes %s%s%s%s%s%s%s\n", + label, + params, + HCC_ISOC_THRES(params), + HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", + HCC_CANPARK(params) ? " park" : "", +- HCC_64BIT_ADDR(params) ? " 64 bit addr" : ""); ++ HCC_64BIT_ADDR(params) ? " 64 bit addr" : "", ++ HCC_LPM(params) ? " LPM" : "", ++ HCC_PER_PORT_CHANGE_EVENT(params) ? " ppce" : "", ++ HCC_HW_PREFETCH(params) ? " hw prefetch" : "", ++ HCC_32FRAME_PERIODIC_LIST(params) ? ++ " 32 peridic list" : ""); + } + } + #else +@@ -191,8 +196,9 @@ static int __maybe_unused + dbg_status_buf (char *buf, unsigned len, const char *label, u32 status) + { + return scnprintf (buf, len, +- "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", ++ "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s%s", + label, label [0] ? " " : "", status, ++ (status & STS_PPCE_MASK) ? " PPCE" : "", + (status & STS_ASS) ? " Async" : "", + (status & STS_PSS) ? " Periodic" : "", + (status & STS_RECL) ? " Recl" : "", +@@ -210,8 +216,9 @@ static int __maybe_unused + dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable) + { + return scnprintf (buf, len, +- "%s%sintrenable %02x%s%s%s%s%s%s", ++ "%s%sintrenable %02x%s%s%s%s%s%s%s", + label, label [0] ? " " : "", enable, ++ (enable & STS_PPCE_MASK) ? " PPCE" : "", + (enable & STS_IAA) ? " IAA" : "", + (enable & STS_FATAL) ? " FATAL" : "", + (enable & STS_FLR) ? " FLR" : "", +@@ -228,9 +235,15 @@ static int + dbg_command_buf (char *buf, unsigned len, const char *label, u32 command) + { + return scnprintf (buf, len, +- "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s", ++ "%s%scommand %07x %s%s%s%s%s%s=%d ithresh=%d%s%s%s%s " ++ "period=%s%s %s", + label, label [0] ? " " : "", command, +- (command & CMD_PARK) ? "park" : "(park)", ++ (command & CMD_HIRD) ? " HIRD" : "", ++ (command & CMD_PPCEE) ? " PPCEE" : "", ++ (command & CMD_FSP) ? " FSP" : "", ++ (command & CMD_ASPE) ? " ASPE" : "", ++ (command & CMD_PSPE) ? " PSPE" : "", ++ (command & CMD_PARK) ? " park" : "(park)", + CMD_PARK_CNT (command), + (command >> 16) & 0x3f, + (command & CMD_LRESET) ? " LReset" : "", +@@ -257,11 +270,22 @@ dbg_port_buf (char *buf, unsigned len, c + } + + return scnprintf (buf, len, +- "%s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s", ++ "%s%sport:%d status %06x %d %s%s%s%s%s%s " ++ "sig=%s%s%s%s%s%s%s%s%s%s%s", + label, label [0] ? " " : "", port, status, ++ status>>25,/*device address */ ++ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ACK ? ++ " ACK" : "", ++ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_NYET ? ++ " NYET" : "", ++ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_STALL ? ++ " STALL" : "", ++ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ERR ? ++ " ERR" : "", + (status & PORT_POWER) ? " POWER" : "", + (status & PORT_OWNER) ? " OWNER" : "", + sig, ++ (status & PORT_LPM) ? " LPM" : "", + (status & PORT_RESET) ? " RESET" : "", + (status & PORT_SUSPEND) ? " SUSPEND" : "", + (status & PORT_RESUME) ? " RESUME" : "", +@@ -330,6 +354,13 @@ static int debug_async_open(struct inode + static int debug_periodic_open(struct inode *, struct file *); + static int debug_registers_open(struct inode *, struct file *); + static int debug_async_open(struct inode *, struct file *); ++static int debug_lpm_open(struct inode *, struct file *); ++static ssize_t debug_lpm_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos); ++static ssize_t debug_lpm_write(struct file *file, const char __user *buffer, ++ size_t count, loff_t *ppos); ++static int debug_lpm_close(struct inode *inode, struct file *file); ++ + static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); + static int debug_close(struct inode *, struct file *); + +@@ -351,6 +382,13 @@ static const struct file_operations debu + .read = debug_output, + .release = debug_close, + }; ++static const struct file_operations debug_lpm_fops = { ++ .owner = THIS_MODULE, ++ .open = debug_lpm_open, ++ .read = debug_lpm_read, ++ .write = debug_lpm_write, ++ .release = debug_lpm_close, ++}; + + static struct dentry *ehci_debug_root; + +@@ -917,6 +955,94 @@ static int debug_registers_open(struct i + return file->private_data ? 0 : -ENOMEM; + } + ++static int debug_lpm_open(struct inode *inode, struct file *file) ++{ ++ file->private_data = inode->i_private; ++ return 0; ++} ++ ++static int debug_lpm_close(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++static ssize_t debug_lpm_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ /* TODO: show lpm stats */ ++ return 0; ++} ++ ++static ssize_t debug_lpm_write(struct file *file, const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct usb_hcd *hcd; ++ struct ehci_hcd *ehci; ++ char buf[50]; ++ size_t len; ++ u32 temp; ++ unsigned long port; ++ u32 __iomem *portsc ; ++ u32 params; ++ ++ hcd = bus_to_hcd(file->private_data); ++ ehci = hcd_to_ehci(hcd); ++ ++ len = min(count, sizeof(buf) - 1); ++ if (copy_from_user(buf, user_buf, len)) ++ return -EFAULT; ++ buf[len] = '\0'; ++ if (len > 0 && buf[len - 1] == '\n') ++ buf[len - 1] = '\0'; ++ ++ if (strncmp(buf, "enable", 5) == 0) { ++ if (strict_strtoul(buf + 7, 10, &port)) ++ return -EINVAL; ++ params = ehci_readl(ehci, &ehci->caps->hcs_params); ++ if (port > HCS_N_PORTS(params)) { ++ ehci_dbg(ehci, "ERR: LPM on bad port %lu\n", port); ++ return -ENODEV; ++ } ++ portsc = &ehci->regs->port_status[port-1]; ++ temp = ehci_readl(ehci, portsc); ++ if (!(temp & PORT_DEV_ADDR)) { ++ ehci_dbg(ehci, "LPM: no device attached\n"); ++ return -ENODEV; ++ } ++ temp |= PORT_LPM; ++ ehci_writel(ehci, temp, portsc); ++ printk(KERN_INFO "force enable LPM for port %lu\n", port); ++ } else if (strncmp(buf, "hird=", 5) == 0) { ++ unsigned long hird; ++ if (strict_strtoul(buf + 5, 16, &hird)) ++ return -EINVAL; ++ printk(KERN_INFO "setting hird %s %lu\n", buf + 6, hird); ++ temp = ehci_readl(ehci, &ehci->regs->command); ++ temp &= ~CMD_HIRD; ++ temp |= hird << 24; ++ ehci_writel(ehci, temp, &ehci->regs->command); ++ } else if (strncmp(buf, "disable", 7) == 0) { ++ if (strict_strtoul(buf + 8, 10, &port)) ++ return -EINVAL; ++ params = ehci_readl(ehci, &ehci->caps->hcs_params); ++ if (port > HCS_N_PORTS(params)) { ++ ehci_dbg(ehci, "ERR: LPM off bad port %lu\n", port); ++ return -ENODEV; ++ } ++ portsc = &ehci->regs->port_status[port-1]; ++ temp = ehci_readl(ehci, portsc); ++ if (!(temp & PORT_DEV_ADDR)) { ++ ehci_dbg(ehci, "ERR: no device attached\n"); ++ return -ENODEV; ++ } ++ temp &= ~PORT_LPM; ++ ehci_writel(ehci, temp, portsc); ++ printk(KERN_INFO "disabled LPM for port %lu\n", port); ++ } else ++ return -EOPNOTSUPP; ++ return count; ++} ++ + static inline void create_debug_files (struct ehci_hcd *ehci) + { + struct usb_bus *bus = &ehci_to_hcd(ehci)->self; +@@ -940,6 +1066,10 @@ static inline void create_debug_files (s + ehci->debug_registers = debugfs_create_file("registers", S_IRUGO, + ehci->debug_dir, bus, + &debug_registers_fops); ++ ++ ehci->debug_registers = debugfs_create_file("lpm", S_IRUGO|S_IWUGO, ++ ehci->debug_dir, bus, ++ &debug_lpm_fops); + if (!ehci->debug_registers) + goto registers_error; + return; +--- a/drivers/usb/host/ehci.h ++++ b/drivers/usb/host/ehci.h +@@ -157,6 +157,7 @@ struct ehci_hcd { /* one per controlle + struct dentry *debug_async; + struct dentry *debug_periodic; + struct dentry *debug_registers; ++ struct dentry *debug_lpm; + #endif + }; + +--- a/include/linux/usb/ehci_def.h ++++ b/include/linux/usb/ehci_def.h +@@ -39,6 +39,12 @@ struct ehci_caps { + #define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ + + u32 hcc_params; /* HCCPARAMS - offset 0x8 */ ++/* EHCI 1.1 addendum */ ++#define HCC_32FRAME_PERIODIC_LIST(p) ((p)&(1 << 19)) ++#define HCC_PER_PORT_CHANGE_EVENT(p) ((p)&(1 << 18)) ++#define HCC_LPM(p) ((p)&(1 << 17)) ++#define HCC_HW_PREFETCH(p) ((p)&(1 << 16)) ++ + #define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ + #define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ + #define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ +@@ -54,6 +60,13 @@ struct ehci_regs { + + /* USBCMD: offset 0x00 */ + u32 command; ++ ++/* EHCI 1.1 addendum */ ++#define CMD_HIRD (0xf<<24) /* host initiated resume duration */ ++#define CMD_PPCEE (1<<15) /* per port change event enable */ ++#define CMD_FSP (1<<14) /* fully synchronized prefetch */ ++#define CMD_ASPE (1<<13) /* async schedule prefetch enable */ ++#define CMD_PSPE (1<<12) /* periodic schedule prefetch enable */ + /* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ + #define CMD_PARK (1<<11) /* enable "park" on async qh */ + #define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ +@@ -67,6 +80,7 @@ struct ehci_regs { + + /* USBSTS: offset 0x04 */ + u32 status; ++#define STS_PPCE_MASK (0xff<<16) /* Per-Port change event 1-16 */ + #define STS_ASS (1<<15) /* Async Schedule Status */ + #define STS_PSS (1<<14) /* Periodic Schedule Status */ + #define STS_RECL (1<<13) /* Reclamation */ +@@ -100,6 +114,14 @@ struct ehci_regs { + + /* PORTSC: offset 0x44 */ + u32 port_status[0]; /* up to N_PORTS */ ++/* EHCI 1.1 addendum */ ++#define PORTSC_SUSPEND_STS_ACK 0 ++#define PORTSC_SUSPEND_STS_NYET 1 ++#define PORTSC_SUSPEND_STS_STALL 2 ++#define PORTSC_SUSPEND_STS_ERR 3 ++ ++#define PORT_DEV_ADDR (0x7f<<25) /* device address */ ++#define PORT_SSTS (0x3<<23) /* suspend status */ + /* 31:23 reserved */ + #define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ + #define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ +@@ -115,6 +137,7 @@ struct ehci_regs { + #define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ + /* 11:10 for detecting lowspeed devices (reset vs release ownership) */ + /* 9 reserved */ ++#define PORT_LPM (1<<9) /* LPM transaction */ + #define PORT_RESET (1<<8) /* reset port */ + #define PORT_SUSPEND (1<<7) /* suspend port */ + #define PORT_RESUME (1<<6) /* resume it */ |
