usb: host: pehci: Add USB core changes needed by PEHCI

A user can initiate host compliance tests or suspend/resume
requests over the ISP1763A USB Host controller by issuing
IOCTLs on the pehci device.
Add necessary changes in USB core to support these requests.
These changes are taken from the 1.0.6.0 version of ST-E pehci
driver present at: http://www.box.net/shared/static/o5esh7r14q.zip
along with the fixes for checkpatch errors and removal of unused
variables.

Change-Id: Ic6b175bcd3104730bac90278456e3bfbcd507801
Signed-off-by: Manu Gautam <mgautam@codeaurora.org>
This commit is contained in:
Manu Gautam
2011-01-06 12:38:05 +05:30
committed by Stephen Boyd
parent e3c0e555c3
commit 05ea6076b8
3 changed files with 377 additions and 1 deletions

View File

@@ -30,6 +30,31 @@
#include "usb.h"
#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
#include <linux/usb/hcd.h>
#include <linux/usb/ch11.h>
int portno;
int No_Data_Phase;
EXPORT_SYMBOL(No_Data_Phase);
int No_Status_Phase;
EXPORT_SYMBOL(No_Status_Phase);
unsigned char hub_tier;
#define PDC_HOST_NOTIFY 0x8001 /*completion from core */
#define UNSUPPORTED_DEVICE 0x8099
#define UNWANTED_SUSPEND 0x8098
#define PDC_POWERMANAGEMENT 0x8097
int Unwanted_SecondReset;
EXPORT_SYMBOL(Unwanted_SecondReset);
int HostComplianceTest;
EXPORT_SYMBOL(HostComplianceTest);
int HostTest;
EXPORT_SYMBOL(HostTest);
#endif
/* if we are in debug mode, always announce new devices */
#ifdef DEBUG
#ifndef CONFIG_USB_ANNOUNCE_NEW_DEVICES
@@ -3366,6 +3391,9 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
(portchange & USB_PORT_STAT_C_CONNECTION))
clear_bit(port1, hub->removed_bits);
#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
if (Unwanted_SecondReset == 0) /*stericsson*/
#endif
if (portchange & (USB_PORT_STAT_C_CONNECTION |
USB_PORT_STAT_C_ENABLE)) {
status = hub_port_debounce(hub, port1);
@@ -3504,7 +3532,32 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
status = hub_power_remaining(hub);
if (status)
dev_dbg(hub_dev, "%dmA power budget left\n", status);
#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
if (HostComplianceTest == 1 && udev->devnum > 1) {
if (HostTest == 7) { /*SINGLE_STEP_GET_DEV_DESC */
dev_info(hub_dev, "Testing "
"SINGLE_STEP_GET_DEV_DESC\n");
/* Test the Single Step Get Device Descriptor ,
* take care it should not get status phase
*/
No_Data_Phase = 1;
No_Status_Phase = 1;
usb_get_device_descriptor(udev, 8);
No_Data_Phase = 0;
No_Status_Phase = 0;
}
if (HostTest == 8) {
dev_info(hub_dev, "Testing "
"SINGLE_STEP_SET_FEATURE\n");
/* Test Single Step Set Feature */
No_Status_Phase = 1;
usb_get_device_descriptor(udev, 8);
No_Status_Phase = 0;
}
}
#endif
return;
loop_disable:
@@ -3581,6 +3634,11 @@ static void hub_events(void)
u16 portstatus;
u16 portchange;
int i, ret;
#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
int j;
int otgport = 0;
struct usb_port_status port_status;
#endif
int connect_change, wakeup_change;
/*
@@ -3657,6 +3715,171 @@ static void hub_events(void)
/* deal with port status changes */
for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
struct usb_port_status portsts;
/*if we have something to do on
* otg port
* */
if ((hdev->otgstate & USB_OTG_SUSPEND) ||
(hdev->otgstate & USB_OTG_ENUMERATE) ||
(hdev->otgstate & USB_OTG_DISCONNECT) ||
(hdev->otgstate & USB_OTG_RESUME)) {
otgport = 1;
}
if (hdev->otgstate & USB_OTG_RESUME) {
ret = clear_port_feature(hdev, i,
USB_PORT_FEAT_SUSPEND);
if (ret < 0) {
dev_err(hub_dev, "usb otg port Resume"
" fails, %d\n", ret);
}
hdev->otgstate &= ~USB_OTG_RESUME;
}
if ((hdev->otgstate & USB_OTG_SUSPEND)
&& (hdev->children[0])) {
hdev->otgstate &= ~USB_OTG_SUSPEND;
ret = set_port_feature(hdev, 1,
USB_PORT_FEAT_SUSPEND);
if (ret < 0) {
dev_err(hub_dev, "usb otg port suspend"
" fails, %d\n", ret);
break;
}
msleep(1);
ret = get_port_status(hdev, i, &portsts);
if (ret < 0) {
dev_err(hub_dev, "usb otg get port"
" status fails, %d\n", ret);
break;
}
portchange = le16_to_cpu(portsts.wPortChange);
if (portchange & USB_PORT_STAT_C_SUSPEND) {
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_SUSPEND);
}
break;
}
if (hdev->otgstate & USB_OTG_REMOTEWAKEUP) {
for (j = 1; j <= hub->descriptor->bNbrPorts;
j++) {
if (hdev->children[j - 1]) {
dev_dbg(hub_dev, "child"
" found at port %d\n", j);
ret = usb_control_msg(hdev->
children[j - 1],
usb_sndctrlpipe(hdev->
children[j - 1],
0),
USB_REQ_SET_FEATURE,
USB_RECIP_DEVICE,
USB_DEVICE_REMOTE_WAKEUP,
0, NULL,
0,
USB_CTRL_SET_TIMEOUT);
if (ret < 0) {
dev_err(hub_dev, "Port"
" %d doesn't support"
"remote wakeup\n", j);
} else {
dev_dbg(hub_dev, "Port"
" %d supports"
"remote wakeup\n", j);
}
ret = set_port_feature(hdev, j,
USB_PORT_FEAT_SUSPEND);
if (ret < 0) {
dev_err(hub_dev, "Port"
" %d NOT ABLE TO"
" SUSPEND\n", j);
} else {
dev_dbg(hub_dev, "Port"
" %d is ABLE TO"
" SUSPEND\n", j);
}
}
}
ret = usb_control_msg(hdev,
usb_sndctrlpipe(hdev, 0),
USB_REQ_SET_FEATURE,
USB_RECIP_DEVICE,
USB_DEVICE_REMOTE_WAKEUP,
0, NULL, 0,
USB_CTRL_SET_TIMEOUT);
if (ret < 0) {
dev_err(hub_dev, "HUB doesn't support"
" REMOTE WAKEUP\n");
} else {
dev_dbg(hub_dev, "HUB supports"
" REMOTE WAKEUP\n");
}
ret = 0;
msleep(10);
if (hdev->parent == hdev->bus->root_hub) {
if (hdev->hcd_suspend &&
hdev->hcd_priv) {
dev_dbg(hub_dev, "calling"
" suspend after remote wakeup"
" command is issued\n");
hdev->hcd_suspend(hdev->
hcd_priv);
}
if (hdev->otg_notif)
hdev->otg_notif(hdev->otgpriv,
PDC_POWERMANAGEMENT, 10);
}
}
if (hdev->otgstate & USB_OTG_WAKEUP_ALL) {
(void) usb_control_msg(hdev,
usb_sndctrlpipe(hdev, 0),
USB_REQ_CLEAR_FEATURE,
USB_RECIP_DEVICE,
USB_DEVICE_REMOTE_WAKEUP,
0, NULL, 0,
USB_CTRL_SET_TIMEOUT);
dev_dbg(hub_dev, "Hub CLEARED REMOTE WAKEUP\n");
for (j = 1; j <= hub->descriptor->bNbrPorts;
j++) {
if (hdev->children[j - 1]) {
dev_dbg(hub_dev, "PORT %d"
" SUSPEND IS CLEARD\n", j);
clear_port_feature(hdev, j,
USB_PORT_FEAT_C_SUSPEND);
msleep(50);
(void) usb_control_msg(hdev->
children[j - 1],
usb_sndctrlpipe(
hdev->children[j - 1],
0),
USB_REQ_CLEAR_FEATURE,
USB_RECIP_DEVICE,
USB_DEVICE_REMOTE_WAKEUP,
0, NULL,
0,
USB_CTRL_SET_TIMEOUT);
dev_dbg(hub_dev, "PORT %d "
"REMOTE WAKEUP IS "
"CLEARD\n", j);
msleep(10);
}
}
}
/*
* reset the state of otg device,
* regardless of otg device
*/
hdev->otgstate = 0;
#endif
if (test_bit(i, hub->busy_bits))
continue;
connect_change = test_bit(i, hub->change_bits);
@@ -3761,9 +3984,19 @@ static void hub_events(void)
HUB_BH_RESET_TIME, true);
}
if (connect_change)
if (connect_change) {
#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
if (hdev->parent == hdev->bus->root_hub)
if (hdev->otg_notif
&& (HostComplianceTest == 0))
hdev->otg_notif(hdev->otgpriv,
PDC_HOST_NOTIFY,
5);
portno = i;
#endif
hub_port_connect_change(hub, i,
portstatus, portchange);
}
} /* end for i */
/* deal with hub status changes */
@@ -3795,7 +4028,105 @@ static void hub_events(void)
"condition\n");
}
}
#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
/* if we have something on otg */
if (otgport) {
otgport = 0;
/* notify otg controller about it */
if (hdev->parent == hdev->bus->root_hub)
if (hdev->otg_notif)
hdev->otg_notif(hdev->otgpriv,
PDC_HOST_NOTIFY, 0);
}
if (HostComplianceTest && hdev->devnum > 1) {
/* TEST_SE0_NAK */
if (HostTest == 1) {
dev_info(hub_dev, "Testing for TEST_SE0_NAK\n");
ret = clear_port_feature(hdev, portno,
USB_PORT_FEAT_C_CONNECTION);
ret = set_port_feature(hdev, portno,
USB_PORT_FEAT_SUSPEND);
ret = set_port_feature(hdev, portno | 0x300,
USB_PORT_FEAT_TEST);
ret = get_port_status(hdev, portno,
&port_status);
}
/*TEST_J*/
if (HostTest == 2) {
dev_info(hub_dev, "Testing TEST_J\n");
ret = clear_port_feature(hdev, portno,
USB_PORT_FEAT_C_CONNECTION);
ret = set_port_feature(hdev, portno,
USB_PORT_FEAT_SUSPEND);
ret = set_port_feature(hdev, portno | 0x100,
USB_PORT_FEAT_TEST);
ret = get_port_status(hdev, portno,
&port_status);
}
if (HostTest == 3) {
dev_info(hub_dev, "Testing TEST_K\n");
ret = clear_port_feature(hdev, portno,
USB_PORT_FEAT_C_CONNECTION);
ret = set_port_feature(hdev, portno,
USB_PORT_FEAT_SUSPEND);
ret = set_port_feature(hdev, portno | 0x200,
USB_PORT_FEAT_TEST);
ret = get_port_status(hdev, portno,
&port_status);
}
if (HostTest == 4) {
dev_info(hub_dev, "Testing TEST_PACKET at Port"
" %d\n", portno);
ret = clear_port_feature(hdev, portno,
USB_PORT_FEAT_C_CONNECTION);
if (ret < 0)
dev_err(hub_dev, "Clear port feature"
" C_CONNECTION failed\n");
ret = set_port_feature(hdev, portno,
USB_PORT_FEAT_SUSPEND);
if (ret < 0)
dev_err(hub_dev, "Clear port feature"
" SUSPEND failed\n");
ret = set_port_feature(hdev, portno | 0x400,
USB_PORT_FEAT_TEST);
if (ret < 0)
dev_err(hub_dev, "Clear port feature"
" TEST failed\n");
ret = get_port_status(hdev, portno,
&port_status);
if (ret < 0)
dev_err(hub_dev, "Get port status"
" failed\n");
}
if (HostTest == 5) {
dev_info(hub_dev, "Testing TEST_FORCE_ENBLE\n");
ret = clear_port_feature(hdev, portno,
USB_PORT_FEAT_C_CONNECTION);
ret = set_port_feature(hdev, portno,
USB_PORT_FEAT_SUSPEND);
ret = set_port_feature(hdev, portno | 0x500,
USB_PORT_FEAT_TEST);
ret = get_port_status(hdev, portno,
&port_status);
}
if (HostTest == 6) {
dev_info(hub_dev, "Testing "
"HS_HOST_PORT_SUSPEND_RESUME\n");
ret = clear_port_feature(hdev, portno,
USB_PORT_FEAT_C_CONNECTION);
ret = set_port_feature(hdev, portno,
USB_PORT_FEAT_SUSPEND);
msleep(3000);
ret = clear_port_feature(hdev, portno,
USB_PORT_FEAT_SUSPEND);
HostTest = 0;
}
}
#endif
loop_autopm:
/* Balance the usb_autopm_get_interface() above */
usb_autopm_put_interface_no_suspend(intf);

View File

@@ -92,7 +92,30 @@ static int is_targeted(struct usb_device *dev)
if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
(id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
continue;
#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
/*Hub is targeted device,so code execution should reach here */
if (USB_CLASS_HUB == dev->descriptor.bDeviceClass) {
/* count the tiers and if it is more than 6, return 0 */
unsigned char tier = 0;
struct usb_device *root_hub;
root_hub = dev->bus->root_hub;
while ((dev->parent != NULL) && /* root hub not count */
(dev->parent != root_hub) &&
(tier != 6)) {/* interal hub not count */
tier++;
dev = dev->parent;
}
if (tier == 6) {
dev_err(&dev->dev, "5 tier of hubs reached,"
" newly added hub will not be"
" supported!\n");
hub_tier = 1;
return 0;
}
}
#endif
return 1;
}

View File

@@ -372,6 +372,16 @@ struct usb_bus {
* limit. Because the arrays need to add a bit for hub status data, we
* do 31, so plus one evens out to four bytes.
*/
#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
#define USB_OTG_SUSPEND 0x1
#define USB_OTG_ENUMERATE 0x2
#define USB_OTG_DISCONNECT 0x4
#define USB_OTG_RESUME 0x8
#define USB_OTG_REMOTEWAKEUP 0x10
#define USB_OTG_WAKEUP_ALL 0x20
#endif
#define USB_MAXCHILDREN (31)
struct usb_tt;
@@ -500,6 +510,18 @@ struct usb_device {
struct dentry *usbfs_dentry;
#endif
#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
/*otg add ons */
u8 otgdevice; /*device is otg type */
/*otg states from otg driver, suspend, enumerate, disconnect */
u8 otgstate;
void *otgpriv;
void (*otg_notif) (void *otg_priv,
unsigned long notif, unsigned long data);
void *hcd_priv;
void (*hcd_suspend) (void *hcd_priv);
#endif
int maxchild;
struct usb_device **children;