aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlek Du <alek.du@intel.com>2010-06-04 03:47:54 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-08-10 17:35:35 -0400
commitaa4d8342988d0c1a79ff19b2ede1e81dfbb16ea5 (patch)
tree7139abaa1f63af9a70405cec91dcd1b8a30bf0d0
parente644814a2ccbfe171d2fd2b9bca491ead1ae1a96 (diff)
USB: EHCI: EHCI 1.1 addendum: preparation
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>
-rw-r--r--drivers/usb/host/ehci-dbg.c144
-rw-r--r--drivers/usb/host/ehci-hcd.c1
-rw-r--r--drivers/usb/host/ehci.h1
-rw-r--r--include/linux/usb/ehci_def.h23
4 files changed, 162 insertions, 7 deletions
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 874d2000bf92..df5546bb8367 100644
--- 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_hcd *ehci, char *label)
98 HCC_64BIT_ADDR(params) ? " 64 bit addr" : ""); 98 HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
99 } else { 99 } else {
100 ehci_dbg (ehci, 100 ehci_dbg (ehci,
101 "%s hcc_params %04x thresh %d uframes %s%s%s\n", 101 "%s hcc_params %04x thresh %d uframes %s%s%s%s%s%s%s\n",
102 label, 102 label,
103 params, 103 params,
104 HCC_ISOC_THRES(params), 104 HCC_ISOC_THRES(params),
105 HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", 105 HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
106 HCC_CANPARK(params) ? " park" : "", 106 HCC_CANPARK(params) ? " park" : "",
107 HCC_64BIT_ADDR(params) ? " 64 bit addr" : ""); 107 HCC_64BIT_ADDR(params) ? " 64 bit addr" : "",
108 HCC_LPM(params) ? " LPM" : "",
109 HCC_PER_PORT_CHANGE_EVENT(params) ? " ppce" : "",
110 HCC_HW_PREFETCH(params) ? " hw prefetch" : "",
111 HCC_32FRAME_PERIODIC_LIST(params) ?
112 " 32 peridic list" : "");
108 } 113 }
109} 114}
110#else 115#else
@@ -191,8 +196,9 @@ static int __maybe_unused
191dbg_status_buf (char *buf, unsigned len, const char *label, u32 status) 196dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
192{ 197{
193 return scnprintf (buf, len, 198 return scnprintf (buf, len,
194 "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", 199 "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s%s",
195 label, label [0] ? " " : "", status, 200 label, label [0] ? " " : "", status,
201 (status & STS_PPCE_MASK) ? " PPCE" : "",
196 (status & STS_ASS) ? " Async" : "", 202 (status & STS_ASS) ? " Async" : "",
197 (status & STS_PSS) ? " Periodic" : "", 203 (status & STS_PSS) ? " Periodic" : "",
198 (status & STS_RECL) ? " Recl" : "", 204 (status & STS_RECL) ? " Recl" : "",
@@ -210,8 +216,9 @@ static int __maybe_unused
210dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable) 216dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
211{ 217{
212 return scnprintf (buf, len, 218 return scnprintf (buf, len,
213 "%s%sintrenable %02x%s%s%s%s%s%s", 219 "%s%sintrenable %02x%s%s%s%s%s%s%s",
214 label, label [0] ? " " : "", enable, 220 label, label [0] ? " " : "", enable,
221 (enable & STS_PPCE_MASK) ? " PPCE" : "",
215 (enable & STS_IAA) ? " IAA" : "", 222 (enable & STS_IAA) ? " IAA" : "",
216 (enable & STS_FATAL) ? " FATAL" : "", 223 (enable & STS_FATAL) ? " FATAL" : "",
217 (enable & STS_FLR) ? " FLR" : "", 224 (enable & STS_FLR) ? " FLR" : "",
@@ -228,9 +235,15 @@ static int
228dbg_command_buf (char *buf, unsigned len, const char *label, u32 command) 235dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
229{ 236{
230 return scnprintf (buf, len, 237 return scnprintf (buf, len,
231 "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s", 238 "%s%scommand %07x %s%s%s%s%s%s=%d ithresh=%d%s%s%s%s "
239 "period=%s%s %s",
232 label, label [0] ? " " : "", command, 240 label, label [0] ? " " : "", command,
233 (command & CMD_PARK) ? "park" : "(park)", 241 (command & CMD_HIRD) ? " HIRD" : "",
242 (command & CMD_PPCEE) ? " PPCEE" : "",
243 (command & CMD_FSP) ? " FSP" : "",
244 (command & CMD_ASPE) ? " ASPE" : "",
245 (command & CMD_PSPE) ? " PSPE" : "",
246 (command & CMD_PARK) ? " park" : "(park)",
234 CMD_PARK_CNT (command), 247 CMD_PARK_CNT (command),
235 (command >> 16) & 0x3f, 248 (command >> 16) & 0x3f,
236 (command & CMD_LRESET) ? " LReset" : "", 249 (command & CMD_LRESET) ? " LReset" : "",
@@ -257,11 +270,22 @@ dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
257 } 270 }
258 271
259 return scnprintf (buf, len, 272 return scnprintf (buf, len,
260 "%s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s", 273 "%s%sport:%d status %06x %d %s%s%s%s%s%s "
274 "sig=%s%s%s%s%s%s%s%s%s%s%s",
261 label, label [0] ? " " : "", port, status, 275 label, label [0] ? " " : "", port, status,
276 status>>25,/*device address */
277 (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ACK ?
278 " ACK" : "",
279 (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_NYET ?
280 " NYET" : "",
281 (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_STALL ?
282 " STALL" : "",
283 (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ERR ?
284 " ERR" : "",
262 (status & PORT_POWER) ? " POWER" : "", 285 (status & PORT_POWER) ? " POWER" : "",
263 (status & PORT_OWNER) ? " OWNER" : "", 286 (status & PORT_OWNER) ? " OWNER" : "",
264 sig, 287 sig,
288 (status & PORT_LPM) ? " LPM" : "",
265 (status & PORT_RESET) ? " RESET" : "", 289 (status & PORT_RESET) ? " RESET" : "",
266 (status & PORT_SUSPEND) ? " SUSPEND" : "", 290 (status & PORT_SUSPEND) ? " SUSPEND" : "",
267 (status & PORT_RESUME) ? " RESUME" : "", 291 (status & PORT_RESUME) ? " RESUME" : "",
@@ -330,6 +354,13 @@ static int debug_async_open(struct inode *, struct file *);
330static int debug_periodic_open(struct inode *, struct file *); 354static int debug_periodic_open(struct inode *, struct file *);
331static int debug_registers_open(struct inode *, struct file *); 355static int debug_registers_open(struct inode *, struct file *);
332static int debug_async_open(struct inode *, struct file *); 356static int debug_async_open(struct inode *, struct file *);
357static int debug_lpm_open(struct inode *, struct file *);
358static ssize_t debug_lpm_read(struct file *file, char __user *user_buf,
359 size_t count, loff_t *ppos);
360static ssize_t debug_lpm_write(struct file *file, const char __user *buffer,
361 size_t count, loff_t *ppos);
362static int debug_lpm_close(struct inode *inode, struct file *file);
363
333static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); 364static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*);
334static int debug_close(struct inode *, struct file *); 365static int debug_close(struct inode *, struct file *);
335 366
@@ -351,6 +382,13 @@ static const struct file_operations debug_registers_fops = {
351 .read = debug_output, 382 .read = debug_output,
352 .release = debug_close, 383 .release = debug_close,
353}; 384};
385static const struct file_operations debug_lpm_fops = {
386 .owner = THIS_MODULE,
387 .open = debug_lpm_open,
388 .read = debug_lpm_read,
389 .write = debug_lpm_write,
390 .release = debug_lpm_close,
391};
354 392
355static struct dentry *ehci_debug_root; 393static struct dentry *ehci_debug_root;
356 394
@@ -917,6 +955,94 @@ static int debug_registers_open(struct inode *inode, struct file *file)
917 return file->private_data ? 0 : -ENOMEM; 955 return file->private_data ? 0 : -ENOMEM;
918} 956}
919 957
958static int debug_lpm_open(struct inode *inode, struct file *file)
959{
960 file->private_data = inode->i_private;
961 return 0;
962}
963
964static int debug_lpm_close(struct inode *inode, struct file *file)
965{
966 return 0;
967}
968
969static ssize_t debug_lpm_read(struct file *file, char __user *user_buf,
970 size_t count, loff_t *ppos)
971{
972 /* TODO: show lpm stats */
973 return 0;
974}
975
976static ssize_t debug_lpm_write(struct file *file, const char __user *user_buf,
977 size_t count, loff_t *ppos)
978{
979 struct usb_hcd *hcd;
980 struct ehci_hcd *ehci;
981 char buf[50];
982 size_t len;
983 u32 temp;
984 unsigned long port;
985 u32 __iomem *portsc ;
986 u32 params;
987
988 hcd = bus_to_hcd(file->private_data);
989 ehci = hcd_to_ehci(hcd);
990
991 len = min(count, sizeof(buf) - 1);
992 if (copy_from_user(buf, user_buf, len))
993 return -EFAULT;
994 buf[len] = '\0';
995 if (len > 0 && buf[len - 1] == '\n')
996 buf[len - 1] = '\0';
997
998 if (strncmp(buf, "enable", 5) == 0) {
999 if (strict_strtoul(buf + 7, 10, &port))
1000 return -EINVAL;
1001 params = ehci_readl(ehci, &ehci->caps->hcs_params);
1002 if (port > HCS_N_PORTS(params)) {
1003 ehci_dbg(ehci, "ERR: LPM on bad port %lu\n", port);
1004 return -ENODEV;
1005 }
1006 portsc = &ehci->regs->port_status[port-1];
1007 temp = ehci_readl(ehci, portsc);
1008 if (!(temp & PORT_DEV_ADDR)) {
1009 ehci_dbg(ehci, "LPM: no device attached\n");
1010 return -ENODEV;
1011 }
1012 temp |= PORT_LPM;
1013 ehci_writel(ehci, temp, portsc);
1014 printk(KERN_INFO "force enable LPM for port %lu\n", port);
1015 } else if (strncmp(buf, "hird=", 5) == 0) {
1016 unsigned long hird;
1017 if (strict_strtoul(buf + 5, 16, &hird))
1018 return -EINVAL;
1019 printk(KERN_INFO "setting hird %s %lu\n", buf + 6, hird);
1020 temp = ehci_readl(ehci, &ehci->regs->command);
1021 temp &= ~CMD_HIRD;
1022 temp |= hird << 24;
1023 ehci_writel(ehci, temp, &ehci->regs->command);
1024 } else if (strncmp(buf, "disable", 7) == 0) {
1025 if (strict_strtoul(buf + 8, 10, &port))
1026 return -EINVAL;
1027 params = ehci_readl(ehci, &ehci->caps->hcs_params);
1028 if (port > HCS_N_PORTS(params)) {
1029 ehci_dbg(ehci, "ERR: LPM off bad port %lu\n", port);
1030 return -ENODEV;
1031 }
1032 portsc = &ehci->regs->port_status[port-1];
1033 temp = ehci_readl(ehci, portsc);
1034 if (!(temp & PORT_DEV_ADDR)) {
1035 ehci_dbg(ehci, "ERR: no device attached\n");
1036 return -ENODEV;
1037 }
1038 temp &= ~PORT_LPM;
1039 ehci_writel(ehci, temp, portsc);
1040 printk(KERN_INFO "disabled LPM for port %lu\n", port);
1041 } else
1042 return -EOPNOTSUPP;
1043 return count;
1044}
1045
920static inline void create_debug_files (struct ehci_hcd *ehci) 1046static inline void create_debug_files (struct ehci_hcd *ehci)
921{ 1047{
922 struct usb_bus *bus = &ehci_to_hcd(ehci)->self; 1048 struct usb_bus *bus = &ehci_to_hcd(ehci)->self;
@@ -940,6 +1066,10 @@ static inline void create_debug_files (struct ehci_hcd *ehci)
940 ehci->debug_registers = debugfs_create_file("registers", S_IRUGO, 1066 ehci->debug_registers = debugfs_create_file("registers", S_IRUGO,
941 ehci->debug_dir, bus, 1067 ehci->debug_dir, bus,
942 &debug_registers_fops); 1068 &debug_registers_fops);
1069
1070 ehci->debug_registers = debugfs_create_file("lpm", S_IRUGO|S_IWUGO,
1071 ehci->debug_dir, bus,
1072 &debug_lpm_fops);
943 if (!ehci->debug_registers) 1073 if (!ehci->debug_registers)
944 goto registers_error; 1074 goto registers_error;
945 return; 1075 return;
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index a3ef2a9d9dc2..20ca6a9ff213 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -36,6 +36,7 @@
36#include <linux/dma-mapping.h> 36#include <linux/dma-mapping.h>
37#include <linux/debugfs.h> 37#include <linux/debugfs.h>
38#include <linux/slab.h> 38#include <linux/slab.h>
39#include <linux/uaccess.h>
39 40
40#include <asm/byteorder.h> 41#include <asm/byteorder.h>
41#include <asm/io.h> 42#include <asm/io.h>
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 650a687f2854..bfaac1646365 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -157,6 +157,7 @@ struct ehci_hcd { /* one per controller */
157 struct dentry *debug_async; 157 struct dentry *debug_async;
158 struct dentry *debug_periodic; 158 struct dentry *debug_periodic;
159 struct dentry *debug_registers; 159 struct dentry *debug_registers;
160 struct dentry *debug_lpm;
160#endif 161#endif
161}; 162};
162 163
diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h
index 80287af2a738..2e262cb15425 100644
--- a/include/linux/usb/ehci_def.h
+++ b/include/linux/usb/ehci_def.h
@@ -39,6 +39,12 @@ struct ehci_caps {
39#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ 39#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
40 40
41 u32 hcc_params; /* HCCPARAMS - offset 0x8 */ 41 u32 hcc_params; /* HCCPARAMS - offset 0x8 */
42/* EHCI 1.1 addendum */
43#define HCC_32FRAME_PERIODIC_LIST(p) ((p)&(1 << 19))
44#define HCC_PER_PORT_CHANGE_EVENT(p) ((p)&(1 << 18))
45#define HCC_LPM(p) ((p)&(1 << 17))
46#define HCC_HW_PREFETCH(p) ((p)&(1 << 16))
47
42#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ 48#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
43#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ 49#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
44#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ 50#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
@@ -54,6 +60,13 @@ struct ehci_regs {
54 60
55 /* USBCMD: offset 0x00 */ 61 /* USBCMD: offset 0x00 */
56 u32 command; 62 u32 command;
63
64/* EHCI 1.1 addendum */
65#define CMD_HIRD (0xf<<24) /* host initiated resume duration */
66#define CMD_PPCEE (1<<15) /* per port change event enable */
67#define CMD_FSP (1<<14) /* fully synchronized prefetch */
68#define CMD_ASPE (1<<13) /* async schedule prefetch enable */
69#define CMD_PSPE (1<<12) /* periodic schedule prefetch enable */
57/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ 70/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
58#define CMD_PARK (1<<11) /* enable "park" on async qh */ 71#define CMD_PARK (1<<11) /* enable "park" on async qh */
59#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ 72#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
@@ -67,6 +80,7 @@ struct ehci_regs {
67 80
68 /* USBSTS: offset 0x04 */ 81 /* USBSTS: offset 0x04 */
69 u32 status; 82 u32 status;
83#define STS_PPCE_MASK (0xff<<16) /* Per-Port change event 1-16 */
70#define STS_ASS (1<<15) /* Async Schedule Status */ 84#define STS_ASS (1<<15) /* Async Schedule Status */
71#define STS_PSS (1<<14) /* Periodic Schedule Status */ 85#define STS_PSS (1<<14) /* Periodic Schedule Status */
72#define STS_RECL (1<<13) /* Reclamation */ 86#define STS_RECL (1<<13) /* Reclamation */
@@ -100,6 +114,14 @@ struct ehci_regs {
100 114
101 /* PORTSC: offset 0x44 */ 115 /* PORTSC: offset 0x44 */
102 u32 port_status[0]; /* up to N_PORTS */ 116 u32 port_status[0]; /* up to N_PORTS */
117/* EHCI 1.1 addendum */
118#define PORTSC_SUSPEND_STS_ACK 0
119#define PORTSC_SUSPEND_STS_NYET 1
120#define PORTSC_SUSPEND_STS_STALL 2
121#define PORTSC_SUSPEND_STS_ERR 3
122
123#define PORT_DEV_ADDR (0x7f<<25) /* device address */
124#define PORT_SSTS (0x3<<23) /* suspend status */
103/* 31:23 reserved */ 125/* 31:23 reserved */
104#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ 126#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
105#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ 127#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
@@ -115,6 +137,7 @@ struct ehci_regs {
115#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ 137#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */
116/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ 138/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
117/* 9 reserved */ 139/* 9 reserved */
140#define PORT_LPM (1<<9) /* LPM transaction */
118#define PORT_RESET (1<<8) /* reset port */ 141#define PORT_RESET (1<<8) /* reset port */
119#define PORT_SUSPEND (1<<7) /* suspend port */ 142#define PORT_SUSPEND (1<<7) /* suspend port */
120#define PORT_RESUME (1<<6) /* resume it */ 143#define PORT_RESUME (1<<6) /* resume it */