static void ush_hsic_port_disable(void) { hsic_enable = 0; if ((hsic.modem_dev) && (hsic.autosuspend_enable != 0)) { dev_dbg(&pci_dev->dev, "Disable auto suspend in port disable\n"); usb_disable_autosuspend(hsic.modem_dev); usb_disable_autosuspend(hsic.rh_dev); hsic.autosuspend_enable = 0; } if (hsic.rh_dev) { if (hsic.autosuspend_enable != 0) { dev_dbg(&pci_dev->dev, "Disable auto suspend in port disable\n"); usb_disable_autosuspend(hsic.rh_dev); hsic.autosuspend_enable = 0; } clear_port_feature(hsic.rh_dev, HSIC_USH_PORT, USB_PORT_FEAT_POWER); usb_enable_autosuspend(hsic.rh_dev); hsic.autosuspend_enable = 1; } s3_wake_unlock(); }
static void ush_hsic_port_disable(struct pci_dev *pdev) { printk(KERN_ERR "%s---->\n", __func__); #if 1 if (hsic.modem_dev) { struct usb_device *hdev; dev_dbg(&pci_dev->dev, "%s----> disconnect modem\n", __func__); hdev = hsic.modem_dev->parent; usb_disable_autosuspend(hsic.modem_dev); usb_disable_autosuspend(hsic.rh_dev); if (hdev->children[HSIC_USH_PORT - 1] == hsic.modem_dev) { printk(KERN_ERR "%s----> usb disconnect\n", __func__); usb_disconnect(&hsic.modem_dev); hdev->children[HSIC_USH_PORT - 1] = NULL; } } #endif if (hsic.rh_dev) { dev_dbg(&pci_dev->dev, "%s----> disable port\n", __func__); printk(KERN_ERR "%s----> disable PP\n", __func__); clear_port_feature(hsic.rh_dev, HSIC_USH_PORT, USB_PORT_FEAT_POWER); } hsic.hsic_stopped = 1; hsic_enable = 0; }
// Disable port static void usb_hub_disconnect(struct usbhub_s *hub, u32 port) { int ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE); if (ret) dprintf(1, "Failure on hub port %d disconnect\n", port); }
/* return: -1 on error, 0 on success, 1 on disconnect. */ static int hub_port_reset(struct usb_device *hub, int port, struct usb_device *dev, unsigned int delay) { int i, status; /* Reset the port */ for (i = 0; i < HUB_RESET_TRIES; i++) { set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); /* return on disconnect or reset */ status = hub_port_wait_reset(hub, port, dev, delay); if (status != -1) { clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_RESET); dev->state = status ? USB_STATE_NOTATTACHED : USB_STATE_DEFAULT; return status; } dev_dbg (hubdev (hub), "port %d not enabled, trying reset again...\n", port + 1); delay = HUB_LONG_RESET_TIME; } dev_err (hubdev (hub), "Cannot enable port %i. Maybe the USB cable is bad?\n", port + 1); return -1; }
int hub_port_disable(struct usb_device *hub, int port) { int ret; ret = clear_port_feature(hub, port + 1, USB_PORT_FEAT_ENABLE); if (ret) dev_err(hubdev(hub), "cannot disable port %d (err = %d)\n", port + 1, ret); return ret; }
/** * xhci_ush_pci_shutdown - shutdown host controller * @dev: USB Host Controller being shutdown */ static void xhci_ush_pci_shutdown(struct pci_dev *dev) { struct usb_hcd *hcd; hcd = pci_get_drvdata(dev); if (!hcd) return; if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && hcd->driver->shutdown) { disable_irq(gpio_to_irq(hsic.aux_gpio)); disable_irq(gpio_to_irq(hsic.wakeup_gpio)); hcd->driver->shutdown(hcd); if (hsic.rh_dev) { dev_dbg(&pci_dev->dev, "%s: disable port\n", __func__); clear_port_feature(hsic.rh_dev, HSIC_USH_PORT, USB_PORT_FEAT_POWER); } pci_disable_device(dev); } }
/* return: -1 on error, 0 on success, 1 on disconnect. */ static int hub_port_debounce(struct usb_device *hub, int port) { int ret; int delay_time, stable_count; u16 portchange, portstatus; unsigned connection; connection = 0; stable_count = 0; for (delay_time = 0; delay_time < HUB_DEBOUNCE_TIMEOUT; delay_time += HUB_DEBOUNCE_STEP) { wait_ms(HUB_DEBOUNCE_STEP); ret = hub_port_status(hub, port, &portstatus, &portchange); if (ret < 0) return -1; if ((portstatus & USB_PORT_STAT_CONNECTION) == connection) { if (connection) { if (++stable_count == HUB_DEBOUNCE_STABLE) break; } } else { stable_count = 0; } connection = portstatus & USB_PORT_STAT_CONNECTION; if ((portchange & USB_PORT_STAT_C_CONNECTION)) { clear_port_feature(hub, port+1, USB_PORT_FEAT_C_CONNECTION); } } /* XXX Replace this with dbg() when 2.6 is about to ship. */ dev_dbg (hubdev (hub), "debounce: port %d: delay %dms stable %d status 0x%x\n", port + 1, delay_time, stable_count, portstatus); return ((portstatus&USB_PORT_STAT_CONNECTION)) ? 0 : 1; }
static void hub_port_connect_change(struct usb_hub *hubstate, int port, u16 portstatus, u16 portchange) { struct usb_device *hub = interface_to_usbdev(hubstate->intf); struct usb_device *dev; unsigned int delay = HUB_SHORT_RESET_TIME; int i; dev_dbg (&hubstate->intf->dev, "port %d, status %x, change %x, %s\n", port + 1, portstatus, portchange, portspeed (portstatus)); /* Clear the connection change status */ clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION); /* Disconnect any existing devices under this port */ if (hub->children[port]) usb_disconnect(&hub->children[port]); /* Return now if nothing is connected */ if (!(portstatus & USB_PORT_STAT_CONNECTION)) { if (portstatus & USB_PORT_STAT_ENABLE) hub_port_disable(hub, port); return; } if (hub_port_debounce(hub, port)) { dev_err (&hubstate->intf->dev, "connect-debounce failed, port %d disabled\n", port+1); hub_port_disable(hub, port); return; } /* Some low speed devices have problems with the quick delay, so */ /* be a bit pessimistic with those devices. RHbug #23670 */ if (portstatus & USB_PORT_STAT_LOW_SPEED) delay = HUB_LONG_RESET_TIME; down(&usb_address0_sem); for (i = 0; i < HUB_PROBE_TRIES; i++) { struct usb_device *pdev; int len; /* Allocate a new device struct */ dev = usb_alloc_dev(hub, hub->bus); if (!dev) { dev_err (&hubstate->intf->dev, "couldn't allocate usb_device\n"); break; } hub->children[port] = dev; dev->state = USB_STATE_POWERED; /* Reset the device, and detect its speed */ if (hub_port_reset(hub, port, dev, delay)) { usb_put_dev(dev); break; } /* Find a new address for it */ usb_connect(dev); /* Set up TT records, if needed */ if (hub->tt) { dev->tt = hub->tt; dev->ttport = hub->ttport; } else if (dev->speed != USB_SPEED_HIGH && hub->speed == USB_SPEED_HIGH) { dev->tt = &hubstate->tt; dev->ttport = port + 1; } /* Save readable and stable topology id, distinguishing * devices by location for diagnostics, tools, etc. The * string is a path along hub ports, from the root. Each * device's id will be stable until USB is re-cabled, and * hubs are often labeled with these port numbers. * * Initial size: ".NN" times five hubs + NUL = 16 bytes max * (quite rare, since most hubs have 4-6 ports). */ pdev = dev->parent; if (pdev->devpath [0] != '0') /* parent not root? */ len = snprintf (dev->devpath, sizeof dev->devpath, "%s.%d", pdev->devpath, port + 1); /* root == "0", root port 2 == "2", port 3 that hub "2.3" */ else len = snprintf (dev->devpath, sizeof dev->devpath, "%d", port + 1); if (len == sizeof dev->devpath) dev_err (&hubstate->intf->dev, "devpath size! usb/%03d/%03d path %s\n", dev->bus->busnum, dev->devnum, dev->devpath); dev_info (&hubstate->intf->dev, "new USB device on port %d, assigned address %d\n", port + 1, dev->devnum); /* put the device in the global device tree. the hub port * is the "bus_id"; hubs show in hierarchy like bridges */ dev->dev.parent = dev->parent->dev.parent->parent; /* Run it through the hoops (find a driver, etc) */ if (!usb_new_device(dev, &hub->dev)) goto done; /* Free the configuration if there was an error */ usb_put_dev(dev); /* Switch to a long reset time */ delay = HUB_LONG_RESET_TIME; } hub->children[port] = NULL; hub_port_disable(hub, port); done: up(&usb_address0_sem); }
static void hub_events(void) { unsigned long flags; struct list_head *tmp; struct usb_device *dev; struct usb_hub *hub; u16 hubstatus; u16 hubchange; u16 portstatus; u16 portchange; int i, ret; int m=0; /* * We restart the list every time to avoid a deadlock with * deleting hubs downstream from this one. This should be * safe since we delete the hub from the event list. * Not the most efficient, but avoids deadlocks. */ while (m<5) { m++; spin_lock_irqsave(&hub_event_lock, flags); if (list_empty(&hub_event_list)) break; /* Grab the next entry from the beginning of the list */ tmp = hub_event_list.next; hub = list_entry(tmp, struct usb_hub, event_list); dev = interface_to_usbdev(hub->intf); list_del_init(tmp); if (unlikely(down_trylock(&hub->khubd_sem))) BUG(); /* never blocks, we were on list */ spin_unlock_irqrestore(&hub_event_lock, flags); if (hub->error) { dev_dbg (&hub->intf->dev, "resetting for error %d\n", hub->error); if (hub_reset(hub)) { dev_dbg (&hub->intf->dev, "can't reset; disconnecting\n"); up(&hub->khubd_sem); hub_start_disconnect(dev); continue; } hub->nerrors = 0; hub->error = 0; } for (i = 0; i < hub->descriptor->bNbrPorts; i++) { ret = hub_port_status(dev, i, &portstatus, &portchange); if (ret < 0) { continue; } if (portchange & USB_PORT_STAT_C_CONNECTION) { hub_port_connect_change(hub, i, portstatus, portchange); } else if (portchange & USB_PORT_STAT_C_ENABLE) { dev_dbg (hubdev (dev), "port %d enable change, status %x\n", i + 1, portstatus); clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); /* * EM interference sometimes causes badly * shielded USB devices to be shutdown by * the hub, this hack enables them again. * Works at least with mouse driver. */ if (!(portstatus & USB_PORT_STAT_ENABLE) && (portstatus & USB_PORT_STAT_CONNECTION) && (dev->children[i])) { dev_err (&hub->intf->dev, "port %i " "disabled by hub (EMI?), " "re-enabling...", i + 1); hub_port_connect_change(hub, i, portstatus, portchange); } } if (portchange & USB_PORT_STAT_C_SUSPEND) { dev_dbg (&hub->intf->dev, "suspend change on port %d\n", i + 1); clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_SUSPEND); } if (portchange & USB_PORT_STAT_C_OVERCURRENT) { dev_err (&hub->intf->dev, "over-current change on port %d\n", i + 1); clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT); hub_power_on(hub); } if (portchange & USB_PORT_STAT_C_RESET) { dev_dbg (&hub->intf->dev, "reset change on port %d\n", i + 1); clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); } } /* end for i */ /* deal with hub status changes */ if (hub_hub_status(hub, &hubstatus, &hubchange) < 0) dev_err (&hub->intf->dev, "get_hub_status failed\n"); else { if (hubchange & HUB_CHANGE_LOCAL_POWER) { dev_dbg (&hub->intf->dev, "power change\n"); clear_hub_feature(dev, C_HUB_LOCAL_POWER); } if (hubchange & HUB_CHANGE_OVERCURRENT) { dev_dbg (&hub->intf->dev, "overcurrent change\n"); wait_ms(500); /* Cool down */ clear_hub_feature(dev, C_HUB_OVER_CURRENT); hub_power_on(hub); } } up(&hub->khubd_sem); } /* end while (1) */ spin_unlock_irqrestore(&hub_event_lock, flags); }