static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) { struct at91_usbh_data *pdata = pdev->dev.platform_data; int i; if (pdata) { for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { if (!gpio_is_valid(pdata->vbus_pin[i])) continue; gpio_request(pdata->vbus_pin[i], "ohci_vbus"); ohci_at91_usb_set_power(pdata, i, 1); } for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) { int ret; if (!gpio_is_valid(pdata->overcurrent_pin[i])) continue; gpio_request(pdata->overcurrent_pin[i], "ohci_overcurrent"); ret = request_irq(gpio_to_irq(pdata->overcurrent_pin[i]), ohci_hcd_at91_overcurrent_irq, IRQF_SHARED, "ohci_overcurrent", pdev); if (ret) { gpio_free(pdata->overcurrent_pin[i]); dev_warn(& pdev->dev, "cannot get GPIO IRQ for overcurrent\n"); } } } device_init_wakeup(&pdev->dev, 1); return usb_hcd_at91_probe(&ohci_at91_hc_driver, pdev); }
static int __devexit ohci_hcd_at91_drv_remove(struct platform_device *pdev) { struct at91_usbh_data *pdata = pdev->dev.platform_data; int i; if (pdata) { at91_for_each_port(i) { if (!gpio_is_valid(pdata->vbus_pin[i])) continue; ohci_at91_usb_set_power(pdata, i, 0); gpio_free(pdata->vbus_pin[i]); } at91_for_each_port(i) { if (!gpio_is_valid(pdata->overcurrent_pin[i])) continue; free_irq(gpio_to_irq(pdata->overcurrent_pin[i]), pdev); gpio_free(pdata->overcurrent_pin[i]); } } device_init_wakeup(&pdev->dev, 0); usb_hcd_at91_remove(platform_get_drvdata(pdev), pdev); return 0; }
static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data) { struct platform_device *pdev = data; struct at91_usbh_data *pdata = pdev->dev.platform_data; int val, gpio, port; /* From the GPIO notifying the over-current situation, find * out the corresponding port */ gpio = irq_to_gpio(irq); for (port = 0; port < ARRAY_SIZE(pdata->overcurrent_pin); port++) { if (pdata->overcurrent_pin[port] == gpio) break; } if (port == ARRAY_SIZE(pdata->overcurrent_pin)) { dev_err(& pdev->dev, "overcurrent interrupt from unknown GPIO\n"); return IRQ_HANDLED; } val = gpio_get_value(gpio); /* When notified of an over-current situation, disable power on the corresponding port, and mark this port in over-current. */ if (! val) { ohci_at91_usb_set_power(pdata, port, 0); pdata->overcurrent_status[port] = 1; pdata->overcurrent_changed[port] = 1; } dev_dbg(& pdev->dev, "overcurrent situation %s\n", val ? "exited" : "notified"); return IRQ_HANDLED; }
static int ohci_hcd_at91_drv_remove(struct platform_device *pdev) { struct at91_usbh_data *pdata = pdev->dev.platform_data; int i; if (pdata) { for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { if (pdata->vbus_pin[i] <= 0) continue; ohci_at91_usb_set_power(pdata, i, 0); gpio_free(pdata->vbus_pin[i]); } for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) { if (pdata->overcurrent_pin[i] <= 0) continue; free_irq(gpio_to_irq(pdata->overcurrent_pin[i]), pdev); gpio_free(pdata->overcurrent_pin[i]); } } device_init_wakeup(&pdev->dev, 0); usb_hcd_at91_remove(platform_get_drvdata(pdev), pdev); return 0; }
static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data) { struct platform_device *pdev = data; struct at91_usbh_data *pdata = dev_get_platdata(&pdev->dev); int val, gpio, port; /* From the GPIO notifying the over-current situation, find * out the corresponding port */ at91_for_each_port(port) { if (gpio_is_valid(pdata->overcurrent_pin[port]) && gpio_to_irq(pdata->overcurrent_pin[port]) == irq) { gpio = pdata->overcurrent_pin[port]; break; } } if (port == AT91_MAX_USBH_PORTS) { dev_err(& pdev->dev, "overcurrent interrupt from unknown GPIO\n"); return IRQ_HANDLED; } val = gpio_get_value(gpio); /* When notified of an over-current situation, disable power on the corresponding port, and mark this port in over-current. */ if (!val) { ohci_at91_usb_set_power(pdata, port, 0); pdata->overcurrent_status[port] = 1; pdata->overcurrent_changed[port] = 1; } dev_dbg(& pdev->dev, "overcurrent situation %s\n", val ? "exited" : "notified"); return IRQ_HANDLED; }
static int __devinit ohci_hcd_at91_drv_probe(struct platform_device *pdev) { struct at91_usbh_data *pdata; int i; int gpio; int ret; ret = ohci_at91_of_init(pdev); if (ret) return ret; pdata = pdev->dev.platform_data; if (pdata) { at91_for_each_port(i) { if (!gpio_is_valid(pdata->vbus_pin[i])) continue; gpio = pdata->vbus_pin[i]; ret = gpio_request(gpio, "ohci_vbus"); if (ret) { dev_err(&pdev->dev, "can't request vbus gpio %d\n", gpio); continue; } ret = gpio_direction_output(gpio, !pdata->vbus_pin_active_low[i]); if (ret) { dev_err(&pdev->dev, "can't put vbus gpio %d as output %d\n", gpio, !pdata->vbus_pin_active_low[i]); gpio_free(gpio); continue; } ohci_at91_usb_set_power(pdata, i, 1); } at91_for_each_port(i) { if (!gpio_is_valid(pdata->overcurrent_pin[i])) continue; gpio = pdata->overcurrent_pin[i]; ret = gpio_request(gpio, "ohci_overcurrent"); if (ret) { dev_err(&pdev->dev, "can't request overcurrent gpio %d\n", gpio); continue; } ret = gpio_direction_input(gpio); if (ret) { dev_err(&pdev->dev, "can't configure overcurrent gpio %d as input\n", gpio); gpio_free(gpio); continue; } ret = request_irq(gpio_to_irq(gpio), ohci_hcd_at91_overcurrent_irq, IRQF_SHARED, "ohci_overcurrent", pdev); if (ret) { gpio_free(gpio); dev_err(&pdev->dev, "can't get gpio IRQ for overcurrent\n"); } } } device_init_wakeup(&pdev->dev, 1); return usb_hcd_at91_probe(&ohci_at91_hc_driver, pdev); }
/* * Look at the control requests to the root hub and see if we need to override. */ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct at91_usbh_data *pdata = hcd->self.controller->platform_data; struct usb_hub_descriptor *desc; int ret = -EINVAL; u32 *data = (u32 *)buf; dev_dbg(hcd->self.controller, "ohci_at91_hub_control(%p,0x%04x,0x%04x,0x%04x,%p,%04x)\n", hcd, typeReq, wValue, wIndex, buf, wLength); wIndex--; switch (typeReq) { case SetPortFeature: if (wValue == USB_PORT_FEAT_POWER) { dev_dbg(hcd->self.controller, "SetPortFeat: POWER\n"); if (valid_port(wIndex)) { ohci_at91_usb_set_power(pdata, wIndex, 1); ret = 0; } goto out; } break; case ClearPortFeature: switch (wValue) { case USB_PORT_FEAT_C_OVER_CURRENT: dev_dbg(hcd->self.controller, "ClearPortFeature: C_OVER_CURRENT\n"); if (valid_port(wIndex)) { pdata->overcurrent_changed[wIndex] = 0; pdata->overcurrent_status[wIndex] = 0; } goto out; case USB_PORT_FEAT_OVER_CURRENT: dev_dbg(hcd->self.controller, "ClearPortFeature: OVER_CURRENT\n"); if (valid_port(wIndex)) pdata->overcurrent_status[wIndex] = 0; goto out; case USB_PORT_FEAT_POWER: dev_dbg(hcd->self.controller, "ClearPortFeature: POWER\n"); if (valid_port(wIndex)) { ohci_at91_usb_set_power(pdata, wIndex, 0); return 0; } } break; } ret = ohci_hub_control(hcd, typeReq, wValue, wIndex + 1, buf, wLength); if (ret) goto out; switch (typeReq) { case GetHubDescriptor: /* update the hub's descriptor */ desc = (struct usb_hub_descriptor *)buf; dev_dbg(hcd->self.controller, "wHubCharacteristics 0x%04x\n", desc->wHubCharacteristics); /* remove the old configurations for power-switching, and * over-current protection, and insert our new configuration */ desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_LPSM); desc->wHubCharacteristics |= cpu_to_le16(0x0001); if (pdata->overcurrent_supported) { desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_OCPM); desc->wHubCharacteristics |= cpu_to_le16(0x0008|0x0001); } dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n", desc->wHubCharacteristics); return ret; case GetPortStatus: /* check port status */ dev_dbg(hcd->self.controller, "GetPortStatus(%d)\n", wIndex); if (valid_port(wIndex)) { if (!ohci_at91_usb_get_power(pdata, wIndex)) *data &= ~cpu_to_le32(RH_PS_PPS); if (pdata->overcurrent_changed[wIndex]) *data |= cpu_to_le32(RH_PS_OCIC); if (pdata->overcurrent_status[wIndex]) *data |= cpu_to_le32(RH_PS_POCI); } } out: return ret; }