port.c 4.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * usb port device code
 *
 * Copyright (C) 2012 Intel Corp
 *
 * Author: Lan Tianyu <tianyu.lan@intel.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 */

19
#include <linux/slab.h>
20
#include <linux/pm_qos.h>
21

22 23
#include "hub.h"

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
static const struct attribute_group *port_dev_group[];

static ssize_t show_port_connect_type(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct usb_port *port_dev = to_usb_port(dev);
	char *result;

	switch (port_dev->connect_type) {
	case USB_PORT_CONNECT_TYPE_HOT_PLUG:
		result = "hotplug";
		break;
	case USB_PORT_CONNECT_TYPE_HARD_WIRED:
		result = "hardwired";
		break;
	case USB_PORT_NOT_USED:
		result = "not used";
		break;
	default:
		result = "unknown";
		break;
	}

	return sprintf(buf, "%s\n", result);
}
static DEVICE_ATTR(connect_type, S_IRUGO, show_port_connect_type,
		NULL);

static struct attribute *port_dev_attrs[] = {
	&dev_attr_connect_type.attr,
	NULL,
};

static struct attribute_group port_dev_attr_grp = {
	.attrs = port_dev_attrs,
};

static const struct attribute_group *port_dev_group[] = {
	&port_dev_attr_grp,
	NULL,
};

66 67 68 69
static void usb_port_device_release(struct device *dev)
{
	struct usb_port *port_dev = to_usb_port(dev);

70
	dev_pm_qos_hide_flags(dev);
71 72 73
	kfree(port_dev);
}

74 75 76 77 78 79
#ifdef CONFIG_USB_SUSPEND
static int usb_port_runtime_resume(struct device *dev)
{
	struct usb_port *port_dev = to_usb_port(dev);
	struct usb_device *hdev = to_usb_device(dev->parent->parent);
	struct usb_interface *intf = to_usb_interface(dev->parent);
80 81
	struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
	int port1 = port_dev->portnum;
82 83
	int retval;

84 85 86
	if (!hub)
		return -EINVAL;

87
	usb_autopm_get_interface(intf);
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
	set_bit(port1, hub->busy_bits);

	retval = usb_hub_set_port_power(hdev, port1, true);
	if (port_dev->child && !retval) {
		/*
		 * Wait for usb hub port to be reconnected in order to make
		 * the resume procedure successful.
		 */
		retval = hub_port_debounce_be_connected(hub, port1);
		if (retval < 0) {
			dev_dbg(&port_dev->dev, "can't get reconnection after setting port  power on, status %d\n",
					retval);
			goto out;
		}
		usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);

		/* Set return value to 0 if debounce successful */
		retval = 0;
	}

out:
	clear_bit(port1, hub->busy_bits);
110 111 112 113 114 115 116 117 118
	usb_autopm_put_interface(intf);
	return retval;
}

static int usb_port_runtime_suspend(struct device *dev)
{
	struct usb_port *port_dev = to_usb_port(dev);
	struct usb_device *hdev = to_usb_device(dev->parent->parent);
	struct usb_interface *intf = to_usb_interface(dev->parent);
119 120
	struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
	int port1 = port_dev->portnum;
121 122
	int retval;

123 124 125
	if (!hub)
		return -EINVAL;

126 127 128 129 130
	if (dev_pm_qos_flags(&port_dev->dev, PM_QOS_FLAG_NO_POWER_OFF)
			== PM_QOS_FLAGS_ALL)
		return -EAGAIN;

	usb_autopm_get_interface(intf);
131 132 133 134 135
	set_bit(port1, hub->busy_bits);
	retval = usb_hub_set_port_power(hdev, port1, false);
	usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
	usb_clear_port_feature(hdev, port1,	USB_PORT_FEAT_C_ENABLE);
	clear_bit(port1, hub->busy_bits);
136 137 138 139 140 141 142 143 144 145 146 147 148
	usb_autopm_put_interface(intf);
	return retval;
}
#endif

static const struct dev_pm_ops usb_port_pm_ops = {
#ifdef CONFIG_USB_SUSPEND
	.runtime_suspend =	usb_port_runtime_suspend,
	.runtime_resume =	usb_port_runtime_resume,
	.runtime_idle =		pm_generic_runtime_idle,
#endif
};

149 150 151
struct device_type usb_port_device_type = {
	.name =		"usb_port",
	.release =	usb_port_device_release,
152
	.pm =		&usb_port_pm_ops,
153 154 155 156 157 158 159 160 161 162 163 164 165 166
};

int usb_hub_create_port_device(struct usb_hub *hub, int port1)
{
	struct usb_port *port_dev = NULL;
	int retval;

	port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
	if (!port_dev) {
		retval = -ENOMEM;
		goto exit;
	}

	hub->ports[port1 - 1] = port_dev;
167
	port_dev->portnum = port1;
168
	port_dev->power_is_on = true;
169
	port_dev->dev.parent = hub->intfdev;
170
	port_dev->dev.groups = port_dev_group;
171 172 173 174 175 176 177
	port_dev->dev.type = &usb_port_device_type;
	dev_set_name(&port_dev->dev, "port%d", port1);

	retval = device_register(&port_dev->dev);
	if (retval)
		goto error_register;

178
	pm_runtime_set_active(&port_dev->dev);
179 180 181 182 183 184 185 186 187

	/* It would be dangerous if user space couldn't
	 * prevent usb device from being powered off. So don't
	 * enable port runtime pm if failed to expose port's pm qos.
	 */
	if (!dev_pm_qos_expose_flags(&port_dev->dev,
			PM_QOS_FLAG_NO_POWER_OFF))
		pm_runtime_enable(&port_dev->dev);

188
	device_enable_async_suspend(&port_dev->dev);
189 190 191 192 193 194 195 196 197 198 199 200 201 202
	return 0;

error_register:
	put_device(&port_dev->dev);
exit:
	return retval;
}

void usb_hub_remove_port_device(struct usb_hub *hub,
				       int port1)
{
	device_unregister(&hub->ports[port1 - 1]->dev);
}