port.c 5.1 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
	usb_acpi_unregister_power_resources(dev);
72 73 74
	kfree(port_dev);
}

75 76 77 78 79 80
#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);
81 82
	struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
	int port1 = port_dev->portnum;
83 84
	int retval;

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

88
	usb_autopm_get_interface(intf);
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
	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);
111 112 113 114 115 116 117 118 119
	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);
120 121
	struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
	int port1 = port_dev->portnum;
122 123
	int retval;

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

127 128 129 130 131
	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);
132 133 134 135 136
	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);
137 138 139 140 141 142 143 144 145 146 147 148 149
	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
};

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

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;
168
	port_dev->portnum = port1;
169
	port_dev->power_is_on = true;
170
	port_dev->dev.parent = hub->intfdev;
171
	port_dev->dev.groups = port_dev_group;
172 173 174 175 176 177 178
	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;

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

	/* 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);

189

190 191 192 193
	retval = usb_acpi_register_power_resources(&port_dev->dev);
	if (retval && retval != -ENODEV)
		dev_warn(&port_dev->dev, "the port can't register its ACPI power resource.\n");

194 195 196 197 198 199 200 201 202 203 204 205 206 207
	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);
}