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