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 70 71 72
static void usb_port_device_release(struct device *dev)
{
	struct usb_port *port_dev = to_usb_port(dev);

	kfree(port_dev);
}

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

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

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

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

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

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

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

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

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

187
	device_enable_async_suspend(&port_dev->dev);
188 189 190 191 192 193 194 195 196 197 198 199 200 201
	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);
}