Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Documentation/devicetree/bindings/usb/atmel-usb.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,20 @@ Required properties:
- atmel,oc-gpio: If present, specifies a gpio that needs to be
activated for the overcurrent detection.

Optional properties:
- id-gpio: If present, specifies a gpio used for OTG "like" detection.
This is only applicable to devices that share a transceiver between
host port A and the device port. Both OHCI and UDC nodes should have
this property.

usb0: ohci@00500000 {
compatible = "atmel,at91rm9200-ohci", "usb-ohci";
reg = <0x00500000 0x100000>;
clocks = <&uhphs_clk>, <&uhphs_clk>, <&uhpck>;
clock-names = "ohci_clk", "hclk", "uhpck";
interrupts = <20 4>;
num-ports = <2>;
id-gpio = <&pioE, 26, 0>;
};

EHCI
Expand Down Expand Up @@ -93,6 +100,10 @@ Required properties:
Optional properties:
- atmel,vbus-gpio: If present, specifies a gpio that allows to detect whether
vbus is present (USB is connected).
- id-gpio: If present, specifies a gpio used for OTG "like" detection.
This is only applicable to devices that share a transceiver between
host port A and the device port. Both OHCI and UDC nodes should have
this property.

Required child node properties:
- name: Name of the endpoint.
Expand All @@ -112,6 +123,7 @@ usb2: gadget@fff78000 {
clocks = <&utmi>, <&udphs_clk>;
clock-names = "hclk", "pclk";
atmel,vbus-gpio = <&pioB 19 0>;
id-gpio = <&pioE, 26, 0>;

ep@0 {
reg = <0>;
Expand Down
33 changes: 30 additions & 3 deletions drivers/usb/gadget/udc/atmel_usba_udc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1945,6 +1945,20 @@ static irqreturn_t usba_vbus_irq_thread(int irq, void *devid)
/* debounce */
udelay(10);

/* If ID pin is pulled low, configure USB port as host */
if (gpio_is_valid(udc->id_pin)) {
if (!(gpio_get_value(udc->id_pin))) {
udc->id_prev = 0;
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
return IRQ_HANDLED;
}

if (udc->id_prev != gpio_get_value(udc->id_pin)) {
udc->id_prev = 1;
return IRQ_HANDLED;
}
}

mutex_lock(&udc->vbus_mutex);

vbus = vbus_is_present(udc);
Expand Down Expand Up @@ -1983,10 +1997,19 @@ static int atmel_usba_start(struct usb_gadget *gadget,

/* If Vbus is present, enable the controller and wait for reset */
udc->vbus_prev = vbus_is_present(udc);
if (udc->vbus_prev) {

if (gpio_is_valid(udc->id_pin)) {
if ((udc->vbus_prev) && gpio_get_value(udc->id_pin)) {
ret = usba_start(udc);
if (ret)
goto err;
if (ret)
goto err;
}
} else {
if (udc->vbus_prev) {
ret = usba_start(udc);
if (ret)
goto err;
}
}

mutex_unlock(&udc->vbus_mutex);
Expand Down Expand Up @@ -2082,6 +2105,8 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
&flags);
udc->vbus_pin_inverted = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0;

udc->id_pin = of_get_named_gpio_flags(np, "id-gpio", 0, &flags);

if (fifo_mode == 0) {
pp = NULL;
while ((pp = of_get_next_child(np, pp)))
Expand Down Expand Up @@ -2234,6 +2259,7 @@ static struct usba_ep * usba_udc_pdata(struct platform_device *pdev,

udc->vbus_pin = pdata->vbus_pin;
udc->vbus_pin_inverted = pdata->vbus_pin_inverted;
udc->id_pin = pdata->id_pin;
udc->num_ep = pdata->num_ep;

INIT_LIST_HEAD(&eps[0].ep.ep_list);
Expand Down Expand Up @@ -2309,6 +2335,7 @@ static int usba_udc_probe(struct platform_device *pdev)
udc->pclk = pclk;
udc->hclk = hclk;
udc->vbus_pin = -ENODEV;
udc->id_pin = -ENODEV;

ret = -ENOMEM;
udc->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/gadget/udc/atmel_usba_udc.h
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,8 @@ struct usba_udc {

u16 test_mode;
int vbus_prev;
int id_prev;
int id_pin;

u32 int_enb_cache;

Expand Down
58 changes: 58 additions & 0 deletions drivers/usb/host/ohci-at91.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ struct at91_usbh_data {
u8 vbus_pin_active_low[AT91_MAX_USBH_PORTS];
u8 overcurrent_status[AT91_MAX_USBH_PORTS];
u8 overcurrent_changed[AT91_MAX_USBH_PORTS];
int id_gpio;
};

struct ohci_at91_priv {
Expand Down Expand Up @@ -508,6 +509,29 @@ static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data)
return IRQ_HANDLED;
}

static irqreturn_t ohci_at91_otg_irq(int irq, void *data)
{
struct platform_device *pdev = data;
struct at91_usbh_data *pdata = dev_get_platdata(&pdev->dev);

/* debounce */
mdelay(10);

/* OTG "like" connector can only be on port A as it shares a
transceiver with the UDP, so vbus_pin index is 0 */
if (gpio_is_valid(pdata->id_gpio)) {
if (gpio_get_value(pdata->id_gpio)) {
/* id pin is high, we are not a host so set VBUS low */
gpio_direction_output(pdata->vbus_pin[0], 1);
} else {
/* id pin is low, we are now a host, set VBUS hi */
gpio_direction_output(pdata->vbus_pin[0], 0);
}
}

return IRQ_HANDLED;
}

static const struct of_device_id at91_ohci_dt_ids[] = {
{ .compatible = "atmel,at91rm9200-ohci" },
{ /* sentinel */ }
Expand Down Expand Up @@ -557,6 +581,7 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)

gpio = of_get_named_gpio_flags(np, "atmel,vbus-gpio", i,
&flags);

pdata->vbus_pin[i] = gpio;
if (!gpio_is_valid(gpio))
continue;
Expand Down Expand Up @@ -619,6 +644,34 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
}
}

/* Get ID pin (if present) for OTG "like" detection */
pdata->id_gpio = of_get_named_gpio_flags(np, "id-gpio", 0, &flags);

if (gpio_is_valid(pdata->id_gpio)) {
ret = gpio_request(pdata->id_gpio, "otg_id_pin");
if (ret) {
dev_err(&pdev->dev, "can't request ID pin %d\n", pdata->id_gpio);
}

ret = gpio_direction_input(pdata->id_gpio);

if (ret) {
dev_err(&pdev->dev, "can't configure ID pin %d as input\n", pdata->id_gpio);
gpio_free(pdata->id_gpio);
}

ret = request_irq(gpio_to_irq(pdata->id_gpio), ohci_at91_otg_irq, 0, "otg_irq", pdev);

if (ret) {
gpio_free(pdata->id_gpio);
dev_err(&pdev->dev, "OTG IRQ request failed. id_gpio: %d\n", pdata->id_gpio);
}

if (!gpio_get_value(pdata->id_gpio)) {
gpio_direction_output(pdata->vbus_pin[0], 0);
}
}

device_init_wakeup(&pdev->dev, 1);
return usb_hcd_at91_probe(&ohci_at91_hc_driver, pdev);
}
Expand All @@ -642,6 +695,11 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev)
free_irq(gpio_to_irq(pdata->overcurrent_pin[i]), pdev);
gpio_free(pdata->overcurrent_pin[i]);
}

if (gpio_is_valid(pdata->id_gpio)) {
free_irq(gpio_to_irq(pdata->id_gpio), pdev);
gpio_free(pdata->id_gpio);
}
}

device_init_wakeup(&pdev->dev, 0);
Expand Down
1 change: 1 addition & 0 deletions include/linux/usb/atmel_usba_udc.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct usba_ep_data {
struct usba_platform_data {
int vbus_pin;
int vbus_pin_inverted;
int id_pin;
int num_ep;
struct usba_ep_data ep[0];
};
Expand Down