diff options
author | Alek Du <alek.du@intel.com> | 2010-06-04 03:47:54 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-08-10 17:35:35 -0400 |
commit | aa4d8342988d0c1a79ff19b2ede1e81dfbb16ea5 (patch) | |
tree | 7139abaa1f63af9a70405cec91dcd1b8a30bf0d0 /drivers | |
parent | e644814a2ccbfe171d2fd2b9bca491ead1ae1a96 (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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/host/ehci-dbg.c | 144 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 1 |
3 files changed, 139 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 | |||
191 | dbg_status_buf (char *buf, unsigned len, const char *label, u32 status) | 196 | dbg_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 | |||
210 | dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable) | 216 | dbg_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 | |||
228 | dbg_command_buf (char *buf, unsigned len, const char *label, u32 command) | 235 | dbg_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 *); | |||
330 | static int debug_periodic_open(struct inode *, struct file *); | 354 | static int debug_periodic_open(struct inode *, struct file *); |
331 | static int debug_registers_open(struct inode *, struct file *); | 355 | static int debug_registers_open(struct inode *, struct file *); |
332 | static int debug_async_open(struct inode *, struct file *); | 356 | static int debug_async_open(struct inode *, struct file *); |
357 | static int debug_lpm_open(struct inode *, struct file *); | ||
358 | static ssize_t debug_lpm_read(struct file *file, char __user *user_buf, | ||
359 | size_t count, loff_t *ppos); | ||
360 | static ssize_t debug_lpm_write(struct file *file, const char __user *buffer, | ||
361 | size_t count, loff_t *ppos); | ||
362 | static int debug_lpm_close(struct inode *inode, struct file *file); | ||
363 | |||
333 | static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); | 364 | static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); |
334 | static int debug_close(struct inode *, struct file *); | 365 | static 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 | }; |
385 | static 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 | ||
355 | static struct dentry *ehci_debug_root; | 393 | static 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 | ||
958 | static int debug_lpm_open(struct inode *inode, struct file *file) | ||
959 | { | ||
960 | file->private_data = inode->i_private; | ||
961 | return 0; | ||
962 | } | ||
963 | |||
964 | static int debug_lpm_close(struct inode *inode, struct file *file) | ||
965 | { | ||
966 | return 0; | ||
967 | } | ||
968 | |||
969 | static 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 | |||
976 | static 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 | |||
920 | static inline void create_debug_files (struct ehci_hcd *ehci) | 1046 | static 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 | ||