summaryrefslogtreecommitdiffstats
path: root/drivers/usb/musb/musb_barebox.c
blob: eaad61ca67a50ec571206d05ddc4abbe03a22cdb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// SPDX-License-Identifier: GPL-2.0
#include <common.h>
#include <init.h>
#include <clock.h>
#include <usb/musb.h>
#include <usb/usb.h>
#include <linux/types.h>
#include <linux/err.h>
#include <linux/barebox-wrapper.h>

#include "musb_core.h"
#include "musb_gadget.h"

static struct usb_host_endpoint hep;
static struct urb urb;

static void musb_host_complete_urb(struct urb *urb)
{
	urb->dev->status &= ~USB_ST_NOT_PROC;
	urb->dev->act_len = urb->actual_length;
}

static struct urb *construct_urb(struct usb_device *dev, int endpoint_type,
				unsigned long pipe, void *buffer, int len,
				struct devrequest *setup, int interval)
{
	int epnum = usb_pipeendpoint(pipe);
	int is_in = usb_pipein(pipe);

	memset(&urb, 0, sizeof(struct urb));
	memset(&hep, 0, sizeof(struct usb_host_endpoint));
	INIT_LIST_HEAD(&hep.urb_list);
	INIT_LIST_HEAD(&urb.urb_list);
	urb.ep = &hep;
	urb.complete = musb_host_complete_urb;
	urb.status = -EINPROGRESS;
	urb.dev = dev;
	urb.pipe = pipe;
	urb.transfer_buffer = buffer;
	urb.transfer_dma = (unsigned long)buffer;
	urb.transfer_buffer_length = len;
	urb.setup_packet = (unsigned char *)setup;

	urb.ep->desc.wMaxPacketSize =
		__cpu_to_le16(is_in ? dev->epmaxpacketin[epnum] :
				dev->epmaxpacketout[epnum]);
	urb.ep->desc.bmAttributes = endpoint_type;
	urb.ep->desc.bEndpointAddress =
		(is_in ? USB_DIR_IN : USB_DIR_OUT) | epnum;
	urb.ep->desc.bInterval = interval;

	return &urb;
}

#define MUSB_HOST_TIMEOUT 0x5fffff

static int submit_urb(struct usb_device *dev, struct urb *urb, int timeout_ms)
{
	struct usb_host *host = dev->host;
	struct musb *musb = to_musb(host);
	int ret;
	uint64_t start;
	uint64_t timeout = timeout_ms;

	ret = musb_urb_enqueue(musb->hcd, urb, 0);
	if (ret < 0) {
		printf("Failed to enqueue URB to controller\n");
		return ret;
	}

	start = get_time_ns();

	do {
		musb->isr(musb);

		if (!urb->status)
			return 0;

	} while (!is_timeout(start, timeout * MSECOND));

	if (urb->dev->status & USB_ST_NOT_PROC)
		ret = -ETIMEDOUT;
	else
		ret = urb->status;

	musb_urb_dequeue(musb->hcd, urb, -ECONNRESET);

	return ret;
}

static int
submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
		int length, int timeout)
{
	struct urb *urb = construct_urb(dev, USB_ENDPOINT_XFER_BULK, pipe,
					buffer, length, NULL, 0);
	return submit_urb(dev, urb, timeout);
}

static int
submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
		   int length, struct devrequest *setup, int timeout)
{
	struct usb_host *host = dev->host;
	struct musb *musb = to_musb(host);
	struct urb *urb = construct_urb(dev, USB_ENDPOINT_XFER_CONTROL, pipe,
					buffer, length, setup, 0);

	/* Fix speed for non hub-attached devices */
	if (!dev->parent)
		dev->speed = musb->host_speed;

	return submit_urb(dev, urb, timeout);
}

static int
submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
	       int length, int interval)
{
	struct urb *urb = construct_urb(dev, USB_ENDPOINT_XFER_INT, pipe,
					buffer, length, NULL, interval);
	return submit_urb(dev, urb, 100);
}

int musb_register(struct musb *musb)
{
	struct usb_host *host;

	host = &musb->host;
	host->hw_dev = musb->controller;
	host->init = musb_init;
	host->submit_int_msg = submit_int_msg;
	host->submit_control_msg = submit_control_msg;
	host->submit_bulk_msg = submit_bulk_msg;

	usb_register_host(host);

	return 0;
}