xref: /linux/drivers/net/wireless/morsemicro/mm81x/usb.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
1*b1906ceaSLachlan Hodges // SPDX-License-Identifier: GPL-2.0-only
2*b1906ceaSLachlan Hodges /*
3*b1906ceaSLachlan Hodges  * Copyright (c) 2017-2026 Morse Micro
4*b1906ceaSLachlan Hodges  */
5*b1906ceaSLachlan Hodges #include <linux/jiffies.h>
6*b1906ceaSLachlan Hodges #include <linux/module.h>
7*b1906ceaSLachlan Hodges #include <linux/usb.h>
8*b1906ceaSLachlan Hodges #include "hif.h"
9*b1906ceaSLachlan Hodges #include "bus.h"
10*b1906ceaSLachlan Hodges #include "mac.h"
11*b1906ceaSLachlan Hodges #include "core.h"
12*b1906ceaSLachlan Hodges 
13*b1906ceaSLachlan Hodges /*
14*b1906ceaSLachlan Hodges  * URB timeout in milliseconds. If an URB does not complete within this
15*b1906ceaSLachlan Hodges  * time, it will be killed. This timeout needs to account for USB suspendand
16*b1906ceaSLachlan Hodges  * resume occurring before the URB can be transferred, and it also needs to
17*b1906ceaSLachlan Hodges  * account for transferring USB_MAX_TRANSFER_SIZE bytes over a potentially
18*b1906ceaSLachlan Hodges  * slow, congested USB Full Speed link.
19*b1906ceaSLachlan Hodges  */
20*b1906ceaSLachlan Hodges #define URB_TIMEOUT_MS 250
21*b1906ceaSLachlan Hodges 
22*b1906ceaSLachlan Hodges /* High speed USB 2^(4-1) * 125usec = 1msec */
23*b1906ceaSLachlan Hodges #define MM81X_USB_INTERRUPT_INTERVAL 4
24*b1906ceaSLachlan Hodges 
25*b1906ceaSLachlan Hodges /* Max bytes per USB read/write */
26*b1906ceaSLachlan Hodges #define USB_MAX_TRANSFER_SIZE (16 * 1024)
27*b1906ceaSLachlan Hodges 
28*b1906ceaSLachlan Hodges /* INT EP buffer size */
29*b1906ceaSLachlan Hodges #define MM81X_EP_INT_BUFFER_SIZE 8
30*b1906ceaSLachlan Hodges 
31*b1906ceaSLachlan Hodges /* Morse vendor IDs*/
32*b1906ceaSLachlan Hodges #define MM81X_VENDOR_ID 0x325b
33*b1906ceaSLachlan Hodges #define MM81X_MM810X_PRODUCT_ID 0x8100
34*b1906ceaSLachlan Hodges 
35*b1906ceaSLachlan Hodges /* Power management runtime auto-suspend delay value in milliseconds */
36*b1906ceaSLachlan Hodges #define PM_RUNTIME_AUTOSUSPEND_DELAY_MS 100
37*b1906ceaSLachlan Hodges 
38*b1906ceaSLachlan Hodges enum mm81x_usb_endpoints {
39*b1906ceaSLachlan Hodges 	MM81X_EP_CMD = 0,
40*b1906ceaSLachlan Hodges 	MM81X_EP_INT,
41*b1906ceaSLachlan Hodges 	MM81X_EP_MEM_RD,
42*b1906ceaSLachlan Hodges 	MM81X_EP_MEM_WR,
43*b1906ceaSLachlan Hodges 	MM81X_EP_REG_RD,
44*b1906ceaSLachlan Hodges 	MM81X_EP_REG_WR,
45*b1906ceaSLachlan Hodges 	MM81X_EP_EP_MAX,
46*b1906ceaSLachlan Hodges };
47*b1906ceaSLachlan Hodges 
48*b1906ceaSLachlan Hodges struct mm81x_usb_endpoint {
49*b1906ceaSLachlan Hodges 	unsigned char *buffer;
50*b1906ceaSLachlan Hodges 	struct urb *urb;
51*b1906ceaSLachlan Hodges 	__u8 addr;
52*b1906ceaSLachlan Hodges 	int size;
53*b1906ceaSLachlan Hodges };
54*b1906ceaSLachlan Hodges 
55*b1906ceaSLachlan Hodges enum mm81x_usb_flags { MM81X_USB_FLAG_ATTACHED, MM81X_USB_FLAG_SUSPENDED };
56*b1906ceaSLachlan Hodges 
57*b1906ceaSLachlan Hodges struct mm81x_usb {
58*b1906ceaSLachlan Hodges 	struct usb_device *udev;
59*b1906ceaSLachlan Hodges 	struct usb_interface *interface;
60*b1906ceaSLachlan Hodges 	struct mm81x_usb_endpoint endpoints[MM81X_EP_EP_MAX];
61*b1906ceaSLachlan Hodges 	int errors;
62*b1906ceaSLachlan Hodges 
63*b1906ceaSLachlan Hodges 	/* serialise USB device struct */
64*b1906ceaSLachlan Hodges 	struct mutex lock;
65*b1906ceaSLachlan Hodges 
66*b1906ceaSLachlan Hodges 	/* serialise USB bus access */
67*b1906ceaSLachlan Hodges 	struct mutex bus_lock;
68*b1906ceaSLachlan Hodges 
69*b1906ceaSLachlan Hodges 	bool ongoing_cmd;
70*b1906ceaSLachlan Hodges 	bool ongoing_rw;
71*b1906ceaSLachlan Hodges 	wait_queue_head_t rw_in_wait;
72*b1906ceaSLachlan Hodges 	unsigned long flags;
73*b1906ceaSLachlan Hodges };
74*b1906ceaSLachlan Hodges 
75*b1906ceaSLachlan Hodges enum mm81x_usb_command_direction {
76*b1906ceaSLachlan Hodges 	MM81X_USB_WRITE = 0x00,
77*b1906ceaSLachlan Hodges 	MM81X_USB_READ = 0x80,
78*b1906ceaSLachlan Hodges 	MM81X_USB_RESET = 0x02,
79*b1906ceaSLachlan Hodges };
80*b1906ceaSLachlan Hodges 
81*b1906ceaSLachlan Hodges struct mm81x_usb_command {
82*b1906ceaSLachlan Hodges 	__le32 dir; /* Next BULK direction */
83*b1906ceaSLachlan Hodges 	__le32 address; /* Next BULK address */
84*b1906ceaSLachlan Hodges 	__le32 length; /* Next BULK size */
85*b1906ceaSLachlan Hodges };
86*b1906ceaSLachlan Hodges 
87*b1906ceaSLachlan Hodges static const struct usb_device_id mm81x_usb_table[] = {
88*b1906ceaSLachlan Hodges 	{ USB_DEVICE(MM81X_VENDOR_ID, MM81X_MM810X_PRODUCT_ID) },
89*b1906ceaSLachlan Hodges 	{} /* Terminating entry */
90*b1906ceaSLachlan Hodges };
91*b1906ceaSLachlan Hodges 
92*b1906ceaSLachlan Hodges MODULE_DEVICE_TABLE(usb, mm81x_usb_table);
93*b1906ceaSLachlan Hodges 
mm81x_usb_irq_work(struct work_struct * work)94*b1906ceaSLachlan Hodges static void mm81x_usb_irq_work(struct work_struct *work)
95*b1906ceaSLachlan Hodges {
96*b1906ceaSLachlan Hodges 	struct mm81x *mors = container_of(work, struct mm81x, usb_irq_work);
97*b1906ceaSLachlan Hodges 
98*b1906ceaSLachlan Hodges 	mm81x_claim_bus(mors);
99*b1906ceaSLachlan Hodges 	mm81x_hw_irq_handle(mors);
100*b1906ceaSLachlan Hodges 	mm81x_release_bus(mors);
101*b1906ceaSLachlan Hodges }
102*b1906ceaSLachlan Hodges 
mm81x_usb_urb_status_is_disconnect(const struct urb * urb)103*b1906ceaSLachlan Hodges static bool mm81x_usb_urb_status_is_disconnect(const struct urb *urb)
104*b1906ceaSLachlan Hodges {
105*b1906ceaSLachlan Hodges 	return ((urb->status == -EPROTO) || (urb->status == -EILSEQ) ||
106*b1906ceaSLachlan Hodges 		(urb->status == -ETIME) || (urb->status == -EPIPE));
107*b1906ceaSLachlan Hodges }
108*b1906ceaSLachlan Hodges 
mm81x_usb_int_handler(struct urb * urb)109*b1906ceaSLachlan Hodges static void mm81x_usb_int_handler(struct urb *urb)
110*b1906ceaSLachlan Hodges {
111*b1906ceaSLachlan Hodges 	int ret;
112*b1906ceaSLachlan Hodges 	struct mm81x *mors = urb->context;
113*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
114*b1906ceaSLachlan Hodges 
115*b1906ceaSLachlan Hodges 	if (!test_bit(MM81X_USB_FLAG_ATTACHED, &musb->flags))
116*b1906ceaSLachlan Hodges 		return;
117*b1906ceaSLachlan Hodges 
118*b1906ceaSLachlan Hodges 	if (urb->status) {
119*b1906ceaSLachlan Hodges 		if (mm81x_usb_urb_status_is_disconnect(urb)) {
120*b1906ceaSLachlan Hodges 			clear_bit(MM81X_USB_FLAG_ATTACHED, &musb->flags);
121*b1906ceaSLachlan Hodges 			set_bit(MM81X_STATE_CHIP_UNRESPONSIVE,
122*b1906ceaSLachlan Hodges 				&mors->state_flags);
123*b1906ceaSLachlan Hodges 			dev_dbg(mors->dev,
124*b1906ceaSLachlan Hodges 				"USB sudden disconnect detected in %s",
125*b1906ceaSLachlan Hodges 				__func__);
126*b1906ceaSLachlan Hodges 			return;
127*b1906ceaSLachlan Hodges 		}
128*b1906ceaSLachlan Hodges 
129*b1906ceaSLachlan Hodges 		if (!(urb->status == -ENOENT || urb->status == -ECONNRESET ||
130*b1906ceaSLachlan Hodges 		      urb->status == -ESHUTDOWN))
131*b1906ceaSLachlan Hodges 			dev_err(mors->dev, "- nonzero read status received: %d",
132*b1906ceaSLachlan Hodges 				urb->status);
133*b1906ceaSLachlan Hodges 	}
134*b1906ceaSLachlan Hodges 
135*b1906ceaSLachlan Hodges 	ret = usb_submit_urb(urb, GFP_ATOMIC);
136*b1906ceaSLachlan Hodges 
137*b1906ceaSLachlan Hodges 	/* usb_kill_urb has been called */
138*b1906ceaSLachlan Hodges 	if (ret == -EPERM)
139*b1906ceaSLachlan Hodges 		return;
140*b1906ceaSLachlan Hodges 	else if (ret)
141*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "error: resubmit urb %p err code %d", urb,
142*b1906ceaSLachlan Hodges 			ret);
143*b1906ceaSLachlan Hodges 
144*b1906ceaSLachlan Hodges 	queue_work(mors->chip_wq, &mors->usb_irq_work);
145*b1906ceaSLachlan Hodges }
146*b1906ceaSLachlan Hodges 
mm81x_usb_int_enable(struct mm81x * mors)147*b1906ceaSLachlan Hodges static int mm81x_usb_int_enable(struct mm81x *mors)
148*b1906ceaSLachlan Hodges {
149*b1906ceaSLachlan Hodges 	int ret = 0;
150*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
151*b1906ceaSLachlan Hodges 	struct urb *urb;
152*b1906ceaSLachlan Hodges 
153*b1906ceaSLachlan Hodges 	if (!test_bit(MM81X_USB_FLAG_ATTACHED, &musb->flags))
154*b1906ceaSLachlan Hodges 		return -ENODEV;
155*b1906ceaSLachlan Hodges 
156*b1906ceaSLachlan Hodges 	urb = usb_alloc_urb(0, GFP_KERNEL);
157*b1906ceaSLachlan Hodges 	if (!urb) {
158*b1906ceaSLachlan Hodges 		ret = -ENOMEM;
159*b1906ceaSLachlan Hodges 		goto out;
160*b1906ceaSLachlan Hodges 	}
161*b1906ceaSLachlan Hodges 
162*b1906ceaSLachlan Hodges 	musb->endpoints[MM81X_EP_INT].urb = urb;
163*b1906ceaSLachlan Hodges 
164*b1906ceaSLachlan Hodges 	musb->endpoints[MM81X_EP_INT].buffer =
165*b1906ceaSLachlan Hodges 		usb_alloc_coherent(musb->udev, MM81X_EP_INT_BUFFER_SIZE,
166*b1906ceaSLachlan Hodges 				   GFP_KERNEL, &urb->transfer_dma);
167*b1906ceaSLachlan Hodges 	if (!musb->endpoints[MM81X_EP_INT].buffer) {
168*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "couldn't allocate transfer_buffer");
169*b1906ceaSLachlan Hodges 		ret = -ENOMEM;
170*b1906ceaSLachlan Hodges 		goto error_set_urb_null;
171*b1906ceaSLachlan Hodges 	}
172*b1906ceaSLachlan Hodges 
173*b1906ceaSLachlan Hodges 	usb_fill_int_urb(
174*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_INT].urb, musb->udev,
175*b1906ceaSLachlan Hodges 		usb_rcvintpipe(musb->udev, musb->endpoints[MM81X_EP_INT].addr),
176*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_INT].buffer, MM81X_EP_INT_BUFFER_SIZE,
177*b1906ceaSLachlan Hodges 		mm81x_usb_int_handler, mors, MM81X_USB_INTERRUPT_INTERVAL);
178*b1906ceaSLachlan Hodges 	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
179*b1906ceaSLachlan Hodges 
180*b1906ceaSLachlan Hodges 	ret = usb_submit_urb(urb, GFP_KERNEL);
181*b1906ceaSLachlan Hodges 	if (ret) {
182*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "Couldn't submit urb. Error number %d", ret);
183*b1906ceaSLachlan Hodges 		goto error;
184*b1906ceaSLachlan Hodges 	}
185*b1906ceaSLachlan Hodges 
186*b1906ceaSLachlan Hodges 	return 0;
187*b1906ceaSLachlan Hodges 
188*b1906ceaSLachlan Hodges error:
189*b1906ceaSLachlan Hodges 	usb_free_coherent(musb->udev, MM81X_EP_INT_BUFFER_SIZE,
190*b1906ceaSLachlan Hodges 			  musb->endpoints[MM81X_EP_INT].buffer,
191*b1906ceaSLachlan Hodges 			  urb->transfer_dma);
192*b1906ceaSLachlan Hodges error_set_urb_null:
193*b1906ceaSLachlan Hodges 	musb->endpoints[MM81X_EP_INT].urb = NULL;
194*b1906ceaSLachlan Hodges 	usb_free_urb(urb);
195*b1906ceaSLachlan Hodges out:
196*b1906ceaSLachlan Hodges 	return ret;
197*b1906ceaSLachlan Hodges }
198*b1906ceaSLachlan Hodges 
mm81x_usb_int_stop(struct mm81x * mors)199*b1906ceaSLachlan Hodges static void mm81x_usb_int_stop(struct mm81x *mors)
200*b1906ceaSLachlan Hodges {
201*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
202*b1906ceaSLachlan Hodges 
203*b1906ceaSLachlan Hodges 	usb_kill_urb(musb->endpoints[MM81X_EP_INT].urb);
204*b1906ceaSLachlan Hodges 	cancel_work_sync(&mors->usb_irq_work);
205*b1906ceaSLachlan Hodges }
206*b1906ceaSLachlan Hodges 
mm81x_usb_cmd_callback(struct urb * urb)207*b1906ceaSLachlan Hodges static void mm81x_usb_cmd_callback(struct urb *urb)
208*b1906ceaSLachlan Hodges {
209*b1906ceaSLachlan Hodges 	struct mm81x *mors = urb->context;
210*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
211*b1906ceaSLachlan Hodges 
212*b1906ceaSLachlan Hodges 	/* sync/async unlink faults aren't errors */
213*b1906ceaSLachlan Hodges 	if (urb->status) {
214*b1906ceaSLachlan Hodges 		if (!(urb->status == -ENOENT || urb->status == -ECONNRESET ||
215*b1906ceaSLachlan Hodges 		      urb->status == -ESHUTDOWN))
216*b1906ceaSLachlan Hodges 			dev_err(mors->dev,
217*b1906ceaSLachlan Hodges 				"nonzero write bulk status received: %d",
218*b1906ceaSLachlan Hodges 				urb->status);
219*b1906ceaSLachlan Hodges 
220*b1906ceaSLachlan Hodges 		musb->errors = urb->status;
221*b1906ceaSLachlan Hodges 	}
222*b1906ceaSLachlan Hodges 
223*b1906ceaSLachlan Hodges 	musb->ongoing_cmd = false;
224*b1906ceaSLachlan Hodges 	wake_up(&musb->rw_in_wait);
225*b1906ceaSLachlan Hodges }
226*b1906ceaSLachlan Hodges 
mm81x_usb_cmd(struct mm81x_usb * musb,const struct mm81x_usb_command * cmd)227*b1906ceaSLachlan Hodges static int mm81x_usb_cmd(struct mm81x_usb *musb,
228*b1906ceaSLachlan Hodges 			 const struct mm81x_usb_command *cmd)
229*b1906ceaSLachlan Hodges {
230*b1906ceaSLachlan Hodges 	int retval = 0;
231*b1906ceaSLachlan Hodges 	struct mm81x *mors = usb_get_intfdata(musb->interface);
232*b1906ceaSLachlan Hodges 	struct mm81x_usb_endpoint *ep = &musb->endpoints[MM81X_EP_CMD];
233*b1906ceaSLachlan Hodges 	size_t writesize = sizeof(*cmd);
234*b1906ceaSLachlan Hodges 
235*b1906ceaSLachlan Hodges 	if (!test_bit(MM81X_USB_FLAG_ATTACHED, &musb->flags))
236*b1906ceaSLachlan Hodges 		return -ENODEV;
237*b1906ceaSLachlan Hodges 
238*b1906ceaSLachlan Hodges 	memcpy(ep->buffer, cmd, writesize);
239*b1906ceaSLachlan Hodges 
240*b1906ceaSLachlan Hodges 	usb_fill_bulk_urb(ep->urb, musb->udev,
241*b1906ceaSLachlan Hodges 			  usb_sndbulkpipe(musb->udev, ep->addr), ep->buffer,
242*b1906ceaSLachlan Hodges 			  writesize, mm81x_usb_cmd_callback, mors);
243*b1906ceaSLachlan Hodges 	ep->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
244*b1906ceaSLachlan Hodges 
245*b1906ceaSLachlan Hodges 	musb->ongoing_cmd = true;
246*b1906ceaSLachlan Hodges 
247*b1906ceaSLachlan Hodges 	retval = usb_submit_urb(ep->urb, GFP_KERNEL);
248*b1906ceaSLachlan Hodges 	if (retval) {
249*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "- failed submitting write urb, error %d",
250*b1906ceaSLachlan Hodges 			retval);
251*b1906ceaSLachlan Hodges 
252*b1906ceaSLachlan Hodges 		goto error;
253*b1906ceaSLachlan Hodges 	}
254*b1906ceaSLachlan Hodges 
255*b1906ceaSLachlan Hodges 	retval = wait_event_interruptible_timeout(
256*b1906ceaSLachlan Hodges 		musb->rw_in_wait, (!musb->ongoing_cmd),
257*b1906ceaSLachlan Hodges 		msecs_to_jiffies(URB_TIMEOUT_MS));
258*b1906ceaSLachlan Hodges 	if (retval < 0) {
259*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "error waiting for urb %d", retval);
260*b1906ceaSLachlan Hodges 		goto error;
261*b1906ceaSLachlan Hodges 	} else if (retval == 0) {
262*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "timed out waiting for urb");
263*b1906ceaSLachlan Hodges 		usb_kill_urb(ep->urb);
264*b1906ceaSLachlan Hodges 		retval = -ETIMEDOUT;
265*b1906ceaSLachlan Hodges 		goto error;
266*b1906ceaSLachlan Hodges 	}
267*b1906ceaSLachlan Hodges 
268*b1906ceaSLachlan Hodges 	musb->ongoing_cmd = false;
269*b1906ceaSLachlan Hodges 	return writesize;
270*b1906ceaSLachlan Hodges 
271*b1906ceaSLachlan Hodges error:
272*b1906ceaSLachlan Hodges 	musb->ongoing_cmd = false;
273*b1906ceaSLachlan Hodges 	return retval;
274*b1906ceaSLachlan Hodges }
275*b1906ceaSLachlan Hodges 
mm81x_usb_ndr_reset(struct mm81x * mors)276*b1906ceaSLachlan Hodges static int mm81x_usb_ndr_reset(struct mm81x *mors)
277*b1906ceaSLachlan Hodges {
278*b1906ceaSLachlan Hodges 	int ret;
279*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
280*b1906ceaSLachlan Hodges 	struct mm81x_usb_command cmd;
281*b1906ceaSLachlan Hodges 
282*b1906ceaSLachlan Hodges 	mutex_lock(&musb->lock);
283*b1906ceaSLachlan Hodges 
284*b1906ceaSLachlan Hodges 	musb->ongoing_rw = true;
285*b1906ceaSLachlan Hodges 	musb->errors = 0;
286*b1906ceaSLachlan Hodges 
287*b1906ceaSLachlan Hodges 	cmd.dir = cpu_to_le32(MM81X_USB_RESET);
288*b1906ceaSLachlan Hodges 	cmd.address = cpu_to_le32(0);
289*b1906ceaSLachlan Hodges 	cmd.length = cpu_to_le32(0);
290*b1906ceaSLachlan Hodges 
291*b1906ceaSLachlan Hodges 	ret = mm81x_usb_cmd(musb, &cmd);
292*b1906ceaSLachlan Hodges 	if (ret < 0)
293*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "mm81x_usb_cmd (MM81X_USB_RESET) error %d\n",
294*b1906ceaSLachlan Hodges 			ret);
295*b1906ceaSLachlan Hodges 	else
296*b1906ceaSLachlan Hodges 		ret = 0;
297*b1906ceaSLachlan Hodges 
298*b1906ceaSLachlan Hodges 	musb->ongoing_rw = false;
299*b1906ceaSLachlan Hodges 	mutex_unlock(&musb->lock);
300*b1906ceaSLachlan Hodges 	return ret;
301*b1906ceaSLachlan Hodges }
302*b1906ceaSLachlan Hodges 
mm81x_usb_mem_rw_callback(struct urb * urb)303*b1906ceaSLachlan Hodges static void mm81x_usb_mem_rw_callback(struct urb *urb)
304*b1906ceaSLachlan Hodges {
305*b1906ceaSLachlan Hodges 	struct mm81x *mors = urb->context;
306*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
307*b1906ceaSLachlan Hodges 
308*b1906ceaSLachlan Hodges 	/* sync/async unlink faults aren't errors */
309*b1906ceaSLachlan Hodges 	if (urb->status) {
310*b1906ceaSLachlan Hodges 		if (!(urb->status == -ENOENT || urb->status == -ECONNRESET ||
311*b1906ceaSLachlan Hodges 		      urb->status == -ESHUTDOWN))
312*b1906ceaSLachlan Hodges 			dev_err(mors->dev,
313*b1906ceaSLachlan Hodges 				"nonzero write bulk status received: %d",
314*b1906ceaSLachlan Hodges 				urb->status);
315*b1906ceaSLachlan Hodges 
316*b1906ceaSLachlan Hodges 		musb->errors = urb->status;
317*b1906ceaSLachlan Hodges 	}
318*b1906ceaSLachlan Hodges 
319*b1906ceaSLachlan Hodges 	musb->ongoing_rw = false;
320*b1906ceaSLachlan Hodges 	wake_up(&musb->rw_in_wait);
321*b1906ceaSLachlan Hodges }
322*b1906ceaSLachlan Hodges 
mm81x_usb_mem_read(struct mm81x_usb * musb,u32 address,u8 * data,ssize_t size)323*b1906ceaSLachlan Hodges static int mm81x_usb_mem_read(struct mm81x_usb *musb, u32 address, u8 *data,
324*b1906ceaSLachlan Hodges 			      ssize_t size)
325*b1906ceaSLachlan Hodges {
326*b1906ceaSLachlan Hodges 	int ret;
327*b1906ceaSLachlan Hodges 	struct mm81x_usb_command cmd;
328*b1906ceaSLachlan Hodges 	struct mm81x *mors = usb_get_intfdata(musb->interface);
329*b1906ceaSLachlan Hodges 
330*b1906ceaSLachlan Hodges 	if (!test_bit(MM81X_USB_FLAG_ATTACHED, &musb->flags))
331*b1906ceaSLachlan Hodges 		return -ENODEV;
332*b1906ceaSLachlan Hodges 
333*b1906ceaSLachlan Hodges 	mutex_lock(&musb->lock);
334*b1906ceaSLachlan Hodges 
335*b1906ceaSLachlan Hodges 	musb->ongoing_rw = true;
336*b1906ceaSLachlan Hodges 	musb->errors = 0;
337*b1906ceaSLachlan Hodges 
338*b1906ceaSLachlan Hodges 	/* Send command ahead to prepare for Tokens */
339*b1906ceaSLachlan Hodges 	cmd.dir = cpu_to_le32(MM81X_USB_READ);
340*b1906ceaSLachlan Hodges 	cmd.address = cpu_to_le32(address);
341*b1906ceaSLachlan Hodges 	cmd.length = cpu_to_le32(size);
342*b1906ceaSLachlan Hodges 
343*b1906ceaSLachlan Hodges 	ret = mm81x_usb_cmd(musb, &cmd);
344*b1906ceaSLachlan Hodges 	if (ret < 0) {
345*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "mm81x_usb_cmd error %d", ret);
346*b1906ceaSLachlan Hodges 		goto error;
347*b1906ceaSLachlan Hodges 	}
348*b1906ceaSLachlan Hodges 
349*b1906ceaSLachlan Hodges 	/* Let's be fast push the next URB, don't wait until command is done */
350*b1906ceaSLachlan Hodges 	usb_fill_bulk_urb(
351*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_MEM_RD].urb, musb->udev,
352*b1906ceaSLachlan Hodges 		usb_rcvbulkpipe(musb->udev,
353*b1906ceaSLachlan Hodges 				musb->endpoints[MM81X_EP_MEM_RD].addr),
354*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_MEM_RD].buffer, size,
355*b1906ceaSLachlan Hodges 		mm81x_usb_mem_rw_callback, mors);
356*b1906ceaSLachlan Hodges 
357*b1906ceaSLachlan Hodges 	ret = usb_submit_urb(musb->endpoints[MM81X_EP_MEM_RD].urb, GFP_ATOMIC);
358*b1906ceaSLachlan Hodges 	if (ret < 0) {
359*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "failed submitting read urb, error %d", ret);
360*b1906ceaSLachlan Hodges 		ret = (ret == -ENOMEM) ? ret : -EIO;
361*b1906ceaSLachlan Hodges 		goto error;
362*b1906ceaSLachlan Hodges 	}
363*b1906ceaSLachlan Hodges 
364*b1906ceaSLachlan Hodges 	ret = wait_event_interruptible_timeout(
365*b1906ceaSLachlan Hodges 		musb->rw_in_wait, (!musb->ongoing_rw),
366*b1906ceaSLachlan Hodges 		msecs_to_jiffies(URB_TIMEOUT_MS));
367*b1906ceaSLachlan Hodges 	if (ret < 0) {
368*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "wait_event_interruptible: error %d", ret);
369*b1906ceaSLachlan Hodges 		goto error;
370*b1906ceaSLachlan Hodges 	} else if (ret == 0) {
371*b1906ceaSLachlan Hodges 		/* Timed out. */
372*b1906ceaSLachlan Hodges 		usb_kill_urb(musb->endpoints[MM81X_EP_MEM_RD].urb);
373*b1906ceaSLachlan Hodges 	}
374*b1906ceaSLachlan Hodges 
375*b1906ceaSLachlan Hodges 	if (musb->errors) {
376*b1906ceaSLachlan Hodges 		ret = musb->errors;
377*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "mem read error %d", ret);
378*b1906ceaSLachlan Hodges 		goto error;
379*b1906ceaSLachlan Hodges 	}
380*b1906ceaSLachlan Hodges 
381*b1906ceaSLachlan Hodges 	memcpy(data, musb->endpoints[MM81X_EP_MEM_RD].buffer, size);
382*b1906ceaSLachlan Hodges 	ret = size;
383*b1906ceaSLachlan Hodges 
384*b1906ceaSLachlan Hodges error:
385*b1906ceaSLachlan Hodges 	musb->ongoing_rw = false;
386*b1906ceaSLachlan Hodges 	mutex_unlock(&musb->lock);
387*b1906ceaSLachlan Hodges 
388*b1906ceaSLachlan Hodges 	return ret;
389*b1906ceaSLachlan Hodges }
390*b1906ceaSLachlan Hodges 
mm81x_usb_mem_write(struct mm81x_usb * musb,u32 address,u8 * data,ssize_t size)391*b1906ceaSLachlan Hodges static int mm81x_usb_mem_write(struct mm81x_usb *musb, u32 address, u8 *data,
392*b1906ceaSLachlan Hodges 			       ssize_t size)
393*b1906ceaSLachlan Hodges {
394*b1906ceaSLachlan Hodges 	int ret;
395*b1906ceaSLachlan Hodges 	struct mm81x_usb_command cmd;
396*b1906ceaSLachlan Hodges 	struct mm81x *mors = usb_get_intfdata(musb->interface);
397*b1906ceaSLachlan Hodges 
398*b1906ceaSLachlan Hodges 	if (!test_bit(MM81X_USB_FLAG_ATTACHED, &musb->flags))
399*b1906ceaSLachlan Hodges 		return -ENODEV;
400*b1906ceaSLachlan Hodges 
401*b1906ceaSLachlan Hodges 	mutex_lock(&musb->lock);
402*b1906ceaSLachlan Hodges 
403*b1906ceaSLachlan Hodges 	musb->ongoing_rw = true;
404*b1906ceaSLachlan Hodges 	musb->errors = 0;
405*b1906ceaSLachlan Hodges 
406*b1906ceaSLachlan Hodges 	/* Send command ahead to prepare for Tokens */
407*b1906ceaSLachlan Hodges 	cmd.dir = cpu_to_le32(MM81X_USB_WRITE);
408*b1906ceaSLachlan Hodges 	cmd.address = cpu_to_le32(address);
409*b1906ceaSLachlan Hodges 	cmd.length = cpu_to_le32(size);
410*b1906ceaSLachlan Hodges 	ret = mm81x_usb_cmd(musb, &cmd);
411*b1906ceaSLachlan Hodges 	if (ret < 0) {
412*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "mm81x_usb_mem_read error %d", ret);
413*b1906ceaSLachlan Hodges 		goto error;
414*b1906ceaSLachlan Hodges 	}
415*b1906ceaSLachlan Hodges 
416*b1906ceaSLachlan Hodges 	memcpy(musb->endpoints[MM81X_EP_MEM_WR].buffer, data, size);
417*b1906ceaSLachlan Hodges 
418*b1906ceaSLachlan Hodges 	/* prepare a read */
419*b1906ceaSLachlan Hodges 	usb_fill_bulk_urb(
420*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_MEM_WR].urb, musb->udev,
421*b1906ceaSLachlan Hodges 		usb_sndbulkpipe(musb->udev,
422*b1906ceaSLachlan Hodges 				musb->endpoints[MM81X_EP_MEM_WR].addr),
423*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_MEM_WR].buffer, size,
424*b1906ceaSLachlan Hodges 		mm81x_usb_mem_rw_callback, mors);
425*b1906ceaSLachlan Hodges 
426*b1906ceaSLachlan Hodges 	ret = usb_submit_urb(musb->endpoints[MM81X_EP_MEM_WR].urb, GFP_ATOMIC);
427*b1906ceaSLachlan Hodges 	if (ret < 0) {
428*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "- failed submitting write urb, error %d",
429*b1906ceaSLachlan Hodges 			ret);
430*b1906ceaSLachlan Hodges 		ret = (ret == -ENOMEM) ? ret : -EIO;
431*b1906ceaSLachlan Hodges 		goto error;
432*b1906ceaSLachlan Hodges 	}
433*b1906ceaSLachlan Hodges 
434*b1906ceaSLachlan Hodges 	ret = wait_event_interruptible_timeout(
435*b1906ceaSLachlan Hodges 		musb->rw_in_wait, (!musb->ongoing_rw),
436*b1906ceaSLachlan Hodges 		msecs_to_jiffies(URB_TIMEOUT_MS));
437*b1906ceaSLachlan Hodges 	if (ret < 0) {
438*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "error %d", ret);
439*b1906ceaSLachlan Hodges 		goto error;
440*b1906ceaSLachlan Hodges 	} else if (ret == 0) {
441*b1906ceaSLachlan Hodges 		/* Timed out. */
442*b1906ceaSLachlan Hodges 		usb_kill_urb(musb->endpoints[MM81X_EP_MEM_WR].urb);
443*b1906ceaSLachlan Hodges 	}
444*b1906ceaSLachlan Hodges 
445*b1906ceaSLachlan Hodges 	if (musb->errors) {
446*b1906ceaSLachlan Hodges 		ret = musb->errors;
447*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "error %d", ret);
448*b1906ceaSLachlan Hodges 		goto error;
449*b1906ceaSLachlan Hodges 	}
450*b1906ceaSLachlan Hodges 
451*b1906ceaSLachlan Hodges 	ret = size;
452*b1906ceaSLachlan Hodges 
453*b1906ceaSLachlan Hodges error:
454*b1906ceaSLachlan Hodges 	musb->ongoing_rw = false;
455*b1906ceaSLachlan Hodges 	mutex_unlock(&musb->lock);
456*b1906ceaSLachlan Hodges 	return ret;
457*b1906ceaSLachlan Hodges }
458*b1906ceaSLachlan Hodges 
mm81x_usb_dm_read(struct mm81x * mors,u32 address,u8 * data,int len)459*b1906ceaSLachlan Hodges static int mm81x_usb_dm_read(struct mm81x *mors, u32 address, u8 *data, int len)
460*b1906ceaSLachlan Hodges {
461*b1906ceaSLachlan Hodges 	ssize_t offset = 0;
462*b1906ceaSLachlan Hodges 	int ret;
463*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
464*b1906ceaSLachlan Hodges 
465*b1906ceaSLachlan Hodges 	while (offset < len) {
466*b1906ceaSLachlan Hodges 		ret = mm81x_usb_mem_read(musb, address + offset,
467*b1906ceaSLachlan Hodges 					 (u8 *)(data + offset),
468*b1906ceaSLachlan Hodges 					 min((ssize_t)(len - offset),
469*b1906ceaSLachlan Hodges 					     (ssize_t)USB_MAX_TRANSFER_SIZE));
470*b1906ceaSLachlan Hodges 		if (ret < 0) {
471*b1906ceaSLachlan Hodges 			dev_err(mors->dev, "%s failed (errno=%d)", __func__,
472*b1906ceaSLachlan Hodges 				ret);
473*b1906ceaSLachlan Hodges 			return ret;
474*b1906ceaSLachlan Hodges 		}
475*b1906ceaSLachlan Hodges 
476*b1906ceaSLachlan Hodges 		offset += ret;
477*b1906ceaSLachlan Hodges 	}
478*b1906ceaSLachlan Hodges 
479*b1906ceaSLachlan Hodges 	return 0;
480*b1906ceaSLachlan Hodges }
481*b1906ceaSLachlan Hodges 
mm81x_usb_dm_write(struct mm81x * mors,u32 address,const u8 * data,int len)482*b1906ceaSLachlan Hodges static int mm81x_usb_dm_write(struct mm81x *mors, u32 address, const u8 *data,
483*b1906ceaSLachlan Hodges 			      int len)
484*b1906ceaSLachlan Hodges {
485*b1906ceaSLachlan Hodges 	ssize_t offset = 0;
486*b1906ceaSLachlan Hodges 	int ret;
487*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
488*b1906ceaSLachlan Hodges 
489*b1906ceaSLachlan Hodges 	while (offset < len) {
490*b1906ceaSLachlan Hodges 		ret = mm81x_usb_mem_write(musb, address + offset,
491*b1906ceaSLachlan Hodges 					  (u8 *)(data + offset),
492*b1906ceaSLachlan Hodges 					  min((ssize_t)(len - offset),
493*b1906ceaSLachlan Hodges 					      (ssize_t)USB_MAX_TRANSFER_SIZE));
494*b1906ceaSLachlan Hodges 		if (ret < 0) {
495*b1906ceaSLachlan Hodges 			dev_err(mors->dev, "%s failed (errno=%d)", __func__,
496*b1906ceaSLachlan Hodges 				ret);
497*b1906ceaSLachlan Hodges 			return ret;
498*b1906ceaSLachlan Hodges 		}
499*b1906ceaSLachlan Hodges 
500*b1906ceaSLachlan Hodges 		offset += ret;
501*b1906ceaSLachlan Hodges 	}
502*b1906ceaSLachlan Hodges 
503*b1906ceaSLachlan Hodges 	return 0;
504*b1906ceaSLachlan Hodges }
505*b1906ceaSLachlan Hodges 
mm81x_usb_reg32_read(struct mm81x * mors,u32 address,u32 * val)506*b1906ceaSLachlan Hodges static int mm81x_usb_reg32_read(struct mm81x *mors, u32 address, u32 *val)
507*b1906ceaSLachlan Hodges {
508*b1906ceaSLachlan Hodges 	int ret = 0;
509*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
510*b1906ceaSLachlan Hodges 
511*b1906ceaSLachlan Hodges 	ret = mm81x_usb_mem_read(musb, address, (u8 *)val, sizeof(*val));
512*b1906ceaSLachlan Hodges 	if (ret == sizeof(*val)) {
513*b1906ceaSLachlan Hodges 		*val = le32_to_cpup((__le32 *)val);
514*b1906ceaSLachlan Hodges 		return 0;
515*b1906ceaSLachlan Hodges 	}
516*b1906ceaSLachlan Hodges 
517*b1906ceaSLachlan Hodges 	dev_err(mors->dev, "usb reg32 read failed %d", ret);
518*b1906ceaSLachlan Hodges 	return ret;
519*b1906ceaSLachlan Hodges }
520*b1906ceaSLachlan Hodges 
mm81x_usb_reg32_write(struct mm81x * mors,u32 address,u32 val)521*b1906ceaSLachlan Hodges static int mm81x_usb_reg32_write(struct mm81x *mors, u32 address, u32 val)
522*b1906ceaSLachlan Hodges {
523*b1906ceaSLachlan Hodges 	int ret = 0;
524*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
525*b1906ceaSLachlan Hodges 	__le32 val_le = cpu_to_le32(val);
526*b1906ceaSLachlan Hodges 
527*b1906ceaSLachlan Hodges 	ret = mm81x_usb_mem_write(musb, address, (u8 *)&val_le, sizeof(val_le));
528*b1906ceaSLachlan Hodges 	if (ret == sizeof(val_le))
529*b1906ceaSLachlan Hodges 		return 0;
530*b1906ceaSLachlan Hodges 
531*b1906ceaSLachlan Hodges 	dev_err(mors->dev, "usb reg32 write failed %d", ret);
532*b1906ceaSLachlan Hodges 	return ret;
533*b1906ceaSLachlan Hodges }
534*b1906ceaSLachlan Hodges 
mm81x_usb_bus_enable(struct mm81x * mors,bool enable)535*b1906ceaSLachlan Hodges static void mm81x_usb_bus_enable(struct mm81x *mors, bool enable)
536*b1906ceaSLachlan Hodges {
537*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
538*b1906ceaSLachlan Hodges 
539*b1906ceaSLachlan Hodges 	if (enable)
540*b1906ceaSLachlan Hodges 		usb_autopm_get_interface(musb->interface);
541*b1906ceaSLachlan Hodges 	else
542*b1906ceaSLachlan Hodges 		usb_autopm_put_interface(musb->interface);
543*b1906ceaSLachlan Hodges }
544*b1906ceaSLachlan Hodges 
mm81x_usb_claim_bus(struct mm81x * mors)545*b1906ceaSLachlan Hodges static void mm81x_usb_claim_bus(struct mm81x *mors)
546*b1906ceaSLachlan Hodges {
547*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
548*b1906ceaSLachlan Hodges 
549*b1906ceaSLachlan Hodges 	mutex_lock(&musb->bus_lock);
550*b1906ceaSLachlan Hodges }
551*b1906ceaSLachlan Hodges 
mm81x_usb_release_bus(struct mm81x * mors)552*b1906ceaSLachlan Hodges static void mm81x_usb_release_bus(struct mm81x *mors)
553*b1906ceaSLachlan Hodges {
554*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
555*b1906ceaSLachlan Hodges 
556*b1906ceaSLachlan Hodges 	mutex_unlock(&musb->bus_lock);
557*b1906ceaSLachlan Hodges }
558*b1906ceaSLachlan Hodges 
mm81x_usb_set_irq(struct mm81x * mors,bool enable)559*b1906ceaSLachlan Hodges static void mm81x_usb_set_irq(struct mm81x *mors, bool enable)
560*b1906ceaSLachlan Hodges {
561*b1906ceaSLachlan Hodges }
562*b1906ceaSLachlan Hodges 
563*b1906ceaSLachlan Hodges static const struct mm81x_bus_ops mm81x_usb_ops = {
564*b1906ceaSLachlan Hodges 	.dm_read = mm81x_usb_dm_read,
565*b1906ceaSLachlan Hodges 	.dm_write = mm81x_usb_dm_write,
566*b1906ceaSLachlan Hodges 	.reg32_read = mm81x_usb_reg32_read,
567*b1906ceaSLachlan Hodges 	.reg32_write = mm81x_usb_reg32_write,
568*b1906ceaSLachlan Hodges 	.digital_reset = mm81x_usb_ndr_reset,
569*b1906ceaSLachlan Hodges 	.set_bus_enable = mm81x_usb_bus_enable,
570*b1906ceaSLachlan Hodges 	.claim = mm81x_usb_claim_bus,
571*b1906ceaSLachlan Hodges 	.release = mm81x_usb_release_bus,
572*b1906ceaSLachlan Hodges 	.set_irq = mm81x_usb_set_irq,
573*b1906ceaSLachlan Hodges 	.bulk_alignment = MM81X_BUS_DEFAULT_BULK_ALIGNMENT,
574*b1906ceaSLachlan Hodges };
575*b1906ceaSLachlan Hodges 
mm81x_usb_detect_endpoints(struct mm81x * mors,const struct usb_interface * intf)576*b1906ceaSLachlan Hodges static int mm81x_usb_detect_endpoints(struct mm81x *mors,
577*b1906ceaSLachlan Hodges 				      const struct usb_interface *intf)
578*b1906ceaSLachlan Hodges {
579*b1906ceaSLachlan Hodges 	int ret;
580*b1906ceaSLachlan Hodges 	unsigned int i;
581*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
582*b1906ceaSLachlan Hodges 	struct usb_endpoint_descriptor *ep_desc;
583*b1906ceaSLachlan Hodges 	struct usb_host_interface *intf_desc = intf->cur_altsetting;
584*b1906ceaSLachlan Hodges 
585*b1906ceaSLachlan Hodges 	for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) {
586*b1906ceaSLachlan Hodges 		ep_desc = &intf_desc->endpoint[i].desc;
587*b1906ceaSLachlan Hodges 
588*b1906ceaSLachlan Hodges 		if (usb_endpoint_is_bulk_in(ep_desc)) {
589*b1906ceaSLachlan Hodges 			if (!musb->endpoints[MM81X_EP_MEM_RD].addr) {
590*b1906ceaSLachlan Hodges 				musb->endpoints[MM81X_EP_MEM_RD].addr =
591*b1906ceaSLachlan Hodges 					usb_endpoint_num(ep_desc);
592*b1906ceaSLachlan Hodges 				musb->endpoints[MM81X_EP_MEM_RD].size =
593*b1906ceaSLachlan Hodges 					usb_endpoint_maxp(ep_desc);
594*b1906ceaSLachlan Hodges 			} else if (!musb->endpoints[MM81X_EP_REG_RD].addr) {
595*b1906ceaSLachlan Hodges 				musb->endpoints[MM81X_EP_REG_RD].addr =
596*b1906ceaSLachlan Hodges 					usb_endpoint_num(ep_desc);
597*b1906ceaSLachlan Hodges 				musb->endpoints[MM81X_EP_REG_RD].size =
598*b1906ceaSLachlan Hodges 					usb_endpoint_maxp(ep_desc);
599*b1906ceaSLachlan Hodges 			}
600*b1906ceaSLachlan Hodges 		} else if (usb_endpoint_is_bulk_out(ep_desc)) {
601*b1906ceaSLachlan Hodges 			if (!musb->endpoints[MM81X_EP_MEM_WR].addr) {
602*b1906ceaSLachlan Hodges 				musb->endpoints[MM81X_EP_MEM_WR].addr =
603*b1906ceaSLachlan Hodges 					usb_endpoint_num(ep_desc);
604*b1906ceaSLachlan Hodges 				musb->endpoints[MM81X_EP_MEM_WR].size =
605*b1906ceaSLachlan Hodges 					usb_endpoint_maxp(ep_desc);
606*b1906ceaSLachlan Hodges 			} else if (!musb->endpoints[MM81X_EP_REG_WR].addr) {
607*b1906ceaSLachlan Hodges 				musb->endpoints[MM81X_EP_REG_WR].addr =
608*b1906ceaSLachlan Hodges 					usb_endpoint_num(ep_desc);
609*b1906ceaSLachlan Hodges 				musb->endpoints[MM81X_EP_REG_WR].size =
610*b1906ceaSLachlan Hodges 					usb_endpoint_maxp(ep_desc);
611*b1906ceaSLachlan Hodges 			}
612*b1906ceaSLachlan Hodges 		} else if (usb_endpoint_is_int_in(ep_desc)) {
613*b1906ceaSLachlan Hodges 			musb->endpoints[MM81X_EP_INT].addr =
614*b1906ceaSLachlan Hodges 				usb_endpoint_num(ep_desc);
615*b1906ceaSLachlan Hodges 			musb->endpoints[MM81X_EP_INT].size =
616*b1906ceaSLachlan Hodges 				usb_endpoint_maxp(ep_desc);
617*b1906ceaSLachlan Hodges 		}
618*b1906ceaSLachlan Hodges 	}
619*b1906ceaSLachlan Hodges 
620*b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "\tMemory Endpoint IN %s detected: %u size %u",
621*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_MEM_RD].addr ? "" : "not",
622*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_MEM_RD].addr,
623*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_MEM_RD].size);
624*b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "\tMemory Endpoint OUT %s detected: %u size %u",
625*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_MEM_WR].addr ? "" : "not",
626*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_MEM_WR].addr,
627*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_MEM_WR].size);
628*b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "\tRegister Endpoint IN %s detected: %u",
629*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_REG_RD].addr ? "" : "not",
630*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_REG_RD].addr);
631*b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "\tRegister Endpoint OUT %s detected: %u",
632*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_REG_WR].addr ? "" : "not",
633*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_REG_WR].addr);
634*b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "\tStats IN endpoint %s detected: %u",
635*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_INT].addr ? "" : "not",
636*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_INT].addr);
637*b1906ceaSLachlan Hodges 
638*b1906ceaSLachlan Hodges 	/* Verify we have an IN and OUT */
639*b1906ceaSLachlan Hodges 	if (!(musb->endpoints[MM81X_EP_MEM_RD].addr &&
640*b1906ceaSLachlan Hodges 	      musb->endpoints[MM81X_EP_MEM_WR].addr))
641*b1906ceaSLachlan Hodges 		return -ENODEV;
642*b1906ceaSLachlan Hodges 
643*b1906ceaSLachlan Hodges 	/* Verify the stats MM81X_EP_INT is detected */
644*b1906ceaSLachlan Hodges 	if (!musb->endpoints[MM81X_EP_INT].addr)
645*b1906ceaSLachlan Hodges 		return -ENODEV;
646*b1906ceaSLachlan Hodges 
647*b1906ceaSLachlan Hodges 	/* Verify minimum interrupt status read */
648*b1906ceaSLachlan Hodges 	if (musb->endpoints[MM81X_EP_INT].size < 8)
649*b1906ceaSLachlan Hodges 		return -ENODEV;
650*b1906ceaSLachlan Hodges 
651*b1906ceaSLachlan Hodges 	musb->endpoints[MM81X_EP_CMD].urb = usb_alloc_urb(0, GFP_KERNEL);
652*b1906ceaSLachlan Hodges 	if (!musb->endpoints[MM81X_EP_CMD].urb) {
653*b1906ceaSLachlan Hodges 		ret = -ENOMEM;
654*b1906ceaSLachlan Hodges 		goto err_ep;
655*b1906ceaSLachlan Hodges 	}
656*b1906ceaSLachlan Hodges 
657*b1906ceaSLachlan Hodges 	musb->endpoints[MM81X_EP_MEM_RD].urb = usb_alloc_urb(0, GFP_KERNEL);
658*b1906ceaSLachlan Hodges 	if (!musb->endpoints[MM81X_EP_MEM_RD].urb) {
659*b1906ceaSLachlan Hodges 		ret = -ENOMEM;
660*b1906ceaSLachlan Hodges 		goto err_ep;
661*b1906ceaSLachlan Hodges 	}
662*b1906ceaSLachlan Hodges 
663*b1906ceaSLachlan Hodges 	musb->endpoints[MM81X_EP_MEM_WR].urb = usb_alloc_urb(0, GFP_KERNEL);
664*b1906ceaSLachlan Hodges 	if (!musb->endpoints[MM81X_EP_MEM_WR].urb) {
665*b1906ceaSLachlan Hodges 		ret = -ENOMEM;
666*b1906ceaSLachlan Hodges 		goto err_ep;
667*b1906ceaSLachlan Hodges 	}
668*b1906ceaSLachlan Hodges 
669*b1906ceaSLachlan Hodges 	musb->endpoints[MM81X_EP_MEM_RD].buffer =
670*b1906ceaSLachlan Hodges 		kmalloc(USB_MAX_TRANSFER_SIZE, GFP_KERNEL);
671*b1906ceaSLachlan Hodges 	if (!musb->endpoints[MM81X_EP_MEM_RD].buffer) {
672*b1906ceaSLachlan Hodges 		ret = -ENOMEM;
673*b1906ceaSLachlan Hodges 		goto err_ep;
674*b1906ceaSLachlan Hodges 	}
675*b1906ceaSLachlan Hodges 
676*b1906ceaSLachlan Hodges 	musb->endpoints[MM81X_EP_MEM_WR].buffer =
677*b1906ceaSLachlan Hodges 		kmalloc(USB_MAX_TRANSFER_SIZE, GFP_KERNEL);
678*b1906ceaSLachlan Hodges 	if (!musb->endpoints[MM81X_EP_MEM_WR].buffer) {
679*b1906ceaSLachlan Hodges 		ret = -ENOMEM;
680*b1906ceaSLachlan Hodges 		goto err_ep;
681*b1906ceaSLachlan Hodges 	}
682*b1906ceaSLachlan Hodges 
683*b1906ceaSLachlan Hodges 	musb->endpoints[MM81X_EP_CMD].buffer = usb_alloc_coherent(
684*b1906ceaSLachlan Hodges 		musb->udev, sizeof(struct mm81x_usb_command), GFP_KERNEL,
685*b1906ceaSLachlan Hodges 		&musb->endpoints[MM81X_EP_CMD].urb->transfer_dma);
686*b1906ceaSLachlan Hodges 
687*b1906ceaSLachlan Hodges 	if (!musb->endpoints[MM81X_EP_CMD].buffer) {
688*b1906ceaSLachlan Hodges 		ret = -ENOMEM;
689*b1906ceaSLachlan Hodges 		goto err_ep;
690*b1906ceaSLachlan Hodges 	}
691*b1906ceaSLachlan Hodges 
692*b1906ceaSLachlan Hodges 	/* Assign command to memory out end point */
693*b1906ceaSLachlan Hodges 	musb->endpoints[MM81X_EP_CMD].addr =
694*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_MEM_WR].addr;
695*b1906ceaSLachlan Hodges 	musb->endpoints[MM81X_EP_CMD].size =
696*b1906ceaSLachlan Hodges 		musb->endpoints[MM81X_EP_MEM_WR].size;
697*b1906ceaSLachlan Hodges 
698*b1906ceaSLachlan Hodges 	return 0;
699*b1906ceaSLachlan Hodges 
700*b1906ceaSLachlan Hodges err_ep:
701*b1906ceaSLachlan Hodges 	if (musb->endpoints[MM81X_EP_CMD].urb &&
702*b1906ceaSLachlan Hodges 	    musb->endpoints[MM81X_EP_CMD].buffer)
703*b1906ceaSLachlan Hodges 		usb_free_coherent(
704*b1906ceaSLachlan Hodges 			musb->udev, sizeof(struct mm81x_usb_command),
705*b1906ceaSLachlan Hodges 			musb->endpoints[MM81X_EP_CMD].buffer,
706*b1906ceaSLachlan Hodges 			musb->endpoints[MM81X_EP_CMD].urb->transfer_dma);
707*b1906ceaSLachlan Hodges 	usb_free_urb(musb->endpoints[MM81X_EP_MEM_RD].urb);
708*b1906ceaSLachlan Hodges 	usb_free_urb(musb->endpoints[MM81X_EP_CMD].urb);
709*b1906ceaSLachlan Hodges 	usb_free_urb(musb->endpoints[MM81X_EP_MEM_WR].urb);
710*b1906ceaSLachlan Hodges 	kfree(musb->endpoints[MM81X_EP_MEM_RD].buffer);
711*b1906ceaSLachlan Hodges 	kfree(musb->endpoints[MM81X_EP_MEM_WR].buffer);
712*b1906ceaSLachlan Hodges 
713*b1906ceaSLachlan Hodges 	return ret;
714*b1906ceaSLachlan Hodges }
715*b1906ceaSLachlan Hodges 
mm81x_urb_cleanup(struct mm81x * mors)716*b1906ceaSLachlan Hodges static void mm81x_urb_cleanup(struct mm81x *mors)
717*b1906ceaSLachlan Hodges {
718*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
719*b1906ceaSLachlan Hodges 	struct mm81x_usb_endpoint *int_ep = &musb->endpoints[MM81X_EP_INT];
720*b1906ceaSLachlan Hodges 	struct mm81x_usb_endpoint *rd_ep = &musb->endpoints[MM81X_EP_MEM_RD];
721*b1906ceaSLachlan Hodges 	struct mm81x_usb_endpoint *wr_ep = &musb->endpoints[MM81X_EP_MEM_WR];
722*b1906ceaSLachlan Hodges 	struct mm81x_usb_endpoint *cmd_ep = &musb->endpoints[MM81X_EP_CMD];
723*b1906ceaSLachlan Hodges 
724*b1906ceaSLachlan Hodges 	usb_kill_urb(rd_ep->urb);
725*b1906ceaSLachlan Hodges 	usb_kill_urb(wr_ep->urb);
726*b1906ceaSLachlan Hodges 	usb_kill_urb(cmd_ep->urb);
727*b1906ceaSLachlan Hodges 
728*b1906ceaSLachlan Hodges 	if (int_ep->urb)
729*b1906ceaSLachlan Hodges 		usb_free_coherent(musb->udev, MM81X_EP_INT_BUFFER_SIZE,
730*b1906ceaSLachlan Hodges 				  int_ep->buffer, int_ep->urb->transfer_dma);
731*b1906ceaSLachlan Hodges 
732*b1906ceaSLachlan Hodges 	if (cmd_ep->urb)
733*b1906ceaSLachlan Hodges 		usb_free_coherent(musb->udev, sizeof(struct mm81x_usb_command),
734*b1906ceaSLachlan Hodges 				  cmd_ep->buffer, cmd_ep->urb->transfer_dma);
735*b1906ceaSLachlan Hodges 
736*b1906ceaSLachlan Hodges 	kfree(wr_ep->buffer);
737*b1906ceaSLachlan Hodges 	kfree(rd_ep->buffer);
738*b1906ceaSLachlan Hodges 
739*b1906ceaSLachlan Hodges 	usb_free_urb(int_ep->urb);
740*b1906ceaSLachlan Hodges 	usb_free_urb(wr_ep->urb);
741*b1906ceaSLachlan Hodges 	usb_free_urb(rd_ep->urb);
742*b1906ceaSLachlan Hodges 	usb_free_urb(cmd_ep->urb);
743*b1906ceaSLachlan Hodges }
744*b1906ceaSLachlan Hodges 
mm81x_usb_probe(struct usb_interface * interface,const struct usb_device_id * id)745*b1906ceaSLachlan Hodges static int mm81x_usb_probe(struct usb_interface *interface,
746*b1906ceaSLachlan Hodges 			   const struct usb_device_id *id)
747*b1906ceaSLachlan Hodges {
748*b1906ceaSLachlan Hodges 	int ret;
749*b1906ceaSLachlan Hodges 	struct mm81x *mors;
750*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb;
751*b1906ceaSLachlan Hodges 
752*b1906ceaSLachlan Hodges 	mors = mm81x_core_alloc(sizeof(*musb), &interface->dev);
753*b1906ceaSLachlan Hodges 	if (!mors)
754*b1906ceaSLachlan Hodges 		return -ENOMEM;
755*b1906ceaSLachlan Hodges 
756*b1906ceaSLachlan Hodges 	mors->bus_ops = &mm81x_usb_ops;
757*b1906ceaSLachlan Hodges 	mors->bus_type = MM81X_BUS_TYPE_USB;
758*b1906ceaSLachlan Hodges 
759*b1906ceaSLachlan Hodges 	musb = (struct mm81x_usb *)mors->drv_priv;
760*b1906ceaSLachlan Hodges 	musb->udev = usb_get_dev(interface_to_usbdev(interface));
761*b1906ceaSLachlan Hodges 	musb->interface = usb_get_intf(interface);
762*b1906ceaSLachlan Hodges 
763*b1906ceaSLachlan Hodges 	mutex_init(&musb->lock);
764*b1906ceaSLachlan Hodges 	mutex_init(&musb->bus_lock);
765*b1906ceaSLachlan Hodges 	init_waitqueue_head(&musb->rw_in_wait);
766*b1906ceaSLachlan Hodges 	usb_set_intfdata(interface, mors);
767*b1906ceaSLachlan Hodges 
768*b1906ceaSLachlan Hodges 	ret = mm81x_usb_detect_endpoints(mors, interface);
769*b1906ceaSLachlan Hodges 	if (ret < 0)
770*b1906ceaSLachlan Hodges 		goto err_core_free;
771*b1906ceaSLachlan Hodges 
772*b1906ceaSLachlan Hodges 	set_bit(MM81X_USB_FLAG_ATTACHED, &musb->flags);
773*b1906ceaSLachlan Hodges 
774*b1906ceaSLachlan Hodges 	ret = mm81x_core_init(mors);
775*b1906ceaSLachlan Hodges 	if (ret)
776*b1906ceaSLachlan Hodges 		goto err_urb_cleanup;
777*b1906ceaSLachlan Hodges 
778*b1906ceaSLachlan Hodges 	INIT_WORK(&mors->usb_irq_work, mm81x_usb_irq_work);
779*b1906ceaSLachlan Hodges 
780*b1906ceaSLachlan Hodges 	ret = mm81x_usb_int_enable(mors);
781*b1906ceaSLachlan Hodges 	if (ret)
782*b1906ceaSLachlan Hodges 		goto err_core_deinit;
783*b1906ceaSLachlan Hodges 
784*b1906ceaSLachlan Hodges 	ret = mm81x_core_register(mors);
785*b1906ceaSLachlan Hodges 	if (ret)
786*b1906ceaSLachlan Hodges 		goto err_usb_int_stop;
787*b1906ceaSLachlan Hodges 
788*b1906ceaSLachlan Hodges 	/* USB requires remote wakeup functionality for suspend */
789*b1906ceaSLachlan Hodges 	clear_bit(MM81X_USB_FLAG_SUSPENDED, &musb->flags);
790*b1906ceaSLachlan Hodges 	musb->interface->needs_remote_wakeup = 1;
791*b1906ceaSLachlan Hodges 	usb_enable_autosuspend(musb->udev);
792*b1906ceaSLachlan Hodges 	pm_runtime_set_autosuspend_delay(&musb->udev->dev,
793*b1906ceaSLachlan Hodges 					 PM_RUNTIME_AUTOSUSPEND_DELAY_MS);
794*b1906ceaSLachlan Hodges 
795*b1906ceaSLachlan Hodges 	usb_autopm_get_interface(interface);
796*b1906ceaSLachlan Hodges 	return 0;
797*b1906ceaSLachlan Hodges 
798*b1906ceaSLachlan Hodges err_usb_int_stop:
799*b1906ceaSLachlan Hodges 	mm81x_usb_int_stop(mors);
800*b1906ceaSLachlan Hodges err_core_deinit:
801*b1906ceaSLachlan Hodges 	mm81x_core_deinit(mors);
802*b1906ceaSLachlan Hodges err_urb_cleanup:
803*b1906ceaSLachlan Hodges 	mm81x_urb_cleanup(mors);
804*b1906ceaSLachlan Hodges err_core_free:
805*b1906ceaSLachlan Hodges 	mm81x_core_free(mors);
806*b1906ceaSLachlan Hodges 	usb_put_intf(interface);
807*b1906ceaSLachlan Hodges 	usb_put_dev(interface_to_usbdev(interface));
808*b1906ceaSLachlan Hodges 	return ret;
809*b1906ceaSLachlan Hodges }
810*b1906ceaSLachlan Hodges 
mm81x_usb_disconnect(struct usb_interface * interface)811*b1906ceaSLachlan Hodges static void mm81x_usb_disconnect(struct usb_interface *interface)
812*b1906ceaSLachlan Hodges {
813*b1906ceaSLachlan Hodges 	struct mm81x *mors = usb_get_intfdata(interface);
814*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
815*b1906ceaSLachlan Hodges 	int minor = interface->minor;
816*b1906ceaSLachlan Hodges 	struct usb_device *udev = interface_to_usbdev(interface);
817*b1906ceaSLachlan Hodges 
818*b1906ceaSLachlan Hodges 	if (udev->state == USB_STATE_NOTATTACHED) {
819*b1906ceaSLachlan Hodges 		clear_bit(MM81X_USB_FLAG_ATTACHED, &musb->flags);
820*b1906ceaSLachlan Hodges 		set_bit(MM81X_STATE_CHIP_UNRESPONSIVE, &mors->state_flags);
821*b1906ceaSLachlan Hodges 		dev_dbg(mors->dev, "USB suddenly unplugged");
822*b1906ceaSLachlan Hodges 	}
823*b1906ceaSLachlan Hodges 
824*b1906ceaSLachlan Hodges 	usb_disable_autosuspend(udev);
825*b1906ceaSLachlan Hodges 
826*b1906ceaSLachlan Hodges 	if (test_bit(MM81X_USB_FLAG_SUSPENDED, &musb->flags)) {
827*b1906ceaSLachlan Hodges 		dev_dbg(mors->dev, "USB was suspended: release locks");
828*b1906ceaSLachlan Hodges 		mm81x_usb_release_bus(mors);
829*b1906ceaSLachlan Hodges 		mutex_unlock(&musb->lock);
830*b1906ceaSLachlan Hodges 	}
831*b1906ceaSLachlan Hodges 
832*b1906ceaSLachlan Hodges 	clear_bit(MM81X_USB_FLAG_SUSPENDED, &musb->flags);
833*b1906ceaSLachlan Hodges 
834*b1906ceaSLachlan Hodges 	mm81x_core_unregister(mors);
835*b1906ceaSLachlan Hodges 	mm81x_usb_int_stop(mors);
836*b1906ceaSLachlan Hodges 	mm81x_core_deinit(mors);
837*b1906ceaSLachlan Hodges 	mm81x_urb_cleanup(mors);
838*b1906ceaSLachlan Hodges 	mm81x_core_free(mors);
839*b1906ceaSLachlan Hodges 
840*b1906ceaSLachlan Hodges 	usb_autopm_put_interface(interface);
841*b1906ceaSLachlan Hodges 	usb_set_intfdata(interface, NULL);
842*b1906ceaSLachlan Hodges 	dev_info(&interface->dev, "USB Morse #%d now disconnected", minor);
843*b1906ceaSLachlan Hodges 	usb_put_intf(interface);
844*b1906ceaSLachlan Hodges 	usb_put_dev(udev);
845*b1906ceaSLachlan Hodges }
846*b1906ceaSLachlan Hodges 
mm81x_usb_suspend(struct usb_interface * intf,pm_message_t message)847*b1906ceaSLachlan Hodges static int mm81x_usb_suspend(struct usb_interface *intf, pm_message_t message)
848*b1906ceaSLachlan Hodges {
849*b1906ceaSLachlan Hodges 	struct mm81x *mors = usb_get_intfdata(intf);
850*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
851*b1906ceaSLachlan Hodges 	struct mm81x_usb_endpoint *int_ep = &musb->endpoints[MM81X_EP_INT];
852*b1906ceaSLachlan Hodges 	struct mm81x_usb_endpoint *rd_ep = &musb->endpoints[MM81X_EP_MEM_RD];
853*b1906ceaSLachlan Hodges 	struct mm81x_usb_endpoint *wr_ep = &musb->endpoints[MM81X_EP_MEM_WR];
854*b1906ceaSLachlan Hodges 	struct mm81x_usb_endpoint *cmd_ep = &musb->endpoints[MM81X_EP_CMD];
855*b1906ceaSLachlan Hodges 
856*b1906ceaSLachlan Hodges 	if (!test_bit(MM81X_USB_FLAG_ATTACHED, &musb->flags))
857*b1906ceaSLachlan Hodges 		return -ENODEV;
858*b1906ceaSLachlan Hodges 
859*b1906ceaSLachlan Hodges 	usb_kill_urb(int_ep->urb);
860*b1906ceaSLachlan Hodges 	usb_kill_urb(rd_ep->urb);
861*b1906ceaSLachlan Hodges 	usb_kill_urb(wr_ep->urb);
862*b1906ceaSLachlan Hodges 	usb_kill_urb(cmd_ep->urb);
863*b1906ceaSLachlan Hodges 
864*b1906ceaSLachlan Hodges 	/* Locking the bus. No USB communication after this point */
865*b1906ceaSLachlan Hodges 	mm81x_usb_claim_bus(mors);
866*b1906ceaSLachlan Hodges 	mutex_lock(&musb->lock);
867*b1906ceaSLachlan Hodges 
868*b1906ceaSLachlan Hodges 	set_bit(MM81X_USB_FLAG_SUSPENDED, &musb->flags);
869*b1906ceaSLachlan Hodges 	return 0;
870*b1906ceaSLachlan Hodges }
871*b1906ceaSLachlan Hodges 
mm81x_usb_resume(struct usb_interface * intf)872*b1906ceaSLachlan Hodges static int mm81x_usb_resume(struct usb_interface *intf)
873*b1906ceaSLachlan Hodges {
874*b1906ceaSLachlan Hodges 	struct mm81x *mors = usb_get_intfdata(intf);
875*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
876*b1906ceaSLachlan Hodges 	int ret;
877*b1906ceaSLachlan Hodges 	struct mm81x_usb_endpoint *int_ep = &musb->endpoints[MM81X_EP_INT];
878*b1906ceaSLachlan Hodges 
879*b1906ceaSLachlan Hodges 	if (!test_bit(MM81X_USB_FLAG_ATTACHED, &musb->flags))
880*b1906ceaSLachlan Hodges 		return -ENODEV;
881*b1906ceaSLachlan Hodges 
882*b1906ceaSLachlan Hodges 	ret = usb_submit_urb(int_ep->urb, GFP_KERNEL);
883*b1906ceaSLachlan Hodges 	if (ret)
884*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "Couldn't submit urb. Error number %d", ret);
885*b1906ceaSLachlan Hodges 
886*b1906ceaSLachlan Hodges 	mm81x_usb_release_bus(mors);
887*b1906ceaSLachlan Hodges 	mutex_unlock(&musb->lock);
888*b1906ceaSLachlan Hodges 
889*b1906ceaSLachlan Hodges 	clear_bit(MM81X_USB_FLAG_SUSPENDED, &musb->flags);
890*b1906ceaSLachlan Hodges 	return 0;
891*b1906ceaSLachlan Hodges }
892*b1906ceaSLachlan Hodges 
mm81x_usb_reset_resume(struct usb_interface * intf)893*b1906ceaSLachlan Hodges static int mm81x_usb_reset_resume(struct usb_interface *intf)
894*b1906ceaSLachlan Hodges {
895*b1906ceaSLachlan Hodges 	struct mm81x *mors = usb_get_intfdata(intf);
896*b1906ceaSLachlan Hodges 	struct mm81x_usb *musb = (struct mm81x_usb *)mors->drv_priv;
897*b1906ceaSLachlan Hodges 	int ret;
898*b1906ceaSLachlan Hodges 	struct mm81x_usb_endpoint *int_ep = &musb->endpoints[MM81X_EP_INT];
899*b1906ceaSLachlan Hodges 
900*b1906ceaSLachlan Hodges 	if (!test_bit(MM81X_USB_FLAG_ATTACHED, &musb->flags))
901*b1906ceaSLachlan Hodges 		return -ENODEV;
902*b1906ceaSLachlan Hodges 
903*b1906ceaSLachlan Hodges 	ret = usb_submit_urb(int_ep->urb, GFP_KERNEL);
904*b1906ceaSLachlan Hodges 	if (ret)
905*b1906ceaSLachlan Hodges 		dev_err(mors->dev, "Couldn't submit urb. Error number %d", ret);
906*b1906ceaSLachlan Hodges 
907*b1906ceaSLachlan Hodges 	mm81x_usb_release_bus(mors);
908*b1906ceaSLachlan Hodges 	mutex_unlock(&musb->lock);
909*b1906ceaSLachlan Hodges 
910*b1906ceaSLachlan Hodges 	clear_bit(MM81X_USB_FLAG_SUSPENDED, &musb->flags);
911*b1906ceaSLachlan Hodges 
912*b1906ceaSLachlan Hodges 	return 0;
913*b1906ceaSLachlan Hodges }
914*b1906ceaSLachlan Hodges 
mm81x_usb_pre_reset(struct usb_interface * intf)915*b1906ceaSLachlan Hodges static int mm81x_usb_pre_reset(struct usb_interface *intf)
916*b1906ceaSLachlan Hodges {
917*b1906ceaSLachlan Hodges 	return 0;
918*b1906ceaSLachlan Hodges }
919*b1906ceaSLachlan Hodges 
mm81x_usb_post_reset(struct usb_interface * intf)920*b1906ceaSLachlan Hodges static int mm81x_usb_post_reset(struct usb_interface *intf)
921*b1906ceaSLachlan Hodges {
922*b1906ceaSLachlan Hodges 	return 0;
923*b1906ceaSLachlan Hodges }
924*b1906ceaSLachlan Hodges 
925*b1906ceaSLachlan Hodges static struct usb_driver mm81x_usb_driver = {
926*b1906ceaSLachlan Hodges 	.name = "mm81x_usb",
927*b1906ceaSLachlan Hodges 	.probe = mm81x_usb_probe,
928*b1906ceaSLachlan Hodges 	.disconnect = mm81x_usb_disconnect,
929*b1906ceaSLachlan Hodges 	.suspend = mm81x_usb_suspend,
930*b1906ceaSLachlan Hodges 	.resume = mm81x_usb_resume,
931*b1906ceaSLachlan Hodges 	.reset_resume = mm81x_usb_reset_resume,
932*b1906ceaSLachlan Hodges 	.pre_reset = mm81x_usb_pre_reset,
933*b1906ceaSLachlan Hodges 	.post_reset = mm81x_usb_post_reset,
934*b1906ceaSLachlan Hodges 	.id_table = mm81x_usb_table,
935*b1906ceaSLachlan Hodges 	.supports_autosuspend = 1,
936*b1906ceaSLachlan Hodges 	.soft_unbind = 1,
937*b1906ceaSLachlan Hodges };
938*b1906ceaSLachlan Hodges 
939*b1906ceaSLachlan Hodges module_usb_driver(mm81x_usb_driver);
940*b1906ceaSLachlan Hodges 
941*b1906ceaSLachlan Hodges MODULE_AUTHOR("Morse Micro");
942*b1906ceaSLachlan Hodges MODULE_DESCRIPTION("Driver support for Morse Micro MM81X USB devices");
943*b1906ceaSLachlan Hodges MODULE_LICENSE("Dual BSD/GPL");
944