Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Alek Du and committed by
Greg Kroah-Hartman
aa4d8342 e644814a

+162 -7
+137 -7
drivers/usb/host/ehci-dbg.c
··· 98 98 HCC_64BIT_ADDR(params) ? " 64 bit addr" : ""); 99 99 } else { 100 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 102 label, 103 103 params, 104 104 HCC_ISOC_THRES(params), 105 105 HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", 106 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 115 #else ··· 196 191 dbg_status_buf (char *buf, unsigned len, const char *label, u32 status) 197 192 { 198 193 return scnprintf (buf, len, 199 - "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", 194 + "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s%s", 200 195 label, label [0] ? " " : "", status, 196 + (status & STS_PPCE_MASK) ? " PPCE" : "", 201 197 (status & STS_ASS) ? " Async" : "", 202 198 (status & STS_PSS) ? " Periodic" : "", 203 199 (status & STS_RECL) ? " Recl" : "", ··· 216 210 dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable) 217 211 { 218 212 return scnprintf (buf, len, 219 - "%s%sintrenable %02x%s%s%s%s%s%s", 213 + "%s%sintrenable %02x%s%s%s%s%s%s%s", 220 214 label, label [0] ? " " : "", enable, 215 + (enable & STS_PPCE_MASK) ? " PPCE" : "", 221 216 (enable & STS_IAA) ? " IAA" : "", 222 217 (enable & STS_FATAL) ? " FATAL" : "", 223 218 (enable & STS_FLR) ? " FLR" : "", ··· 235 228 dbg_command_buf (char *buf, unsigned len, const char *label, u32 command) 236 229 { 237 230 return scnprintf (buf, len, 238 - "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s", 231 + "%s%scommand %07x %s%s%s%s%s%s=%d ithresh=%d%s%s%s%s " 232 + "period=%s%s %s", 239 233 label, label [0] ? " " : "", command, 240 - (command & CMD_PARK) ? "park" : "(park)", 234 + (command & CMD_HIRD) ? " HIRD" : "", 235 + (command & CMD_PPCEE) ? " PPCEE" : "", 236 + (command & CMD_FSP) ? " FSP" : "", 237 + (command & CMD_ASPE) ? " ASPE" : "", 238 + (command & CMD_PSPE) ? " PSPE" : "", 239 + (command & CMD_PARK) ? " park" : "(park)", 241 240 CMD_PARK_CNT (command), 242 241 (command >> 16) & 0x3f, 243 242 (command & CMD_LRESET) ? " LReset" : "", ··· 270 257 } 271 258 272 259 return scnprintf (buf, len, 273 - "%s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s", 260 + "%s%sport:%d status %06x %d %s%s%s%s%s%s " 261 + "sig=%s%s%s%s%s%s%s%s%s%s%s", 274 262 label, label [0] ? " " : "", port, status, 263 + status>>25,/*device address */ 264 + (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ACK ? 265 + " ACK" : "", 266 + (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_NYET ? 267 + " NYET" : "", 268 + (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_STALL ? 269 + " STALL" : "", 270 + (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ERR ? 271 + " ERR" : "", 275 272 (status & PORT_POWER) ? " POWER" : "", 276 273 (status & PORT_OWNER) ? " OWNER" : "", 277 274 sig, 275 + (status & PORT_LPM) ? " LPM" : "", 278 276 (status & PORT_RESET) ? " RESET" : "", 279 277 (status & PORT_SUSPEND) ? " SUSPEND" : "", 280 278 (status & PORT_RESUME) ? " RESUME" : "", ··· 354 330 static int debug_periodic_open(struct inode *, struct file *); 355 331 static int debug_registers_open(struct inode *, struct file *); 356 332 static int debug_async_open(struct inode *, struct file *); 333 + static int debug_lpm_open(struct inode *, struct file *); 334 + static ssize_t debug_lpm_read(struct file *file, char __user *user_buf, 335 + size_t count, loff_t *ppos); 336 + static ssize_t debug_lpm_write(struct file *file, const char __user *buffer, 337 + size_t count, loff_t *ppos); 338 + static int debug_lpm_close(struct inode *inode, struct file *file); 339 + 357 340 static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); 358 341 static int debug_close(struct inode *, struct file *); 359 342 ··· 381 350 .open = debug_registers_open, 382 351 .read = debug_output, 383 352 .release = debug_close, 353 + }; 354 + static const struct file_operations debug_lpm_fops = { 355 + .owner = THIS_MODULE, 356 + .open = debug_lpm_open, 357 + .read = debug_lpm_read, 358 + .write = debug_lpm_write, 359 + .release = debug_lpm_close, 384 360 }; 385 361 386 362 static struct dentry *ehci_debug_root; ··· 955 917 return file->private_data ? 0 : -ENOMEM; 956 918 } 957 919 920 + static int debug_lpm_open(struct inode *inode, struct file *file) 921 + { 922 + file->private_data = inode->i_private; 923 + return 0; 924 + } 925 + 926 + static int debug_lpm_close(struct inode *inode, struct file *file) 927 + { 928 + return 0; 929 + } 930 + 931 + static ssize_t debug_lpm_read(struct file *file, char __user *user_buf, 932 + size_t count, loff_t *ppos) 933 + { 934 + /* TODO: show lpm stats */ 935 + return 0; 936 + } 937 + 938 + static ssize_t debug_lpm_write(struct file *file, const char __user *user_buf, 939 + size_t count, loff_t *ppos) 940 + { 941 + struct usb_hcd *hcd; 942 + struct ehci_hcd *ehci; 943 + char buf[50]; 944 + size_t len; 945 + u32 temp; 946 + unsigned long port; 947 + u32 __iomem *portsc ; 948 + u32 params; 949 + 950 + hcd = bus_to_hcd(file->private_data); 951 + ehci = hcd_to_ehci(hcd); 952 + 953 + len = min(count, sizeof(buf) - 1); 954 + if (copy_from_user(buf, user_buf, len)) 955 + return -EFAULT; 956 + buf[len] = '\0'; 957 + if (len > 0 && buf[len - 1] == '\n') 958 + buf[len - 1] = '\0'; 959 + 960 + if (strncmp(buf, "enable", 5) == 0) { 961 + if (strict_strtoul(buf + 7, 10, &port)) 962 + return -EINVAL; 963 + params = ehci_readl(ehci, &ehci->caps->hcs_params); 964 + if (port > HCS_N_PORTS(params)) { 965 + ehci_dbg(ehci, "ERR: LPM on bad port %lu\n", port); 966 + return -ENODEV; 967 + } 968 + portsc = &ehci->regs->port_status[port-1]; 969 + temp = ehci_readl(ehci, portsc); 970 + if (!(temp & PORT_DEV_ADDR)) { 971 + ehci_dbg(ehci, "LPM: no device attached\n"); 972 + return -ENODEV; 973 + } 974 + temp |= PORT_LPM; 975 + ehci_writel(ehci, temp, portsc); 976 + printk(KERN_INFO "force enable LPM for port %lu\n", port); 977 + } else if (strncmp(buf, "hird=", 5) == 0) { 978 + unsigned long hird; 979 + if (strict_strtoul(buf + 5, 16, &hird)) 980 + return -EINVAL; 981 + printk(KERN_INFO "setting hird %s %lu\n", buf + 6, hird); 982 + temp = ehci_readl(ehci, &ehci->regs->command); 983 + temp &= ~CMD_HIRD; 984 + temp |= hird << 24; 985 + ehci_writel(ehci, temp, &ehci->regs->command); 986 + } else if (strncmp(buf, "disable", 7) == 0) { 987 + if (strict_strtoul(buf + 8, 10, &port)) 988 + return -EINVAL; 989 + params = ehci_readl(ehci, &ehci->caps->hcs_params); 990 + if (port > HCS_N_PORTS(params)) { 991 + ehci_dbg(ehci, "ERR: LPM off bad port %lu\n", port); 992 + return -ENODEV; 993 + } 994 + portsc = &ehci->regs->port_status[port-1]; 995 + temp = ehci_readl(ehci, portsc); 996 + if (!(temp & PORT_DEV_ADDR)) { 997 + ehci_dbg(ehci, "ERR: no device attached\n"); 998 + return -ENODEV; 999 + } 1000 + temp &= ~PORT_LPM; 1001 + ehci_writel(ehci, temp, portsc); 1002 + printk(KERN_INFO "disabled LPM for port %lu\n", port); 1003 + } else 1004 + return -EOPNOTSUPP; 1005 + return count; 1006 + } 1007 + 958 1008 static inline void create_debug_files (struct ehci_hcd *ehci) 959 1009 { 960 1010 struct usb_bus *bus = &ehci_to_hcd(ehci)->self; ··· 1066 940 ehci->debug_registers = debugfs_create_file("registers", S_IRUGO, 1067 941 ehci->debug_dir, bus, 1068 942 &debug_registers_fops); 943 + 944 + ehci->debug_registers = debugfs_create_file("lpm", S_IRUGO|S_IWUGO, 945 + ehci->debug_dir, bus, 946 + &debug_lpm_fops); 1069 947 if (!ehci->debug_registers) 1070 948 goto registers_error; 1071 949 return;
+1
drivers/usb/host/ehci-hcd.c
··· 36 36 #include <linux/dma-mapping.h> 37 37 #include <linux/debugfs.h> 38 38 #include <linux/slab.h> 39 + #include <linux/uaccess.h> 39 40 40 41 #include <asm/byteorder.h> 41 42 #include <asm/io.h>
+1
drivers/usb/host/ehci.h
··· 157 157 struct dentry *debug_async; 158 158 struct dentry *debug_periodic; 159 159 struct dentry *debug_registers; 160 + struct dentry *debug_lpm; 160 161 #endif 161 162 }; 162 163
+23
include/linux/usb/ehci_def.h
··· 39 39 #define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ 40 40 41 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 48 #define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ 43 49 #define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ 44 50 #define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ ··· 60 54 61 55 /* USBCMD: offset 0x00 */ 62 56 u32 command; 57 + 58 + /* EHCI 1.1 addendum */ 59 + #define CMD_HIRD (0xf<<24) /* host initiated resume duration */ 60 + #define CMD_PPCEE (1<<15) /* per port change event enable */ 61 + #define CMD_FSP (1<<14) /* fully synchronized prefetch */ 62 + #define CMD_ASPE (1<<13) /* async schedule prefetch enable */ 63 + #define CMD_PSPE (1<<12) /* periodic schedule prefetch enable */ 63 64 /* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ 64 65 #define CMD_PARK (1<<11) /* enable "park" on async qh */ 65 66 #define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ ··· 80 67 81 68 /* USBSTS: offset 0x04 */ 82 69 u32 status; 70 + #define STS_PPCE_MASK (0xff<<16) /* Per-Port change event 1-16 */ 83 71 #define STS_ASS (1<<15) /* Async Schedule Status */ 84 72 #define STS_PSS (1<<14) /* Periodic Schedule Status */ 85 73 #define STS_RECL (1<<13) /* Reclamation */ ··· 114 100 115 101 /* PORTSC: offset 0x44 */ 116 102 u32 port_status[0]; /* up to N_PORTS */ 103 + /* EHCI 1.1 addendum */ 104 + #define PORTSC_SUSPEND_STS_ACK 0 105 + #define PORTSC_SUSPEND_STS_NYET 1 106 + #define PORTSC_SUSPEND_STS_STALL 2 107 + #define PORTSC_SUSPEND_STS_ERR 3 108 + 109 + #define PORT_DEV_ADDR (0x7f<<25) /* device address */ 110 + #define PORT_SSTS (0x3<<23) /* suspend status */ 117 111 /* 31:23 reserved */ 118 112 #define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ 119 113 #define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ ··· 137 115 #define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ 138 116 /* 11:10 for detecting lowspeed devices (reset vs release ownership) */ 139 117 /* 9 reserved */ 118 + #define PORT_LPM (1<<9) /* LPM transaction */ 140 119 #define PORT_RESET (1<<8) /* reset port */ 141 120 #define PORT_SUSPEND (1<<7) /* suspend port */ 142 121 #define PORT_RESUME (1<<6) /* resume it */