123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- /*
- * 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.
- *
- */
- #include <linux/slab.h>
- #include <linux/pm_qos.h>
- #include "hub.h"
- 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,
- };
- static void usb_port_device_release(struct device *dev)
- {
- struct usb_port *port_dev = to_usb_port(dev);
- dev_pm_qos_hide_flags(dev);
- kfree(port_dev);
- }
- #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);
- struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
- int port1 = port_dev->portnum;
- int retval;
- if (!hub)
- return -EINVAL;
- usb_autopm_get_interface(intf);
- 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);
- 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);
- struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
- int port1 = port_dev->portnum;
- int retval;
- if (!hub)
- return -EINVAL;
- 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);
- 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);
- 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
- };
- struct device_type usb_port_device_type = {
- .name = "usb_port",
- .release = usb_port_device_release,
- .pm = &usb_port_pm_ops,
- };
- 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;
- port_dev->portnum = port1;
- port_dev->power_is_on = true;
- port_dev->dev.parent = hub->intfdev;
- port_dev->dev.groups = port_dev_group;
- 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;
- pm_runtime_set_active(&port_dev->dev);
- /* 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);
- device_enable_async_suspend(&port_dev->dev);
- 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);
- }
|