Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aa4d8342 authored by Alek Du's avatar Alek Du Committed by Greg Kroah-Hartman
Browse files

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: default avatarJacob Pan <jacob.jun.pan@intel.com>
Signed-off-by: default avatarAlek Du <alek.du@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent e644814a
Loading
Loading
Loading
Loading
+137 −7
Original line number Original line Diff line number Diff line
@@ -98,13 +98,18 @@ static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
			HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
			HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
	} else {
	} else {
		ehci_dbg (ehci,
		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,
			label,
			params,
			params,
			HCC_ISOC_THRES(params),
			HCC_ISOC_THRES(params),
			HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
			HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
			HCC_CANPARK(params) ? " park" : "",
			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
#else
@@ -191,8 +196,9 @@ static int __maybe_unused
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
{
{
	return scnprintf (buf, len,
	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,
		label, label [0] ? " " : "", status,
		(status & STS_PPCE_MASK) ? " PPCE" : "",
		(status & STS_ASS) ? " Async" : "",
		(status & STS_ASS) ? " Async" : "",
		(status & STS_PSS) ? " Periodic" : "",
		(status & STS_PSS) ? " Periodic" : "",
		(status & STS_RECL) ? " Recl" : "",
		(status & STS_RECL) ? " Recl" : "",
@@ -210,8 +216,9 @@ static int __maybe_unused
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
{
{
	return scnprintf (buf, len,
	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,
		label, label [0] ? " " : "", enable,
		(enable & STS_PPCE_MASK) ? " PPCE" : "",
		(enable & STS_IAA) ? " IAA" : "",
		(enable & STS_IAA) ? " IAA" : "",
		(enable & STS_FATAL) ? " FATAL" : "",
		(enable & STS_FATAL) ? " FATAL" : "",
		(enable & STS_FLR) ? " FLR" : "",
		(enable & STS_FLR) ? " FLR" : "",
@@ -228,8 +235,14 @@ static int
dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
{
{
	return scnprintf (buf, len,
	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,
		label, label [0] ? " " : "", command,
		(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)",
		(command & CMD_PARK) ? " park" : "(park)",
		CMD_PARK_CNT (command),
		CMD_PARK_CNT (command),
		(command >> 16) & 0x3f,
		(command >> 16) & 0x3f,
@@ -257,11 +270,22 @@ dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
	}
	}


	return scnprintf (buf, len,
	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,
		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_POWER) ? " POWER" : "",
		(status & PORT_OWNER) ? " OWNER" : "",
		(status & PORT_OWNER) ? " OWNER" : "",
		sig,
		sig,
		(status & PORT_LPM) ? " LPM" : "",
		(status & PORT_RESET) ? " RESET" : "",
		(status & PORT_RESET) ? " RESET" : "",
		(status & PORT_SUSPEND) ? " SUSPEND" : "",
		(status & PORT_SUSPEND) ? " SUSPEND" : "",
		(status & PORT_RESUME) ? " RESUME" : "",
		(status & PORT_RESUME) ? " RESUME" : "",
@@ -330,6 +354,13 @@ static int debug_async_open(struct inode *, struct file *);
static int debug_periodic_open(struct inode *, struct file *);
static int debug_periodic_open(struct inode *, struct file *);
static int debug_registers_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_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 ssize_t debug_output(struct file*, char __user*, size_t, loff_t*);
static int debug_close(struct inode *, struct file *);
static int debug_close(struct inode *, struct file *);


@@ -351,6 +382,13 @@ static const struct file_operations debug_registers_fops = {
	.read		= debug_output,
	.read		= debug_output,
	.release	= debug_close,
	.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;
static struct dentry *ehci_debug_root;


@@ -917,6 +955,94 @@ static int debug_registers_open(struct inode *inode, struct file *file)
	return file->private_data ? 0 : -ENOMEM;
	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)
static inline void create_debug_files (struct ehci_hcd *ehci)
{
{
	struct usb_bus *bus = &ehci_to_hcd(ehci)->self;
	struct usb_bus *bus = &ehci_to_hcd(ehci)->self;
@@ -940,6 +1066,10 @@ static inline void create_debug_files (struct ehci_hcd *ehci)
	ehci->debug_registers = debugfs_create_file("registers", S_IRUGO,
	ehci->debug_registers = debugfs_create_file("registers", S_IRUGO,
						    ehci->debug_dir, bus,
						    ehci->debug_dir, bus,
						    &debug_registers_fops);
						    &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)
	if (!ehci->debug_registers)
		goto registers_error;
		goto registers_error;
	return;
	return;
+1 −0
Original line number Original line Diff line number Diff line
@@ -36,6 +36,7 @@
#include <linux/dma-mapping.h>
#include <linux/dma-mapping.h>
#include <linux/debugfs.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/uaccess.h>


#include <asm/byteorder.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/io.h>
+1 −0
Original line number Original line Diff line number Diff line
@@ -157,6 +157,7 @@ struct ehci_hcd { /* one per controller */
	struct dentry		*debug_async;
	struct dentry		*debug_async;
	struct dentry		*debug_periodic;
	struct dentry		*debug_periodic;
	struct dentry		*debug_registers;
	struct dentry		*debug_registers;
	struct dentry		*debug_lpm;
#endif
#endif
};
};


+23 −0
Original line number Original line Diff line number Diff line
@@ -39,6 +39,12 @@ struct ehci_caps {
#define HCS_N_PORTS(p)		(((p)>>0)&0xf)	/* bits 3:0, ports on HC */
#define HCS_N_PORTS(p)		(((p)>>0)&0xf)	/* bits 3:0, ports on HC */


	u32		hcc_params;      /* HCCPARAMS - offset 0x8 */
	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_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_CACHE(p)       ((p)&(1 << 7))  /* true: can cache isoc frame */
#define HCC_ISOC_THRES(p)       (((p)>>4)&0x7)  /* bits 6:4, uframes cached */
#define HCC_ISOC_THRES(p)       (((p)>>4)&0x7)  /* bits 6:4, uframes cached */
@@ -54,6 +60,13 @@ struct ehci_regs {


	/* USBCMD: offset 0x00 */
	/* USBCMD: offset 0x00 */
	u32		command;
	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 */
/* 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	(1<<11)		/* enable "park" on async qh */
#define CMD_PARK_CNT(c)	(((c)>>8)&3)	/* how many transfers to park for */
#define CMD_PARK_CNT(c)	(((c)>>8)&3)	/* how many transfers to park for */
@@ -67,6 +80,7 @@ struct ehci_regs {


	/* USBSTS: offset 0x04 */
	/* USBSTS: offset 0x04 */
	u32		status;
	u32		status;
#define STS_PPCE_MASK	(0xff<<16)	/* Per-Port change event 1-16 */
#define STS_ASS		(1<<15)		/* Async Schedule Status */
#define STS_ASS		(1<<15)		/* Async Schedule Status */
#define STS_PSS		(1<<14)		/* Periodic Schedule Status */
#define STS_PSS		(1<<14)		/* Periodic Schedule Status */
#define STS_RECL	(1<<13)		/* Reclamation */
#define STS_RECL	(1<<13)		/* Reclamation */
@@ -100,6 +114,14 @@ struct ehci_regs {


	/* PORTSC: offset 0x44 */
	/* PORTSC: offset 0x44 */
	u32		port_status[0];	/* up to N_PORTS */
	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 */
/* 31:23 reserved */
#define PORT_WKOC_E	(1<<22)		/* wake on overcurrent (enable) */
#define PORT_WKOC_E	(1<<22)		/* wake on overcurrent (enable) */
#define PORT_WKDISC_E	(1<<21)		/* wake on disconnect (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 */
#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10))	/* USB 1.1 device */
/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
/* 9 reserved */
/* 9 reserved */
#define PORT_LPM	(1<<9)		/* LPM transaction */
#define PORT_RESET	(1<<8)		/* reset port */
#define PORT_RESET	(1<<8)		/* reset port */
#define PORT_SUSPEND	(1<<7)		/* suspend port */
#define PORT_SUSPEND	(1<<7)		/* suspend port */
#define PORT_RESUME	(1<<6)		/* resume it */
#define PORT_RESUME	(1<<6)		/* resume it */