aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/patches-3.3/047-spi_message_queue.patch
blob: 9aff0be2a35803069651ecbfb6a01bcf5f567156 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
commit ffbbdd21329f3e15eeca6df2d4bc11c04d9d91c0
Author: Linus Walleij <linus.walleij@linaro.org>
Date:   Wed Feb 22 10:05:38 2012 +0100

    spi: create a message queueing infrastructure
    
    This rips the message queue in the PL022 driver out and pushes
    it into (optional) common infrastructure. Drivers that want to
    use the message pumping thread will need to define the new
    per-messags transfer methods and leave the deprecated transfer()
    method as NULL.
    
    Most of the design is described in the documentation changes that
    are included in this patch.
    
    Since there is a queue that need to be stopped when the system
    is suspending/resuming, two new calls are implemented for the
    device drivers to call in their suspend()/resume() functions:
    spi_master_suspend() and spi_master_resume().
    
    ChangeLog v1->v2:
    - Remove Kconfig entry and do not make the queue support optional
      at all, instead be more agressive and have it as part of the
      compulsory infrastructure.
    - If the .transfer() method is implemented, delete print a small
      deprecation notice and do not start the transfer pump.
    - Fix a bitrotted comment.
    ChangeLog v2->v3:
    - Fix up a problematic sequence courtesy of Chris Blair.
    - Stop rather than destroy the queue on suspend() courtesy of
      Chris Blair.
    
    Signed-off-by: Chris Blair <chris.blair@stericsson.com>
    Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
    Tested-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
    Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
    Signed-off-by: Grant Likely <grant.likely@secretlab.ca>

[Florian: dropped the changes on drivers/spi/spi-pl022.c, removed
the dev_info() about unqueued drivers still using the master function]

--- a/Documentation/spi/spi-summary
+++ b/Documentation/spi/spi-summary
@@ -1,7 +1,7 @@
 Overview of Linux kernel SPI support
 ====================================
 
-21-May-2007
+02-Feb-2012
 
 What is SPI?
 ------------
@@ -483,9 +483,9 @@ also initialize its own internal state.
 and those methods.)
 
 After you initialize the spi_master, then use spi_register_master() to
-publish it to the rest of the system.  At that time, device nodes for
-the controller and any predeclared spi devices will be made available,
-and the driver model core will take care of binding them to drivers.
+publish it to the rest of the system. At that time, device nodes for the
+controller and any predeclared spi devices will be made available, and
+the driver model core will take care of binding them to drivers.
 
 If you need to remove your SPI controller driver, spi_unregister_master()
 will reverse the effect of spi_register_master().
@@ -521,21 +521,53 @@ SPI MASTER METHODS
 		** When you code setup(), ASSUME that the controller
 		** is actively processing transfers for another device.
 
-    master->transfer(struct spi_device *spi, struct spi_message *message)
-    	This must not sleep.  Its responsibility is arrange that the
-	transfer happens and its complete() callback is issued.  The two
-	will normally happen later, after other transfers complete, and
-	if the controller is idle it will need to be kickstarted.
-
     master->cleanup(struct spi_device *spi)
 	Your controller driver may use spi_device.controller_state to hold
 	state it dynamically associates with that device.  If you do that,
 	be sure to provide the cleanup() method to free that state.
 
+    master->prepare_transfer_hardware(struct spi_master *master)
+	This will be called by the queue mechanism to signal to the driver
+	that a message is coming in soon, so the subsystem requests the
+	driver to prepare the transfer hardware by issuing this call.
+	This may sleep.
+
+    master->unprepare_transfer_hardware(struct spi_master *master)
+	This will be called by the queue mechanism to signal to the driver
+	that there are no more messages pending in the queue and it may
+	relax the hardware (e.g. by power management calls). This may sleep.
+
+    master->transfer_one_message(struct spi_master *master,
+				 struct spi_message *mesg)
+	The subsystem calls the driver to transfer a single message while
+	queuing transfers that arrive in the meantime. When the driver is
+	finished with this message, it must call
+	spi_finalize_current_message() so the subsystem can issue the next
+	transfer. This may sleep.
+
+    DEPRECATED METHODS
+
+    master->transfer(struct spi_device *spi, struct spi_message *message)
+	This must not sleep. Its responsibility is arrange that the
+	transfer happens and its complete() callback is issued. The two
+	will normally happen later, after other transfers complete, and
+	if the controller is idle it will need to be kickstarted. This
+	method is not used on queued controllers and must be NULL if
+	transfer_one_message() and (un)prepare_transfer_hardware() are
+	implemented.
+
 
 SPI MESSAGE QUEUE
 
-The bulk of the driver will be managing the I/O queue fed by transfer().
+If you are happy with the standard queueing mechanism provided by the
+SPI subsystem, just implement the queued methods specified above. Using
+the message queue has the upside of centralizing a lot of code and
+providing pure process-context execution of methods. The message queue
+can also be elevated to realtime priority on high-priority SPI traffic.
+
+Unless the queueing mechanism in the SPI subsystem is selected, the bulk
+of the driver will be managing the I/O queue fed by the now deprecated
+function transfer().
 
 That queue could be purely conceptual.  For example, a driver used only
 for low-frequency sensor access might be fine using synchronous PIO.
@@ -561,4 +593,6 @@ Stephen Street
 Mark Underwood
 Andrew Victor
 Vitaly Wool
-
+Grant Likely
+Mark Brown
+Linus Walleij
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -30,6 +30,9 @@
 #include <linux/of_spi.h>
 #include <linux/pm_runtime.h>
 #include <linux/export.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
 
 static void spidev_release(struct device *dev)
 {
@@ -507,6 +510,293 @@ spi_register_board_info(struct spi_board
 
 /*-------------------------------------------------------------------------*/
 
+/**
+ * spi_pump_messages - kthread work function which processes spi message queue
+ * @work: pointer to kthread work struct contained in the master struct
+ *
+ * This function checks if there is any spi message in the queue that
+ * needs processing and if so call out to the driver to initialize hardware
+ * and transfer each message.
+ *
+ */
+static void spi_pump_messages(struct kthread_work *work)
+{
+	struct spi_master *master =
+		container_of(work, struct spi_master, pump_messages);
+	unsigned long flags;
+	bool was_busy = false;
+	int ret;
+
+	/* Lock queue and check for queue work */
+	spin_lock_irqsave(&master->queue_lock, flags);
+	if (list_empty(&master->queue) || !master->running) {
+		if (master->busy) {
+			ret = master->unprepare_transfer_hardware(master);
+			if (ret) {
+				dev_err(&master->dev,
+					"failed to unprepare transfer hardware\n");
+				return;
+			}
+		}
+		master->busy = false;
+		spin_unlock_irqrestore(&master->queue_lock, flags);
+		return;
+	}
+
+	/* Make sure we are not already running a message */
+	if (master->cur_msg) {
+		spin_unlock_irqrestore(&master->queue_lock, flags);
+		return;
+	}
+	/* Extract head of queue */
+	master->cur_msg =
+	    list_entry(master->queue.next, struct spi_message, queue);
+
+	list_del_init(&master->cur_msg->queue);
+	if (master->busy)
+		was_busy = true;
+	else
+		master->busy = true;
+	spin_unlock_irqrestore(&master->queue_lock, flags);
+
+	if (!was_busy) {
+		ret = master->prepare_transfer_hardware(master);
+		if (ret) {
+			dev_err(&master->dev,
+				"failed to prepare transfer hardware\n");
+			return;
+		}
+	}
+
+	ret = master->transfer_one_message(master, master->cur_msg);
+	if (ret) {
+		dev_err(&master->dev,
+			"failed to transfer one message from queue\n");
+		return;
+	}
+}
+
+static int spi_init_queue(struct spi_master *master)
+{
+	struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
+
+	INIT_LIST_HEAD(&master->queue);
+	spin_lock_init(&master->queue_lock);
+
+	master->running = false;
+	master->busy = false;
+
+	init_kthread_worker(&master->kworker);
+	master->kworker_task = kthread_run(kthread_worker_fn,
+					   &master->kworker,
+					   dev_name(&master->dev));
+	if (IS_ERR(master->kworker_task)) {
+		dev_err(&master->dev, "failed to create message pump task\n");
+		return -ENOMEM;
+	}
+	init_kthread_work(&master->pump_messages, spi_pump_messages);
+
+	/*
+	 * Master config will indicate if this controller should run the
+	 * message pump with high (realtime) priority to reduce the transfer
+	 * latency on the bus by minimising the delay between a transfer
+	 * request and the scheduling of the message pump thread. Without this
+	 * setting the message pump thread will remain at default priority.
+	 */
+	if (master->rt) {
+		dev_info(&master->dev,
+			"will run message pump with realtime priority\n");
+		sched_setscheduler(master->kworker_task, SCHED_FIFO, &param);
+	}
+
+	return 0;
+}
+
+/**
+ * spi_get_next_queued_message() - called by driver to check for queued
+ * messages
+ * @master: the master to check for queued messages
+ *
+ * If there are more messages in the queue, the next message is returned from
+ * this call.
+ */
+struct spi_message *spi_get_next_queued_message(struct spi_master *master)
+{
+	struct spi_message *next;
+	unsigned long flags;
+
+	/* get a pointer to the next message, if any */
+	spin_lock_irqsave(&master->queue_lock, flags);
+	if (list_empty(&master->queue))
+		next = NULL;
+	else
+		next = list_entry(master->queue.next,
+				  struct spi_message, queue);
+	spin_unlock_irqrestore(&master->queue_lock, flags);
+
+	return next;
+}
+EXPORT_SYMBOL_GPL(spi_get_next_queued_message);
+
+/**
+ * spi_finalize_current_message() - the current message is complete
+ * @master: the master to return the message to
+ *
+ * Called by the driver to notify the core that the message in the front of the
+ * queue is complete and can be removed from the queue.
+ */
+void spi_finalize_current_message(struct spi_master *master)
+{
+	struct spi_message *mesg;
+	unsigned long flags;
+
+	spin_lock_irqsave(&master->queue_lock, flags);
+	mesg = master->cur_msg;
+	master->cur_msg = NULL;
+
+	queue_kthread_work(&master->kworker, &master->pump_messages);
+	spin_unlock_irqrestore(&master->queue_lock, flags);
+
+	mesg->state = NULL;
+	if (mesg->complete)
+		mesg->complete(mesg->context);
+}
+EXPORT_SYMBOL_GPL(spi_finalize_current_message);
+
+static int spi_start_queue(struct spi_master *master)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&master->queue_lock, flags);
+
+	if (master->running || master->busy) {
+		spin_unlock_irqrestore(&master->queue_lock, flags);
+		return -EBUSY;
+	}
+
+	master->running = true;
+	master->cur_msg = NULL;
+	spin_unlock_irqrestore(&master->queue_lock, flags);
+
+	queue_kthread_work(&master->kworker, &master->pump_messages);
+
+	return 0;
+}
+
+static int spi_stop_queue(struct spi_master *master)
+{
+	unsigned long flags;
+	unsigned limit = 500;
+	int ret = 0;
+
+	spin_lock_irqsave(&master->queue_lock, flags);
+
+	/*
+	 * This is a bit lame, but is optimized for the common execution path.
+	 * A wait_queue on the master->busy could be used, but then the common
+	 * execution path (pump_messages) would be required to call wake_up or
+	 * friends on every SPI message. Do this instead.
+	 */
+	while ((!list_empty(&master->queue) || master->busy) && limit--) {
+		spin_unlock_irqrestore(&master->queue_lock, flags);
+		msleep(10);
+		spin_lock_irqsave(&master->queue_lock, flags);
+	}
+
+	if (!list_empty(&master->queue) || master->busy)
+		ret = -EBUSY;
+	else
+		master->running = false;
+
+	spin_unlock_irqrestore(&master->queue_lock, flags);
+
+	if (ret) {
+		dev_warn(&master->dev,
+			 "could not stop message queue\n");
+		return ret;
+	}
+	return ret;
+}
+
+static int spi_destroy_queue(struct spi_master *master)
+{
+	int ret;
+
+	ret = spi_stop_queue(master);
+
+	/*
+	 * flush_kthread_worker will block until all work is done.
+	 * If the reason that stop_queue timed out is that the work will never
+	 * finish, then it does no good to call flush/stop thread, so
+	 * return anyway.
+	 */
+	if (ret) {
+		dev_err(&master->dev, "problem destroying queue\n");
+		return ret;
+	}
+
+	flush_kthread_worker(&master->kworker);
+	kthread_stop(master->kworker_task);
+
+	return 0;
+}
+
+/**
+ * spi_queued_transfer - transfer function for queued transfers
+ * @spi: spi device which is requesting transfer
+ * @msg: spi message which is to handled is queued to driver queue
+ */
+static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+	struct spi_master *master = spi->master;
+	unsigned long flags;
+
+	spin_lock_irqsave(&master->queue_lock, flags);
+
+	if (!master->running) {
+		spin_unlock_irqrestore(&master->queue_lock, flags);
+		return -ESHUTDOWN;
+	}
+	msg->actual_length = 0;
+	msg->status = -EINPROGRESS;
+
+	list_add_tail(&msg->queue, &master->queue);
+	if (master->running && !master->busy)
+		queue_kthread_work(&master->kworker, &master->pump_messages);
+
+	spin_unlock_irqrestore(&master->queue_lock, flags);
+	return 0;
+}
+
+static int spi_master_initialize_queue(struct spi_master *master)
+{
+	int ret;
+
+	master->queued = true;
+	master->transfer = spi_queued_transfer;
+
+	/* Initialize and start queue */
+	ret = spi_init_queue(master);
+	if (ret) {
+		dev_err(&master->dev, "problem initializing queue\n");
+		goto err_init_queue;
+	}
+	ret = spi_start_queue(master);
+	if (ret) {
+		dev_err(&master->dev, "problem starting queue\n");
+		goto err_start_queue;
+	}
+
+	return 0;
+
+err_start_queue:
+err_init_queue:
+	spi_destroy_queue(master);
+	return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
 static void spi_master_release(struct device *dev)
 {
 	struct spi_master *master;
@@ -522,6 +812,7 @@ static struct class spi_master_class = {
 };
 
 
+
 /**
  * spi_alloc_master - allocate SPI master controller
  * @dev: the controller, possibly using the platform_bus
@@ -621,6 +912,15 @@ int spi_register_master(struct spi_maste
 	dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
 			dynamic ? " (dynamic)" : "");
 
+	/* If we're using a queued driver, start the queue */
+	if (!master->transfer) {
+		status = spi_master_initialize_queue(master);
+		if (status) {
+			device_unregister(&master->dev);
+			goto done;
+		}
+	}
+
 	mutex_lock(&board_lock);
 	list_add_tail(&master->list, &spi_master_list);
 	list_for_each_entry(bi, &board_list, list)
@@ -636,7 +936,6 @@ done:
 }
 EXPORT_SYMBOL_GPL(spi_register_master);
 
-
 static int __unregister(struct device *dev, void *null)
 {
 	spi_unregister_device(to_spi_device(dev));
@@ -657,6 +956,11 @@ void spi_unregister_master(struct spi_ma
 {
 	int dummy;
 
+	if (master->queued) {
+		if (spi_destroy_queue(master))
+			dev_err(&master->dev, "queue remove failed\n");
+	}
+
 	mutex_lock(&board_lock);
 	list_del(&master->list);
 	mutex_unlock(&board_lock);
@@ -666,6 +970,37 @@ void spi_unregister_master(struct spi_ma
 }
 EXPORT_SYMBOL_GPL(spi_unregister_master);
 
+int spi_master_suspend(struct spi_master *master)
+{
+	int ret;
+
+	/* Basically no-ops for non-queued masters */
+	if (!master->queued)
+		return 0;
+
+	ret = spi_stop_queue(master);
+	if (ret)
+		dev_err(&master->dev, "queue stop failed\n");
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(spi_master_suspend);
+
+int spi_master_resume(struct spi_master *master)
+{
+	int ret;
+
+	if (!master->queued)
+		return 0;
+
+	ret = spi_start_queue(master);
+	if (ret)
+		dev_err(&master->dev, "queue restart failed\n");
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(spi_master_resume);
+
 static int __spi_master_match(struct device *dev, void *data)
 {
 	struct spi_master *m;
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -22,6 +22,7 @@
 #include <linux/device.h>
 #include <linux/mod_devicetable.h>
 #include <linux/slab.h>
+#include <linux/kthread.h>
 
 /*
  * INTERFACES between SPI master-side drivers and SPI infrastructure.
@@ -235,6 +236,27 @@ static inline void spi_unregister_driver
  *	the device whose settings are being modified.
  * @transfer: adds a message to the controller's transfer queue.
  * @cleanup: frees controller-specific state
+ * @queued: whether this master is providing an internal message queue
+ * @kworker: thread struct for message pump
+ * @kworker_task: pointer to task for message pump kworker thread
+ * @pump_messages: work struct for scheduling work to the message pump
+ * @queue_lock: spinlock to syncronise access to message queue
+ * @queue: message queue
+ * @cur_msg: the currently in-flight message
+ * @busy: message pump is busy
+ * @running: message pump is running
+ * @rt: whether this queue is set to run as a realtime task
+ * @prepare_transfer_hardware: a message will soon arrive from the queue
+ *	so the subsystem requests the driver to prepare the transfer hardware
+ *	by issuing this call
+ * @transfer_one_message: the subsystem calls the driver to transfer a single
+ *	message while queuing transfers that arrive in the meantime. When the
+ *	driver is finished with this message, it must call
+ *	spi_finalize_current_message() so the subsystem can issue the next
+ *	transfer
+ * @prepare_transfer_hardware: there are currently no more messages on the
+ *	queue so the subsystem notifies the driver that it may relax the
+ *	hardware by issuing this call
  *
  * Each SPI master controller can communicate with one or more @spi_device
  * children.  These make a small bus, sharing MOSI, MISO and SCK signals
@@ -318,6 +340,28 @@ struct spi_master {
 
 	/* called on release() to free memory provided by spi_master */
 	void			(*cleanup)(struct spi_device *spi);
+
+	/*
+	 * These hooks are for drivers that want to use the generic
+	 * master transfer queueing mechanism. If these are used, the
+	 * transfer() function above must NOT be specified by the driver.
+	 * Over time we expect SPI drivers to be phased over to this API.
+	 */
+	bool				queued;
+	struct kthread_worker		kworker;
+	struct task_struct		*kworker_task;
+	struct kthread_work		pump_messages;
+	spinlock_t			queue_lock;
+	struct list_head		queue;
+	struct spi_message		*cur_msg;
+	bool				busy;
+	bool				running;
+	bool				rt;
+
+	int (*prepare_transfer_hardware)(struct spi_master *master);
+	int (*transfer_one_message)(struct spi_master *master,
+				    struct spi_message *mesg);
+	int (*unprepare_transfer_hardware)(struct spi_master *master);
 };
 
 static inline void *spi_master_get_devdata(struct spi_master *master)
@@ -343,6 +387,13 @@ static inline void spi_master_put(struct
 		put_device(&master->dev);
 }
 
+/* PM calls that need to be issued by the driver */
+extern int spi_master_suspend(struct spi_master *master);
+extern int spi_master_resume(struct spi_master *master);
+
+/* Calls the driver make to interact with the message queue */
+extern struct spi_message *spi_get_next_queued_message(struct spi_master *master);
+extern void spi_finalize_current_message(struct spi_master *master);
 
 /* the spi driver core manages memory for the spi_master classdev */
 extern struct spi_master *