forked from notro/spi-bcm2708
-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathspi-optimize.patch
179 lines (168 loc) · 5.95 KB
/
spi-optimize.patch
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
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index d745f95..24a54aa 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1598,15 +1598,12 @@ int spi_setup(struct spi_device *spi)
}
EXPORT_SYMBOL_GPL(spi_setup);
-static int __spi_async(struct spi_device *spi, struct spi_message *message)
+static inline int __spi_verify(struct spi_device *spi,
+ struct spi_message *message)
{
struct spi_master *master = spi->master;
struct spi_transfer *xfer;
- message->spi = spi;
-
- trace_spi_message_submit(message);
-
if (list_empty(&message->transfers))
return -EINVAL;
if (!message->complete)
@@ -1705,9 +1702,28 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
return -EINVAL;
}
}
+ return 0;
+}
+
+static int __spi_async(struct spi_device *spi, struct spi_message *message)
+{
+ struct spi_master *master = spi->master;
+ int ret = 0;
+
+ trace_spi_message_submit(message);
+
+ if (message->is_optimized) {
+ if (spi != message->spi)
+ return -EINVAL;
+ } else {
+ message->spi = spi;
+ ret = __spi_verify(spi,message);
+ if (ret)
+ return ret;
+ }
message->status = -EINPROGRESS;
- return master->transfer(spi, message);
+ return spi->master->transfer(spi, message);
}
/**
@@ -1804,6 +1820,48 @@ int spi_async_locked(struct spi_device *spi, struct spi_message *message)
}
EXPORT_SYMBOL_GPL(spi_async_locked);
+/**
+ * spi_message_optimize - optimize a message for repeated use minimizing
+ * processing overhead
+ *
+ * @spi: device with which data will be exchanged
+ * @message: describes the data transfers, including completion callback
+ * Context: can sleep
+ */
+int spi_message_optimize(struct spi_device *spi,
+ struct spi_message *message)
+{
+ int ret = 0;
+ if (message->is_optimized)
+ spi_message_unoptimize(message);
+
+ message->spi = spi;
+ ret = __spi_verify(spi,message);
+ if (ret)
+ return ret;
+
+ if (spi->master->optimize_message)
+ ret = spi->master->optimize_message(message);
+ if (ret)
+ return ret;
+
+ message->is_optimized = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(spi_message_optimize);
+
+void spi_message_unoptimize(struct spi_message *message)
+{
+ if (!message->is_optimized)
+ return;
+
+ if (message->spi->master->unoptimize_message)
+ message->spi->master->unoptimize_message(message);
+
+ message->is_optimized = 0;
+}
+EXPORT_SYMBOL_GPL(spi_message_unoptimize);
/*-------------------------------------------------------------------------*/
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 8c62ba7..5206038 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -287,6 +287,12 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* spi_finalize_current_transfer() so the subsystem can issue
* the next transfer
* @unprepare_message: undo any work done by prepare_message().
+ * @optimize_message: allow computation of optimizations of a spi message
+ * this may set up the corresponding DMA transfers
+ * or do other work that need not get computed every
+ * time a message gets executed
+ * Not called from interrupt context.
+ * @unoptimize_message: undo any work done by @optimize_message().
* @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
* number. Any individual value may be -ENOENT for CS lines that
* are not GPIOs (driven by the SPI controller itself).
@@ -412,7 +418,8 @@ struct spi_master {
struct spi_message *message);
int (*unprepare_message)(struct spi_master *master,
struct spi_message *message);
-
+ int (*optimize_message)(struct spi_message *message);
+ void (*unoptimize_message)(struct spi_message *message);
/*
* These hooks are for drivers that use a generic implementation
* of transfer_one_message() provied by the core.
@@ -506,6 +513,8 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum);
* @delay_usecs: microseconds to delay after this transfer before
* (optionally) changing the chipselect status, then starting
* the next transfer or completing this @spi_message.
+ * @vary: allows a driver to mark a SPI transfer as "modifyable" on the
+ * specific pieces of information
* @transfer_list: transfers are sequenced through @spi_message.transfers
*
* SPI transfers always write the same number of bytes as they read.
@@ -584,6 +593,12 @@ struct spi_transfer {
u8 bits_per_word;
u16 delay_usecs;
u32 speed_hz;
+#define SPI_OPTIMIZE_VARY_TX_BUF (1<<0)
+#define SPI_OPTIMIZE_VARY_RX_BUF (1<<1)
+#define SPI_OPTIMIZE_VARY_SPEED_HZ (1<<2)
+#define SPI_OPTIMIZE_VARY_DELAY_USECS (1<<3)
+#define SPI_OPTIMIZE_VARY_LENGTH (1<<4)
+ u32 vary;
struct list_head transfer_list;
};
@@ -594,6 +609,9 @@ struct spi_transfer {
* @spi: SPI device to which the transaction is queued
* @is_dma_mapped: if true, the caller provided both dma and cpu virtual
* addresses for each transfer buffer
+ * @is_optimized: if true, then the spi_message has been processed with
+ * spi_message_optimize() - @state belongs to the spi-driver now
+ * and may not get set by the driver
* @complete: called to report transaction completions
* @context: the argument to complete() when it's called
* @actual_length: the total number of bytes that were transferred in all
@@ -622,6 +640,8 @@ struct spi_message {
struct spi_device *spi;
unsigned is_dma_mapped:1;
+#define SPI_HAVE_OPTIMIZE
+ unsigned is_optimized:1;
/* REVISIT: we might want a flag affecting the behavior of the
* last transfer ... allowing things like "read 16 bit length L"
@@ -655,6 +675,9 @@ static inline void spi_message_init(struct spi_message *m)
INIT_LIST_HEAD(&m->transfers);
}
+int spi_message_optimize(struct spi_device *s,struct spi_message *m);
+void spi_message_unoptimize(struct spi_message *m);
+
static inline void
spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{