blob: 1e43f187231e1a25fa95e3c03a7e861716837ad2 [file] [log] [blame]
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001/*
2 * offload engine driver for the Marvell XOR engine
3 * Copyright (C) 2007, 2008, Marvell International Ltd.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19#include <linux/init.h>
20#include <linux/module.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090021#include <linux/slab.h>
Saeed Bisharaff7b0472008-07-08 11:58:36 -070022#include <linux/delay.h>
23#include <linux/dma-mapping.h>
24#include <linux/spinlock.h>
25#include <linux/interrupt.h>
26#include <linux/platform_device.h>
27#include <linux/memory.h>
Andrew Lunnc5101822012-02-19 13:30:26 +010028#include <linux/clk.h>
Thomas Petazzonif7d12ef2012-11-15 16:47:58 +010029#include <linux/of.h>
30#include <linux/of_irq.h>
31#include <linux/irqdomain.h>
Arnd Bergmannc02cecb2012-08-24 15:21:54 +020032#include <linux/platform_data/dma-mv_xor.h>
Russell King - ARM Linuxd2ebfb32012-03-06 22:34:26 +000033
34#include "dmaengine.h"
Saeed Bisharaff7b0472008-07-08 11:58:36 -070035#include "mv_xor.h"
36
37static void mv_xor_issue_pending(struct dma_chan *chan);
38
39#define to_mv_xor_chan(chan) \
Thomas Petazzoni98817b92012-11-15 14:57:44 +010040 container_of(chan, struct mv_xor_chan, dmachan)
Saeed Bisharaff7b0472008-07-08 11:58:36 -070041
42#define to_mv_xor_slot(tx) \
43 container_of(tx, struct mv_xor_desc_slot, async_tx)
44
Thomas Petazzonic98c1782012-11-15 14:17:18 +010045#define mv_chan_to_devp(chan) \
Thomas Petazzoni1ef48a22012-11-15 15:17:05 +010046 ((chan)->dmadev.dev)
Thomas Petazzonic98c1782012-11-15 14:17:18 +010047
Saeed Bisharaff7b0472008-07-08 11:58:36 -070048static void mv_desc_init(struct mv_xor_desc_slot *desc, unsigned long flags)
49{
50 struct mv_xor_desc *hw_desc = desc->hw_desc;
51
52 hw_desc->status = (1 << 31);
53 hw_desc->phy_next_desc = 0;
54 hw_desc->desc_command = (1 << 31);
55}
56
Saeed Bisharaff7b0472008-07-08 11:58:36 -070057static void mv_desc_set_byte_count(struct mv_xor_desc_slot *desc,
58 u32 byte_count)
59{
60 struct mv_xor_desc *hw_desc = desc->hw_desc;
61 hw_desc->byte_count = byte_count;
62}
63
64static void mv_desc_set_next_desc(struct mv_xor_desc_slot *desc,
65 u32 next_desc_addr)
66{
67 struct mv_xor_desc *hw_desc = desc->hw_desc;
68 BUG_ON(hw_desc->phy_next_desc);
69 hw_desc->phy_next_desc = next_desc_addr;
70}
71
72static void mv_desc_clear_next_desc(struct mv_xor_desc_slot *desc)
73{
74 struct mv_xor_desc *hw_desc = desc->hw_desc;
75 hw_desc->phy_next_desc = 0;
76}
77
Saeed Bisharaff7b0472008-07-08 11:58:36 -070078static void mv_desc_set_dest_addr(struct mv_xor_desc_slot *desc,
79 dma_addr_t addr)
80{
81 struct mv_xor_desc *hw_desc = desc->hw_desc;
82 hw_desc->phy_dest_addr = addr;
83}
84
Saeed Bisharaff7b0472008-07-08 11:58:36 -070085static void mv_desc_set_src_addr(struct mv_xor_desc_slot *desc,
86 int index, dma_addr_t addr)
87{
88 struct mv_xor_desc *hw_desc = desc->hw_desc;
Thomas Petazzonie03bc652013-07-29 17:42:14 +020089 hw_desc->phy_src_addr[mv_phy_src_idx(index)] = addr;
Saeed Bisharaff7b0472008-07-08 11:58:36 -070090 if (desc->type == DMA_XOR)
91 hw_desc->desc_command |= (1 << index);
92}
93
94static u32 mv_chan_get_current_desc(struct mv_xor_chan *chan)
95{
Thomas Petazzoni5733c382013-07-29 17:42:13 +020096 return readl_relaxed(XOR_CURR_DESC(chan));
Saeed Bisharaff7b0472008-07-08 11:58:36 -070097}
98
99static void mv_chan_set_next_descriptor(struct mv_xor_chan *chan,
100 u32 next_desc_addr)
101{
Thomas Petazzoni5733c382013-07-29 17:42:13 +0200102 writel_relaxed(next_desc_addr, XOR_NEXT_DESC(chan));
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700103}
104
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700105static void mv_chan_unmask_interrupts(struct mv_xor_chan *chan)
106{
Thomas Petazzoni5733c382013-07-29 17:42:13 +0200107 u32 val = readl_relaxed(XOR_INTR_MASK(chan));
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700108 val |= XOR_INTR_MASK_VALUE << (chan->idx * 16);
Thomas Petazzoni5733c382013-07-29 17:42:13 +0200109 writel_relaxed(val, XOR_INTR_MASK(chan));
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700110}
111
112static u32 mv_chan_get_intr_cause(struct mv_xor_chan *chan)
113{
Thomas Petazzoni5733c382013-07-29 17:42:13 +0200114 u32 intr_cause = readl_relaxed(XOR_INTR_CAUSE(chan));
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700115 intr_cause = (intr_cause >> (chan->idx * 16)) & 0xFFFF;
116 return intr_cause;
117}
118
119static int mv_is_err_intr(u32 intr_cause)
120{
121 if (intr_cause & ((1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9)))
122 return 1;
123
124 return 0;
125}
126
127static void mv_xor_device_clear_eoc_cause(struct mv_xor_chan *chan)
128{
Simon Guinot86363682010-09-17 23:33:51 +0200129 u32 val = ~(1 << (chan->idx * 16));
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100130 dev_dbg(mv_chan_to_devp(chan), "%s, val 0x%08x\n", __func__, val);
Thomas Petazzoni5733c382013-07-29 17:42:13 +0200131 writel_relaxed(val, XOR_INTR_CAUSE(chan));
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700132}
133
134static void mv_xor_device_clear_err_status(struct mv_xor_chan *chan)
135{
136 u32 val = 0xFFFF0000 >> (chan->idx * 16);
Thomas Petazzoni5733c382013-07-29 17:42:13 +0200137 writel_relaxed(val, XOR_INTR_CAUSE(chan));
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700138}
139
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700140static void mv_set_mode(struct mv_xor_chan *chan,
141 enum dma_transaction_type type)
142{
143 u32 op_mode;
Thomas Petazzoni5733c382013-07-29 17:42:13 +0200144 u32 config = readl_relaxed(XOR_CONFIG(chan));
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700145
146 switch (type) {
147 case DMA_XOR:
148 op_mode = XOR_OPERATION_MODE_XOR;
149 break;
150 case DMA_MEMCPY:
151 op_mode = XOR_OPERATION_MODE_MEMCPY;
152 break;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700153 default:
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100154 dev_err(mv_chan_to_devp(chan),
Joe Perches1ba151c2012-10-28 01:05:44 -0700155 "error: unsupported operation %d\n",
Thomas Petazzonia3fc74b2012-11-15 12:50:27 +0100156 type);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700157 BUG();
158 return;
159 }
160
161 config &= ~0x7;
162 config |= op_mode;
Thomas Petazzonie03bc652013-07-29 17:42:14 +0200163
164#if defined(__BIG_ENDIAN)
165 config |= XOR_DESCRIPTOR_SWAP;
166#else
167 config &= ~XOR_DESCRIPTOR_SWAP;
168#endif
169
Thomas Petazzoni5733c382013-07-29 17:42:13 +0200170 writel_relaxed(config, XOR_CONFIG(chan));
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700171 chan->current_type = type;
172}
173
174static void mv_chan_activate(struct mv_xor_chan *chan)
175{
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100176 dev_dbg(mv_chan_to_devp(chan), " activate chan.\n");
Ezequiel Garcia5a9a55b2014-05-21 14:02:35 -0700177
178 /* writel ensures all descriptors are flushed before activation */
179 writel(BIT(0), XOR_ACTIVATION(chan));
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700180}
181
182static char mv_chan_is_busy(struct mv_xor_chan *chan)
183{
Thomas Petazzoni5733c382013-07-29 17:42:13 +0200184 u32 state = readl_relaxed(XOR_ACTIVATION(chan));
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700185
186 state = (state >> 4) & 0x3;
187
188 return (state == 1) ? 1 : 0;
189}
190
191static int mv_chan_xor_slot_count(size_t len, int src_cnt)
192{
193 return 1;
194}
195
196/**
197 * mv_xor_free_slots - flags descriptor slots for reuse
198 * @slot: Slot to free
199 * Caller must hold &mv_chan->lock while calling this function
200 */
201static void mv_xor_free_slots(struct mv_xor_chan *mv_chan,
202 struct mv_xor_desc_slot *slot)
203{
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100204 dev_dbg(mv_chan_to_devp(mv_chan), "%s %d slot %p\n",
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700205 __func__, __LINE__, slot);
206
207 slot->slots_per_op = 0;
208
209}
210
211/*
212 * mv_xor_start_new_chain - program the engine to operate on new chain headed by
213 * sw_desc
214 * Caller must hold &mv_chan->lock while calling this function
215 */
216static void mv_xor_start_new_chain(struct mv_xor_chan *mv_chan,
217 struct mv_xor_desc_slot *sw_desc)
218{
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100219 dev_dbg(mv_chan_to_devp(mv_chan), "%s %d: sw_desc %p\n",
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700220 __func__, __LINE__, sw_desc);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700221
Bartlomiej Zolnierkiewicz48a9db42013-07-03 15:05:06 -0700222 /* set the hardware chain */
223 mv_chan_set_next_descriptor(mv_chan, sw_desc->async_tx.phys);
224
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700225 mv_chan->pending += sw_desc->slot_cnt;
Thomas Petazzoni98817b92012-11-15 14:57:44 +0100226 mv_xor_issue_pending(&mv_chan->dmachan);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700227}
228
229static dma_cookie_t
230mv_xor_run_tx_complete_actions(struct mv_xor_desc_slot *desc,
231 struct mv_xor_chan *mv_chan, dma_cookie_t cookie)
232{
233 BUG_ON(desc->async_tx.cookie < 0);
234
235 if (desc->async_tx.cookie > 0) {
236 cookie = desc->async_tx.cookie;
237
238 /* call the callback (must not sleep or submit new
239 * operations to this channel)
240 */
241 if (desc->async_tx.callback)
242 desc->async_tx.callback(
243 desc->async_tx.callback_param);
244
Dan Williamsd38a8c62013-10-18 19:35:23 +0200245 dma_descriptor_unmap(&desc->async_tx);
Bartlomiej Zolnierkiewicz54f8d502013-10-18 19:35:32 +0200246 if (desc->group_head)
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700247 desc->group_head = NULL;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700248 }
249
250 /* run dependent operations */
Dan Williams07f22112009-01-05 17:14:31 -0700251 dma_run_dependencies(&desc->async_tx);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700252
253 return cookie;
254}
255
256static int
257mv_xor_clean_completed_slots(struct mv_xor_chan *mv_chan)
258{
259 struct mv_xor_desc_slot *iter, *_iter;
260
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100261 dev_dbg(mv_chan_to_devp(mv_chan), "%s %d\n", __func__, __LINE__);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700262 list_for_each_entry_safe(iter, _iter, &mv_chan->completed_slots,
263 completed_node) {
264
265 if (async_tx_test_ack(&iter->async_tx)) {
266 list_del(&iter->completed_node);
267 mv_xor_free_slots(mv_chan, iter);
268 }
269 }
270 return 0;
271}
272
273static int
274mv_xor_clean_slot(struct mv_xor_desc_slot *desc,
275 struct mv_xor_chan *mv_chan)
276{
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100277 dev_dbg(mv_chan_to_devp(mv_chan), "%s %d: desc %p flags %d\n",
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700278 __func__, __LINE__, desc, desc->async_tx.flags);
279 list_del(&desc->chain_node);
280 /* the client is allowed to attach dependent operations
281 * until 'ack' is set
282 */
283 if (!async_tx_test_ack(&desc->async_tx)) {
284 /* move this slot to the completed_slots */
285 list_add_tail(&desc->completed_node, &mv_chan->completed_slots);
286 return 0;
287 }
288
289 mv_xor_free_slots(mv_chan, desc);
290 return 0;
291}
292
293static void __mv_xor_slot_cleanup(struct mv_xor_chan *mv_chan)
294{
295 struct mv_xor_desc_slot *iter, *_iter;
296 dma_cookie_t cookie = 0;
297 int busy = mv_chan_is_busy(mv_chan);
298 u32 current_desc = mv_chan_get_current_desc(mv_chan);
299 int seen_current = 0;
300
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100301 dev_dbg(mv_chan_to_devp(mv_chan), "%s %d\n", __func__, __LINE__);
302 dev_dbg(mv_chan_to_devp(mv_chan), "current_desc %x\n", current_desc);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700303 mv_xor_clean_completed_slots(mv_chan);
304
305 /* free completed slots from the chain starting with
306 * the oldest descriptor
307 */
308
309 list_for_each_entry_safe(iter, _iter, &mv_chan->chain,
310 chain_node) {
311 prefetch(_iter);
312 prefetch(&_iter->async_tx);
313
314 /* do not advance past the current descriptor loaded into the
315 * hardware channel, subsequent descriptors are either in
316 * process or have not been submitted
317 */
318 if (seen_current)
319 break;
320
321 /* stop the search if we reach the current descriptor and the
322 * channel is busy
323 */
324 if (iter->async_tx.phys == current_desc) {
325 seen_current = 1;
326 if (busy)
327 break;
328 }
329
330 cookie = mv_xor_run_tx_complete_actions(iter, mv_chan, cookie);
331
332 if (mv_xor_clean_slot(iter, mv_chan))
333 break;
334 }
335
336 if ((busy == 0) && !list_empty(&mv_chan->chain)) {
337 struct mv_xor_desc_slot *chain_head;
338 chain_head = list_entry(mv_chan->chain.next,
339 struct mv_xor_desc_slot,
340 chain_node);
341
342 mv_xor_start_new_chain(mv_chan, chain_head);
343 }
344
345 if (cookie > 0)
Thomas Petazzoni98817b92012-11-15 14:57:44 +0100346 mv_chan->dmachan.completed_cookie = cookie;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700347}
348
349static void
350mv_xor_slot_cleanup(struct mv_xor_chan *mv_chan)
351{
352 spin_lock_bh(&mv_chan->lock);
353 __mv_xor_slot_cleanup(mv_chan);
354 spin_unlock_bh(&mv_chan->lock);
355}
356
357static void mv_xor_tasklet(unsigned long data)
358{
359 struct mv_xor_chan *chan = (struct mv_xor_chan *) data;
Saeed Bishara8333f652010-12-21 16:53:39 +0200360 mv_xor_slot_cleanup(chan);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700361}
362
363static struct mv_xor_desc_slot *
364mv_xor_alloc_slots(struct mv_xor_chan *mv_chan, int num_slots,
365 int slots_per_op)
366{
367 struct mv_xor_desc_slot *iter, *_iter, *alloc_start = NULL;
368 LIST_HEAD(chain);
369 int slots_found, retry = 0;
370
371 /* start search from the last allocated descrtiptor
372 * if a contiguous allocation can not be found start searching
373 * from the beginning of the list
374 */
375retry:
376 slots_found = 0;
377 if (retry == 0)
378 iter = mv_chan->last_used;
379 else
380 iter = list_entry(&mv_chan->all_slots,
381 struct mv_xor_desc_slot,
382 slot_node);
383
384 list_for_each_entry_safe_continue(
385 iter, _iter, &mv_chan->all_slots, slot_node) {
386 prefetch(_iter);
387 prefetch(&_iter->async_tx);
388 if (iter->slots_per_op) {
389 /* give up after finding the first busy slot
390 * on the second pass through the list
391 */
392 if (retry)
393 break;
394
395 slots_found = 0;
396 continue;
397 }
398
399 /* start the allocation if the slot is correctly aligned */
400 if (!slots_found++)
401 alloc_start = iter;
402
403 if (slots_found == num_slots) {
404 struct mv_xor_desc_slot *alloc_tail = NULL;
405 struct mv_xor_desc_slot *last_used = NULL;
406 iter = alloc_start;
407 while (num_slots) {
408 int i;
409
410 /* pre-ack all but the last descriptor */
411 async_tx_ack(&iter->async_tx);
412
413 list_add_tail(&iter->chain_node, &chain);
414 alloc_tail = iter;
415 iter->async_tx.cookie = 0;
416 iter->slot_cnt = num_slots;
417 iter->xor_check_result = NULL;
418 for (i = 0; i < slots_per_op; i++) {
419 iter->slots_per_op = slots_per_op - i;
420 last_used = iter;
421 iter = list_entry(iter->slot_node.next,
422 struct mv_xor_desc_slot,
423 slot_node);
424 }
425 num_slots -= slots_per_op;
426 }
427 alloc_tail->group_head = alloc_start;
428 alloc_tail->async_tx.cookie = -EBUSY;
Dan Williams64203b62009-09-08 17:53:03 -0700429 list_splice(&chain, &alloc_tail->tx_list);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700430 mv_chan->last_used = last_used;
431 mv_desc_clear_next_desc(alloc_start);
432 mv_desc_clear_next_desc(alloc_tail);
433 return alloc_tail;
434 }
435 }
436 if (!retry++)
437 goto retry;
438
439 /* try to free some slots if the allocation fails */
440 tasklet_schedule(&mv_chan->irq_tasklet);
441
442 return NULL;
443}
444
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700445/************************ DMA engine API functions ****************************/
446static dma_cookie_t
447mv_xor_tx_submit(struct dma_async_tx_descriptor *tx)
448{
449 struct mv_xor_desc_slot *sw_desc = to_mv_xor_slot(tx);
450 struct mv_xor_chan *mv_chan = to_mv_xor_chan(tx->chan);
451 struct mv_xor_desc_slot *grp_start, *old_chain_tail;
452 dma_cookie_t cookie;
453 int new_hw_chain = 1;
454
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100455 dev_dbg(mv_chan_to_devp(mv_chan),
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700456 "%s sw_desc %p: async_tx %p\n",
457 __func__, sw_desc, &sw_desc->async_tx);
458
459 grp_start = sw_desc->group_head;
460
461 spin_lock_bh(&mv_chan->lock);
Russell King - ARM Linux884485e2012-03-06 22:34:46 +0000462 cookie = dma_cookie_assign(tx);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700463
464 if (list_empty(&mv_chan->chain))
Dan Williams64203b62009-09-08 17:53:03 -0700465 list_splice_init(&sw_desc->tx_list, &mv_chan->chain);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700466 else {
467 new_hw_chain = 0;
468
469 old_chain_tail = list_entry(mv_chan->chain.prev,
470 struct mv_xor_desc_slot,
471 chain_node);
Dan Williams64203b62009-09-08 17:53:03 -0700472 list_splice_init(&grp_start->tx_list,
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700473 &old_chain_tail->chain_node);
474
Olof Johansson31fd8f52014-02-03 17:13:23 -0800475 dev_dbg(mv_chan_to_devp(mv_chan), "Append to last desc %pa\n",
476 &old_chain_tail->async_tx.phys);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700477
478 /* fix up the hardware chain */
479 mv_desc_set_next_desc(old_chain_tail, grp_start->async_tx.phys);
480
481 /* if the channel is not busy */
482 if (!mv_chan_is_busy(mv_chan)) {
483 u32 current_desc = mv_chan_get_current_desc(mv_chan);
484 /*
485 * and the curren desc is the end of the chain before
486 * the append, then we need to start the channel
487 */
488 if (current_desc == old_chain_tail->async_tx.phys)
489 new_hw_chain = 1;
490 }
491 }
492
493 if (new_hw_chain)
494 mv_xor_start_new_chain(mv_chan, grp_start);
495
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700496 spin_unlock_bh(&mv_chan->lock);
497
498 return cookie;
499}
500
501/* returns the number of allocated descriptors */
Dan Williamsaa1e6f12009-01-06 11:38:17 -0700502static int mv_xor_alloc_chan_resources(struct dma_chan *chan)
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700503{
Olof Johansson31fd8f52014-02-03 17:13:23 -0800504 void *virt_desc;
505 dma_addr_t dma_desc;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700506 int idx;
507 struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan);
508 struct mv_xor_desc_slot *slot = NULL;
Thomas Petazzonib503fa02012-11-15 15:55:30 +0100509 int num_descs_in_pool = MV_XOR_POOL_SIZE/MV_XOR_SLOT_SIZE;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700510
511 /* Allocate descriptor slots */
512 idx = mv_chan->slots_allocated;
513 while (idx < num_descs_in_pool) {
514 slot = kzalloc(sizeof(*slot), GFP_KERNEL);
515 if (!slot) {
Ezequiel Garciab8291dd2014-08-27 10:52:49 -0300516 dev_info(mv_chan_to_devp(mv_chan),
517 "channel only initialized %d descriptor slots",
518 idx);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700519 break;
520 }
Olof Johansson31fd8f52014-02-03 17:13:23 -0800521 virt_desc = mv_chan->dma_desc_pool_virt;
522 slot->hw_desc = virt_desc + idx * MV_XOR_SLOT_SIZE;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700523
524 dma_async_tx_descriptor_init(&slot->async_tx, chan);
525 slot->async_tx.tx_submit = mv_xor_tx_submit;
526 INIT_LIST_HEAD(&slot->chain_node);
527 INIT_LIST_HEAD(&slot->slot_node);
Dan Williams64203b62009-09-08 17:53:03 -0700528 INIT_LIST_HEAD(&slot->tx_list);
Olof Johansson31fd8f52014-02-03 17:13:23 -0800529 dma_desc = mv_chan->dma_desc_pool;
530 slot->async_tx.phys = dma_desc + idx * MV_XOR_SLOT_SIZE;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700531 slot->idx = idx++;
532
533 spin_lock_bh(&mv_chan->lock);
534 mv_chan->slots_allocated = idx;
535 list_add_tail(&slot->slot_node, &mv_chan->all_slots);
536 spin_unlock_bh(&mv_chan->lock);
537 }
538
539 if (mv_chan->slots_allocated && !mv_chan->last_used)
540 mv_chan->last_used = list_entry(mv_chan->all_slots.next,
541 struct mv_xor_desc_slot,
542 slot_node);
543
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100544 dev_dbg(mv_chan_to_devp(mv_chan),
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700545 "allocated %d descriptor slots last_used: %p\n",
546 mv_chan->slots_allocated, mv_chan->last_used);
547
548 return mv_chan->slots_allocated ? : -ENOMEM;
549}
550
551static struct dma_async_tx_descriptor *
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700552mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src,
553 unsigned int src_cnt, size_t len, unsigned long flags)
554{
555 struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan);
556 struct mv_xor_desc_slot *sw_desc, *grp_start;
557 int slot_cnt;
558
559 if (unlikely(len < MV_XOR_MIN_BYTE_COUNT))
560 return NULL;
561
Coly Li7912d302011-03-27 01:26:53 +0800562 BUG_ON(len > MV_XOR_MAX_BYTE_COUNT);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700563
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100564 dev_dbg(mv_chan_to_devp(mv_chan),
Olof Johansson31fd8f52014-02-03 17:13:23 -0800565 "%s src_cnt: %d len: %u dest %pad flags: %ld\n",
566 __func__, src_cnt, len, &dest, flags);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700567
568 spin_lock_bh(&mv_chan->lock);
569 slot_cnt = mv_chan_xor_slot_count(len, src_cnt);
570 sw_desc = mv_xor_alloc_slots(mv_chan, slot_cnt, 1);
571 if (sw_desc) {
572 sw_desc->type = DMA_XOR;
573 sw_desc->async_tx.flags = flags;
574 grp_start = sw_desc->group_head;
575 mv_desc_init(grp_start, flags);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700576 mv_desc_set_byte_count(grp_start, len);
577 mv_desc_set_dest_addr(sw_desc->group_head, dest);
578 sw_desc->unmap_src_cnt = src_cnt;
579 sw_desc->unmap_len = len;
580 while (src_cnt--)
581 mv_desc_set_src_addr(grp_start, src_cnt, src[src_cnt]);
582 }
583 spin_unlock_bh(&mv_chan->lock);
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100584 dev_dbg(mv_chan_to_devp(mv_chan),
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700585 "%s sw_desc %p async_tx %p \n",
586 __func__, sw_desc, &sw_desc->async_tx);
587 return sw_desc ? &sw_desc->async_tx : NULL;
588}
589
Lior Amsalem3e4f52e2014-08-27 10:52:50 -0300590static struct dma_async_tx_descriptor *
591mv_xor_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
592 size_t len, unsigned long flags)
593{
594 /*
595 * A MEMCPY operation is identical to an XOR operation with only
596 * a single source address.
597 */
598 return mv_xor_prep_dma_xor(chan, dest, &src, 1, len, flags);
599}
600
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700601static void mv_xor_free_chan_resources(struct dma_chan *chan)
602{
603 struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan);
604 struct mv_xor_desc_slot *iter, *_iter;
605 int in_use_descs = 0;
606
607 mv_xor_slot_cleanup(mv_chan);
608
609 spin_lock_bh(&mv_chan->lock);
610 list_for_each_entry_safe(iter, _iter, &mv_chan->chain,
611 chain_node) {
612 in_use_descs++;
613 list_del(&iter->chain_node);
614 }
615 list_for_each_entry_safe(iter, _iter, &mv_chan->completed_slots,
616 completed_node) {
617 in_use_descs++;
618 list_del(&iter->completed_node);
619 }
620 list_for_each_entry_safe_reverse(
621 iter, _iter, &mv_chan->all_slots, slot_node) {
622 list_del(&iter->slot_node);
623 kfree(iter);
624 mv_chan->slots_allocated--;
625 }
626 mv_chan->last_used = NULL;
627
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100628 dev_dbg(mv_chan_to_devp(mv_chan), "%s slots_allocated %d\n",
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700629 __func__, mv_chan->slots_allocated);
630 spin_unlock_bh(&mv_chan->lock);
631
632 if (in_use_descs)
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100633 dev_err(mv_chan_to_devp(mv_chan),
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700634 "freeing %d in use descriptors!\n", in_use_descs);
635}
636
637/**
Linus Walleij07934482010-03-26 16:50:49 -0700638 * mv_xor_status - poll the status of an XOR transaction
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700639 * @chan: XOR channel handle
640 * @cookie: XOR transaction identifier
Linus Walleij07934482010-03-26 16:50:49 -0700641 * @txstate: XOR transactions state holder (or NULL)
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700642 */
Linus Walleij07934482010-03-26 16:50:49 -0700643static enum dma_status mv_xor_status(struct dma_chan *chan,
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700644 dma_cookie_t cookie,
Linus Walleij07934482010-03-26 16:50:49 -0700645 struct dma_tx_state *txstate)
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700646{
647 struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700648 enum dma_status ret;
649
Russell King - ARM Linux96a2af42012-03-06 22:35:27 +0000650 ret = dma_cookie_status(chan, cookie, txstate);
Vinod Koulb3efb8f2013-10-16 20:51:04 +0530651 if (ret == DMA_COMPLETE) {
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700652 mv_xor_clean_completed_slots(mv_chan);
653 return ret;
654 }
655 mv_xor_slot_cleanup(mv_chan);
656
Russell King - ARM Linux96a2af42012-03-06 22:35:27 +0000657 return dma_cookie_status(chan, cookie, txstate);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700658}
659
660static void mv_dump_xor_regs(struct mv_xor_chan *chan)
661{
662 u32 val;
663
Thomas Petazzoni5733c382013-07-29 17:42:13 +0200664 val = readl_relaxed(XOR_CONFIG(chan));
Joe Perches1ba151c2012-10-28 01:05:44 -0700665 dev_err(mv_chan_to_devp(chan), "config 0x%08x\n", val);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700666
Thomas Petazzoni5733c382013-07-29 17:42:13 +0200667 val = readl_relaxed(XOR_ACTIVATION(chan));
Joe Perches1ba151c2012-10-28 01:05:44 -0700668 dev_err(mv_chan_to_devp(chan), "activation 0x%08x\n", val);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700669
Thomas Petazzoni5733c382013-07-29 17:42:13 +0200670 val = readl_relaxed(XOR_INTR_CAUSE(chan));
Joe Perches1ba151c2012-10-28 01:05:44 -0700671 dev_err(mv_chan_to_devp(chan), "intr cause 0x%08x\n", val);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700672
Thomas Petazzoni5733c382013-07-29 17:42:13 +0200673 val = readl_relaxed(XOR_INTR_MASK(chan));
Joe Perches1ba151c2012-10-28 01:05:44 -0700674 dev_err(mv_chan_to_devp(chan), "intr mask 0x%08x\n", val);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700675
Thomas Petazzoni5733c382013-07-29 17:42:13 +0200676 val = readl_relaxed(XOR_ERROR_CAUSE(chan));
Joe Perches1ba151c2012-10-28 01:05:44 -0700677 dev_err(mv_chan_to_devp(chan), "error cause 0x%08x\n", val);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700678
Thomas Petazzoni5733c382013-07-29 17:42:13 +0200679 val = readl_relaxed(XOR_ERROR_ADDR(chan));
Joe Perches1ba151c2012-10-28 01:05:44 -0700680 dev_err(mv_chan_to_devp(chan), "error addr 0x%08x\n", val);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700681}
682
683static void mv_xor_err_interrupt_handler(struct mv_xor_chan *chan,
684 u32 intr_cause)
685{
686 if (intr_cause & (1 << 4)) {
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100687 dev_dbg(mv_chan_to_devp(chan),
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700688 "ignore this error\n");
689 return;
690 }
691
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100692 dev_err(mv_chan_to_devp(chan),
Joe Perches1ba151c2012-10-28 01:05:44 -0700693 "error on chan %d. intr cause 0x%08x\n",
Thomas Petazzonia3fc74b2012-11-15 12:50:27 +0100694 chan->idx, intr_cause);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700695
696 mv_dump_xor_regs(chan);
697 BUG();
698}
699
700static irqreturn_t mv_xor_interrupt_handler(int irq, void *data)
701{
702 struct mv_xor_chan *chan = data;
703 u32 intr_cause = mv_chan_get_intr_cause(chan);
704
Thomas Petazzonic98c1782012-11-15 14:17:18 +0100705 dev_dbg(mv_chan_to_devp(chan), "intr cause %x\n", intr_cause);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700706
707 if (mv_is_err_intr(intr_cause))
708 mv_xor_err_interrupt_handler(chan, intr_cause);
709
710 tasklet_schedule(&chan->irq_tasklet);
711
712 mv_xor_device_clear_eoc_cause(chan);
713
714 return IRQ_HANDLED;
715}
716
717static void mv_xor_issue_pending(struct dma_chan *chan)
718{
719 struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan);
720
721 if (mv_chan->pending >= MV_XOR_THRESHOLD) {
722 mv_chan->pending = 0;
723 mv_chan_activate(mv_chan);
724 }
725}
726
727/*
728 * Perform a transaction to verify the HW works.
729 */
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700730
Linus Torvaldsc2714332012-12-14 14:54:26 -0800731static int mv_xor_memcpy_self_test(struct mv_xor_chan *mv_chan)
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700732{
733 int i;
734 void *src, *dest;
735 dma_addr_t src_dma, dest_dma;
736 struct dma_chan *dma_chan;
737 dma_cookie_t cookie;
738 struct dma_async_tx_descriptor *tx;
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300739 struct dmaengine_unmap_data *unmap;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700740 int err = 0;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700741
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300742 src = kmalloc(sizeof(u8) * PAGE_SIZE, GFP_KERNEL);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700743 if (!src)
744 return -ENOMEM;
745
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300746 dest = kzalloc(sizeof(u8) * PAGE_SIZE, GFP_KERNEL);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700747 if (!dest) {
748 kfree(src);
749 return -ENOMEM;
750 }
751
752 /* Fill in src buffer */
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300753 for (i = 0; i < PAGE_SIZE; i++)
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700754 ((u8 *) src)[i] = (u8)i;
755
Thomas Petazzoni275cc0c2012-11-15 15:09:42 +0100756 dma_chan = &mv_chan->dmachan;
Dan Williamsaa1e6f12009-01-06 11:38:17 -0700757 if (mv_xor_alloc_chan_resources(dma_chan) < 1) {
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700758 err = -ENODEV;
759 goto out;
760 }
761
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300762 unmap = dmaengine_get_unmap_data(dma_chan->device->dev, 2, GFP_KERNEL);
763 if (!unmap) {
764 err = -ENOMEM;
765 goto free_resources;
766 }
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700767
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300768 src_dma = dma_map_page(dma_chan->device->dev, virt_to_page(src), 0,
769 PAGE_SIZE, DMA_TO_DEVICE);
770 unmap->to_cnt = 1;
771 unmap->addr[0] = src_dma;
772
773 dest_dma = dma_map_page(dma_chan->device->dev, virt_to_page(dest), 0,
774 PAGE_SIZE, DMA_FROM_DEVICE);
775 unmap->from_cnt = 1;
776 unmap->addr[1] = dest_dma;
777
778 unmap->len = PAGE_SIZE;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700779
780 tx = mv_xor_prep_dma_memcpy(dma_chan, dest_dma, src_dma,
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300781 PAGE_SIZE, 0);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700782 cookie = mv_xor_tx_submit(tx);
783 mv_xor_issue_pending(dma_chan);
784 async_tx_ack(tx);
785 msleep(1);
786
Linus Walleij07934482010-03-26 16:50:49 -0700787 if (mv_xor_status(dma_chan, cookie, NULL) !=
Vinod Koulb3efb8f2013-10-16 20:51:04 +0530788 DMA_COMPLETE) {
Thomas Petazzonia3fc74b2012-11-15 12:50:27 +0100789 dev_err(dma_chan->device->dev,
790 "Self-test copy timed out, disabling\n");
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700791 err = -ENODEV;
792 goto free_resources;
793 }
794
Thomas Petazzonic35064c2012-11-15 13:01:59 +0100795 dma_sync_single_for_cpu(dma_chan->device->dev, dest_dma,
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300796 PAGE_SIZE, DMA_FROM_DEVICE);
797 if (memcmp(src, dest, PAGE_SIZE)) {
Thomas Petazzonia3fc74b2012-11-15 12:50:27 +0100798 dev_err(dma_chan->device->dev,
799 "Self-test copy failed compare, disabling\n");
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700800 err = -ENODEV;
801 goto free_resources;
802 }
803
804free_resources:
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300805 dmaengine_unmap_put(unmap);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700806 mv_xor_free_chan_resources(dma_chan);
807out:
808 kfree(src);
809 kfree(dest);
810 return err;
811}
812
813#define MV_XOR_NUM_SRC_TEST 4 /* must be <= 15 */
Bill Pemberton463a1f82012-11-19 13:22:55 -0500814static int
Thomas Petazzoni275cc0c2012-11-15 15:09:42 +0100815mv_xor_xor_self_test(struct mv_xor_chan *mv_chan)
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700816{
817 int i, src_idx;
818 struct page *dest;
819 struct page *xor_srcs[MV_XOR_NUM_SRC_TEST];
820 dma_addr_t dma_srcs[MV_XOR_NUM_SRC_TEST];
821 dma_addr_t dest_dma;
822 struct dma_async_tx_descriptor *tx;
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300823 struct dmaengine_unmap_data *unmap;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700824 struct dma_chan *dma_chan;
825 dma_cookie_t cookie;
826 u8 cmp_byte = 0;
827 u32 cmp_word;
828 int err = 0;
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300829 int src_count = MV_XOR_NUM_SRC_TEST;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700830
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300831 for (src_idx = 0; src_idx < src_count; src_idx++) {
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700832 xor_srcs[src_idx] = alloc_page(GFP_KERNEL);
Roel Kluina09b09a2009-02-25 13:56:21 +0100833 if (!xor_srcs[src_idx]) {
834 while (src_idx--)
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700835 __free_page(xor_srcs[src_idx]);
Roel Kluina09b09a2009-02-25 13:56:21 +0100836 return -ENOMEM;
837 }
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700838 }
839
840 dest = alloc_page(GFP_KERNEL);
Roel Kluina09b09a2009-02-25 13:56:21 +0100841 if (!dest) {
842 while (src_idx--)
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700843 __free_page(xor_srcs[src_idx]);
Roel Kluina09b09a2009-02-25 13:56:21 +0100844 return -ENOMEM;
845 }
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700846
847 /* Fill in src buffers */
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300848 for (src_idx = 0; src_idx < src_count; src_idx++) {
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700849 u8 *ptr = page_address(xor_srcs[src_idx]);
850 for (i = 0; i < PAGE_SIZE; i++)
851 ptr[i] = (1 << src_idx);
852 }
853
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300854 for (src_idx = 0; src_idx < src_count; src_idx++)
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700855 cmp_byte ^= (u8) (1 << src_idx);
856
857 cmp_word = (cmp_byte << 24) | (cmp_byte << 16) |
858 (cmp_byte << 8) | cmp_byte;
859
860 memset(page_address(dest), 0, PAGE_SIZE);
861
Thomas Petazzoni275cc0c2012-11-15 15:09:42 +0100862 dma_chan = &mv_chan->dmachan;
Dan Williamsaa1e6f12009-01-06 11:38:17 -0700863 if (mv_xor_alloc_chan_resources(dma_chan) < 1) {
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700864 err = -ENODEV;
865 goto out;
866 }
867
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300868 unmap = dmaengine_get_unmap_data(dma_chan->device->dev, src_count + 1,
869 GFP_KERNEL);
870 if (!unmap) {
871 err = -ENOMEM;
872 goto free_resources;
873 }
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700874
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300875 /* test xor */
876 for (i = 0; i < src_count; i++) {
877 unmap->addr[i] = dma_map_page(dma_chan->device->dev, xor_srcs[i],
878 0, PAGE_SIZE, DMA_TO_DEVICE);
879 dma_srcs[i] = unmap->addr[i];
880 unmap->to_cnt++;
881 }
882
883 unmap->addr[src_count] = dma_map_page(dma_chan->device->dev, dest, 0, PAGE_SIZE,
884 DMA_FROM_DEVICE);
885 dest_dma = unmap->addr[src_count];
886 unmap->from_cnt = 1;
887 unmap->len = PAGE_SIZE;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700888
889 tx = mv_xor_prep_dma_xor(dma_chan, dest_dma, dma_srcs,
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300890 src_count, PAGE_SIZE, 0);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700891
892 cookie = mv_xor_tx_submit(tx);
893 mv_xor_issue_pending(dma_chan);
894 async_tx_ack(tx);
895 msleep(8);
896
Linus Walleij07934482010-03-26 16:50:49 -0700897 if (mv_xor_status(dma_chan, cookie, NULL) !=
Vinod Koulb3efb8f2013-10-16 20:51:04 +0530898 DMA_COMPLETE) {
Thomas Petazzonia3fc74b2012-11-15 12:50:27 +0100899 dev_err(dma_chan->device->dev,
900 "Self-test xor timed out, disabling\n");
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700901 err = -ENODEV;
902 goto free_resources;
903 }
904
Thomas Petazzonic35064c2012-11-15 13:01:59 +0100905 dma_sync_single_for_cpu(dma_chan->device->dev, dest_dma,
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700906 PAGE_SIZE, DMA_FROM_DEVICE);
907 for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) {
908 u32 *ptr = page_address(dest);
909 if (ptr[i] != cmp_word) {
Thomas Petazzonia3fc74b2012-11-15 12:50:27 +0100910 dev_err(dma_chan->device->dev,
Joe Perches1ba151c2012-10-28 01:05:44 -0700911 "Self-test xor failed compare, disabling. index %d, data %x, expected %x\n",
912 i, ptr[i], cmp_word);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700913 err = -ENODEV;
914 goto free_resources;
915 }
916 }
917
918free_resources:
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300919 dmaengine_unmap_put(unmap);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700920 mv_xor_free_chan_resources(dma_chan);
921out:
Ezequiel Garciad16695a2013-12-10 09:32:36 -0300922 src_idx = src_count;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700923 while (src_idx--)
924 __free_page(xor_srcs[src_idx]);
925 __free_page(dest);
926 return err;
927}
928
Andrew Lunn34c93c82012-11-18 11:44:56 +0100929/* This driver does not implement any of the optional DMA operations. */
930static int
931mv_xor_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
932 unsigned long arg)
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700933{
Andrew Lunn34c93c82012-11-18 11:44:56 +0100934 return -ENOSYS;
935}
936
Thomas Petazzoni1ef48a22012-11-15 15:17:05 +0100937static int mv_xor_channel_remove(struct mv_xor_chan *mv_chan)
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700938{
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700939 struct dma_chan *chan, *_chan;
Thomas Petazzoni1ef48a22012-11-15 15:17:05 +0100940 struct device *dev = mv_chan->dmadev.dev;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700941
Thomas Petazzoni1ef48a22012-11-15 15:17:05 +0100942 dma_async_device_unregister(&mv_chan->dmadev);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700943
Thomas Petazzonib503fa02012-11-15 15:55:30 +0100944 dma_free_coherent(dev, MV_XOR_POOL_SIZE,
Thomas Petazzoni1ef48a22012-11-15 15:17:05 +0100945 mv_chan->dma_desc_pool_virt, mv_chan->dma_desc_pool);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700946
Thomas Petazzoni1ef48a22012-11-15 15:17:05 +0100947 list_for_each_entry_safe(chan, _chan, &mv_chan->dmadev.channels,
Thomas Petazzonia6b4a9d2012-10-29 16:45:46 +0100948 device_node) {
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700949 list_del(&chan->device_node);
950 }
951
Thomas Petazzoni88eb92c2012-11-15 16:11:18 +0100952 free_irq(mv_chan->irq, mv_chan);
953
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700954 return 0;
955}
956
Thomas Petazzoni1ef48a22012-11-15 15:17:05 +0100957static struct mv_xor_chan *
Thomas Petazzoni297eedb2012-11-15 15:29:53 +0100958mv_xor_channel_add(struct mv_xor_device *xordev,
Thomas Petazzonia6b4a9d2012-10-29 16:45:46 +0100959 struct platform_device *pdev,
Thomas Petazzonib503fa02012-11-15 15:55:30 +0100960 int idx, dma_cap_mask_t cap_mask, int irq)
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700961{
962 int ret = 0;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700963 struct mv_xor_chan *mv_chan;
964 struct dma_device *dma_dev;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700965
Thomas Petazzoni1ef48a22012-11-15 15:17:05 +0100966 mv_chan = devm_kzalloc(&pdev->dev, sizeof(*mv_chan), GFP_KERNEL);
Sachin Kamata5776592013-09-02 13:54:20 +0530967 if (!mv_chan)
968 return ERR_PTR(-ENOMEM);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700969
Thomas Petazzoni9aedbdb2012-11-15 15:36:37 +0100970 mv_chan->idx = idx;
Thomas Petazzoni88eb92c2012-11-15 16:11:18 +0100971 mv_chan->irq = irq;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700972
Thomas Petazzoni1ef48a22012-11-15 15:17:05 +0100973 dma_dev = &mv_chan->dmadev;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700974
975 /* allocate coherent memory for hardware descriptors
976 * note: writecombine gives slightly better performance, but
977 * requires that we explicitly flush the writes
978 */
Thomas Petazzoni1ef48a22012-11-15 15:17:05 +0100979 mv_chan->dma_desc_pool_virt =
Thomas Petazzonib503fa02012-11-15 15:55:30 +0100980 dma_alloc_writecombine(&pdev->dev, MV_XOR_POOL_SIZE,
Thomas Petazzoni1ef48a22012-11-15 15:17:05 +0100981 &mv_chan->dma_desc_pool, GFP_KERNEL);
982 if (!mv_chan->dma_desc_pool_virt)
Thomas Petazzonia6b4a9d2012-10-29 16:45:46 +0100983 return ERR_PTR(-ENOMEM);
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700984
985 /* discover transaction capabilites from the platform data */
Thomas Petazzonia6b4a9d2012-10-29 16:45:46 +0100986 dma_dev->cap_mask = cap_mask;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700987
988 INIT_LIST_HEAD(&dma_dev->channels);
989
990 /* set base routines */
991 dma_dev->device_alloc_chan_resources = mv_xor_alloc_chan_resources;
992 dma_dev->device_free_chan_resources = mv_xor_free_chan_resources;
Linus Walleij07934482010-03-26 16:50:49 -0700993 dma_dev->device_tx_status = mv_xor_status;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700994 dma_dev->device_issue_pending = mv_xor_issue_pending;
Andrew Lunn34c93c82012-11-18 11:44:56 +0100995 dma_dev->device_control = mv_xor_control;
Saeed Bisharaff7b0472008-07-08 11:58:36 -0700996 dma_dev->dev = &pdev->dev;
997
998 /* set prep routines based on capability */
999 if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask))
1000 dma_dev->device_prep_dma_memcpy = mv_xor_prep_dma_memcpy;
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001001 if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) {
Joe Perchesc0198942009-06-28 09:26:21 -07001002 dma_dev->max_xor = 8;
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001003 dma_dev->device_prep_dma_xor = mv_xor_prep_dma_xor;
1004 }
1005
Thomas Petazzoni297eedb2012-11-15 15:29:53 +01001006 mv_chan->mmr_base = xordev->xor_base;
Ezequiel Garcia82a14022013-10-30 12:01:43 -03001007 mv_chan->mmr_high_base = xordev->xor_high_base;
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001008 tasklet_init(&mv_chan->irq_tasklet, mv_xor_tasklet, (unsigned long)
1009 mv_chan);
1010
1011 /* clear errors before enabling interrupts */
1012 mv_xor_device_clear_err_status(mv_chan);
1013
Thomas Petazzoni2d0a0742012-11-22 18:19:09 +01001014 ret = request_irq(mv_chan->irq, mv_xor_interrupt_handler,
1015 0, dev_name(&pdev->dev), mv_chan);
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001016 if (ret)
1017 goto err_free_dma;
1018
1019 mv_chan_unmask_interrupts(mv_chan);
1020
Lior Amsalem3e4f52e2014-08-27 10:52:50 -03001021 mv_set_mode(mv_chan, DMA_XOR);
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001022
1023 spin_lock_init(&mv_chan->lock);
1024 INIT_LIST_HEAD(&mv_chan->chain);
1025 INIT_LIST_HEAD(&mv_chan->completed_slots);
1026 INIT_LIST_HEAD(&mv_chan->all_slots);
Thomas Petazzoni98817b92012-11-15 14:57:44 +01001027 mv_chan->dmachan.device = dma_dev;
1028 dma_cookie_init(&mv_chan->dmachan);
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001029
Thomas Petazzoni98817b92012-11-15 14:57:44 +01001030 list_add_tail(&mv_chan->dmachan.device_node, &dma_dev->channels);
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001031
1032 if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) {
Thomas Petazzoni275cc0c2012-11-15 15:09:42 +01001033 ret = mv_xor_memcpy_self_test(mv_chan);
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001034 dev_dbg(&pdev->dev, "memcpy self test returned %d\n", ret);
1035 if (ret)
Thomas Petazzoni2d0a0742012-11-22 18:19:09 +01001036 goto err_free_irq;
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001037 }
1038
1039 if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) {
Thomas Petazzoni275cc0c2012-11-15 15:09:42 +01001040 ret = mv_xor_xor_self_test(mv_chan);
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001041 dev_dbg(&pdev->dev, "xor self test returned %d\n", ret);
1042 if (ret)
Thomas Petazzoni2d0a0742012-11-22 18:19:09 +01001043 goto err_free_irq;
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001044 }
1045
Bartlomiej Zolnierkiewicz48a9db42013-07-03 15:05:06 -07001046 dev_info(&pdev->dev, "Marvell XOR: ( %s%s%s)\n",
Joe Perches1ba151c2012-10-28 01:05:44 -07001047 dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "",
Joe Perches1ba151c2012-10-28 01:05:44 -07001048 dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "",
1049 dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : "");
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001050
1051 dma_async_device_register(dma_dev);
Thomas Petazzoni1ef48a22012-11-15 15:17:05 +01001052 return mv_chan;
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001053
Thomas Petazzoni2d0a0742012-11-22 18:19:09 +01001054err_free_irq:
1055 free_irq(mv_chan->irq, mv_chan);
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001056 err_free_dma:
Thomas Petazzonib503fa02012-11-15 15:55:30 +01001057 dma_free_coherent(&pdev->dev, MV_XOR_POOL_SIZE,
Thomas Petazzoni1ef48a22012-11-15 15:17:05 +01001058 mv_chan->dma_desc_pool_virt, mv_chan->dma_desc_pool);
Thomas Petazzonia6b4a9d2012-10-29 16:45:46 +01001059 return ERR_PTR(ret);
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001060}
1061
1062static void
Thomas Petazzoni297eedb2012-11-15 15:29:53 +01001063mv_xor_conf_mbus_windows(struct mv_xor_device *xordev,
Andrew Lunn63a93322011-12-07 21:48:07 +01001064 const struct mbus_dram_target_info *dram)
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001065{
Ezequiel Garcia82a14022013-10-30 12:01:43 -03001066 void __iomem *base = xordev->xor_high_base;
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001067 u32 win_enable = 0;
1068 int i;
1069
1070 for (i = 0; i < 8; i++) {
1071 writel(0, base + WINDOW_BASE(i));
1072 writel(0, base + WINDOW_SIZE(i));
1073 if (i < 4)
1074 writel(0, base + WINDOW_REMAP_HIGH(i));
1075 }
1076
1077 for (i = 0; i < dram->num_cs; i++) {
Andrew Lunn63a93322011-12-07 21:48:07 +01001078 const struct mbus_dram_window *cs = dram->cs + i;
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001079
1080 writel((cs->base & 0xffff0000) |
1081 (cs->mbus_attr << 8) |
1082 dram->mbus_dram_target_id, base + WINDOW_BASE(i));
1083 writel((cs->size - 1) & 0xffff0000, base + WINDOW_SIZE(i));
1084
1085 win_enable |= (1 << i);
1086 win_enable |= 3 << (16 + (2 * i));
1087 }
1088
1089 writel(win_enable, base + WINDOW_BAR_ENABLE(0));
1090 writel(win_enable, base + WINDOW_BAR_ENABLE(1));
Thomas Petazzonic4b4b732012-11-22 18:16:37 +01001091 writel(0, base + WINDOW_OVERRIDE_CTRL(0));
1092 writel(0, base + WINDOW_OVERRIDE_CTRL(1));
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001093}
1094
Linus Torvaldsc2714332012-12-14 14:54:26 -08001095static int mv_xor_probe(struct platform_device *pdev)
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001096{
Andrew Lunn63a93322011-12-07 21:48:07 +01001097 const struct mbus_dram_target_info *dram;
Thomas Petazzoni297eedb2012-11-15 15:29:53 +01001098 struct mv_xor_device *xordev;
Jingoo Hand4adcc02013-07-30 17:09:11 +09001099 struct mv_xor_platform_data *pdata = dev_get_platdata(&pdev->dev);
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001100 struct resource *res;
Thomas Petazzoni60d151f2012-10-29 16:54:49 +01001101 int i, ret;
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001102
Joe Perches1ba151c2012-10-28 01:05:44 -07001103 dev_notice(&pdev->dev, "Marvell shared XOR driver\n");
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001104
Thomas Petazzoni297eedb2012-11-15 15:29:53 +01001105 xordev = devm_kzalloc(&pdev->dev, sizeof(*xordev), GFP_KERNEL);
1106 if (!xordev)
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001107 return -ENOMEM;
1108
1109 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1110 if (!res)
1111 return -ENODEV;
1112
Thomas Petazzoni297eedb2012-11-15 15:29:53 +01001113 xordev->xor_base = devm_ioremap(&pdev->dev, res->start,
1114 resource_size(res));
1115 if (!xordev->xor_base)
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001116 return -EBUSY;
1117
1118 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
1119 if (!res)
1120 return -ENODEV;
1121
Thomas Petazzoni297eedb2012-11-15 15:29:53 +01001122 xordev->xor_high_base = devm_ioremap(&pdev->dev, res->start,
1123 resource_size(res));
1124 if (!xordev->xor_high_base)
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001125 return -EBUSY;
1126
Thomas Petazzoni297eedb2012-11-15 15:29:53 +01001127 platform_set_drvdata(pdev, xordev);
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001128
1129 /*
1130 * (Re-)program MBUS remapping windows if we are asked to.
1131 */
Andrew Lunn63a93322011-12-07 21:48:07 +01001132 dram = mv_mbus_dram_info();
1133 if (dram)
Thomas Petazzoni297eedb2012-11-15 15:29:53 +01001134 mv_xor_conf_mbus_windows(xordev, dram);
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001135
Andrew Lunnc5101822012-02-19 13:30:26 +01001136 /* Not all platforms can gate the clock, so it is not
1137 * an error if the clock does not exists.
1138 */
Thomas Petazzoni297eedb2012-11-15 15:29:53 +01001139 xordev->clk = clk_get(&pdev->dev, NULL);
1140 if (!IS_ERR(xordev->clk))
1141 clk_prepare_enable(xordev->clk);
Andrew Lunnc5101822012-02-19 13:30:26 +01001142
Thomas Petazzonif7d12ef2012-11-15 16:47:58 +01001143 if (pdev->dev.of_node) {
1144 struct device_node *np;
1145 int i = 0;
1146
1147 for_each_child_of_node(pdev->dev.of_node, np) {
Russell King0be82532013-12-12 23:59:08 +00001148 struct mv_xor_chan *chan;
Thomas Petazzonif7d12ef2012-11-15 16:47:58 +01001149 dma_cap_mask_t cap_mask;
1150 int irq;
1151
1152 dma_cap_zero(cap_mask);
1153 if (of_property_read_bool(np, "dmacap,memcpy"))
1154 dma_cap_set(DMA_MEMCPY, cap_mask);
1155 if (of_property_read_bool(np, "dmacap,xor"))
1156 dma_cap_set(DMA_XOR, cap_mask);
Thomas Petazzonif7d12ef2012-11-15 16:47:58 +01001157 if (of_property_read_bool(np, "dmacap,interrupt"))
1158 dma_cap_set(DMA_INTERRUPT, cap_mask);
1159
1160 irq = irq_of_parse_and_map(np, 0);
Thomas Petazzonif8eb9e72012-11-22 18:22:12 +01001161 if (!irq) {
1162 ret = -ENODEV;
Thomas Petazzonif7d12ef2012-11-15 16:47:58 +01001163 goto err_channel_add;
1164 }
1165
Russell King0be82532013-12-12 23:59:08 +00001166 chan = mv_xor_channel_add(xordev, pdev, i,
1167 cap_mask, irq);
1168 if (IS_ERR(chan)) {
1169 ret = PTR_ERR(chan);
Thomas Petazzonif7d12ef2012-11-15 16:47:58 +01001170 irq_dispose_mapping(irq);
1171 goto err_channel_add;
1172 }
1173
Russell King0be82532013-12-12 23:59:08 +00001174 xordev->channels[i] = chan;
Thomas Petazzonif7d12ef2012-11-15 16:47:58 +01001175 i++;
1176 }
1177 } else if (pdata && pdata->channels) {
Thomas Petazzoni60d151f2012-10-29 16:54:49 +01001178 for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) {
Thomas Petazzonie39f6ec2012-10-30 11:56:26 +01001179 struct mv_xor_channel_data *cd;
Russell King0be82532013-12-12 23:59:08 +00001180 struct mv_xor_chan *chan;
Thomas Petazzoni60d151f2012-10-29 16:54:49 +01001181 int irq;
1182
1183 cd = &pdata->channels[i];
1184 if (!cd) {
1185 ret = -ENODEV;
1186 goto err_channel_add;
1187 }
1188
1189 irq = platform_get_irq(pdev, i);
1190 if (irq < 0) {
1191 ret = irq;
1192 goto err_channel_add;
1193 }
1194
Russell King0be82532013-12-12 23:59:08 +00001195 chan = mv_xor_channel_add(xordev, pdev, i,
1196 cd->cap_mask, irq);
1197 if (IS_ERR(chan)) {
1198 ret = PTR_ERR(chan);
Thomas Petazzoni60d151f2012-10-29 16:54:49 +01001199 goto err_channel_add;
1200 }
Russell King0be82532013-12-12 23:59:08 +00001201
1202 xordev->channels[i] = chan;
Thomas Petazzoni60d151f2012-10-29 16:54:49 +01001203 }
1204 }
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001205
1206 return 0;
Thomas Petazzoni60d151f2012-10-29 16:54:49 +01001207
1208err_channel_add:
1209 for (i = 0; i < MV_XOR_MAX_CHANNELS; i++)
Thomas Petazzonif7d12ef2012-11-15 16:47:58 +01001210 if (xordev->channels[i]) {
Thomas Petazzoniab6e4392013-01-06 11:10:43 +01001211 mv_xor_channel_remove(xordev->channels[i]);
Thomas Petazzonif7d12ef2012-11-15 16:47:58 +01001212 if (pdev->dev.of_node)
1213 irq_dispose_mapping(xordev->channels[i]->irq);
Thomas Petazzonif7d12ef2012-11-15 16:47:58 +01001214 }
Thomas Petazzoni60d151f2012-10-29 16:54:49 +01001215
Thomas Petazzonidab92062013-01-06 11:10:44 +01001216 if (!IS_ERR(xordev->clk)) {
1217 clk_disable_unprepare(xordev->clk);
1218 clk_put(xordev->clk);
1219 }
1220
Thomas Petazzoni60d151f2012-10-29 16:54:49 +01001221 return ret;
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001222}
1223
Linus Torvaldsc2714332012-12-14 14:54:26 -08001224static int mv_xor_remove(struct platform_device *pdev)
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001225{
Thomas Petazzoni297eedb2012-11-15 15:29:53 +01001226 struct mv_xor_device *xordev = platform_get_drvdata(pdev);
Thomas Petazzoni60d151f2012-10-29 16:54:49 +01001227 int i;
Andrew Lunnc5101822012-02-19 13:30:26 +01001228
Thomas Petazzoni60d151f2012-10-29 16:54:49 +01001229 for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) {
Thomas Petazzoni297eedb2012-11-15 15:29:53 +01001230 if (xordev->channels[i])
1231 mv_xor_channel_remove(xordev->channels[i]);
Thomas Petazzoni60d151f2012-10-29 16:54:49 +01001232 }
Andrew Lunnc5101822012-02-19 13:30:26 +01001233
Thomas Petazzoni297eedb2012-11-15 15:29:53 +01001234 if (!IS_ERR(xordev->clk)) {
1235 clk_disable_unprepare(xordev->clk);
1236 clk_put(xordev->clk);
Andrew Lunnc5101822012-02-19 13:30:26 +01001237 }
1238
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001239 return 0;
1240}
1241
Thomas Petazzonif7d12ef2012-11-15 16:47:58 +01001242#ifdef CONFIG_OF
Linus Torvaldsc2714332012-12-14 14:54:26 -08001243static struct of_device_id mv_xor_dt_ids[] = {
Thomas Petazzonif7d12ef2012-11-15 16:47:58 +01001244 { .compatible = "marvell,orion-xor", },
1245 {},
1246};
1247MODULE_DEVICE_TABLE(of, mv_xor_dt_ids);
1248#endif
1249
Thomas Petazzoni61971652012-10-30 12:05:40 +01001250static struct platform_driver mv_xor_driver = {
1251 .probe = mv_xor_probe,
Linus Torvaldsc2714332012-12-14 14:54:26 -08001252 .remove = mv_xor_remove,
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001253 .driver = {
Thomas Petazzonif7d12ef2012-11-15 16:47:58 +01001254 .owner = THIS_MODULE,
1255 .name = MV_XOR_NAME,
1256 .of_match_table = of_match_ptr(mv_xor_dt_ids),
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001257 },
1258};
1259
1260
1261static int __init mv_xor_init(void)
1262{
Thomas Petazzoni61971652012-10-30 12:05:40 +01001263 return platform_driver_register(&mv_xor_driver);
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001264}
1265module_init(mv_xor_init);
1266
1267/* it's currently unsafe to unload this module */
1268#if 0
1269static void __exit mv_xor_exit(void)
1270{
1271 platform_driver_unregister(&mv_xor_driver);
Saeed Bisharaff7b0472008-07-08 11:58:36 -07001272 return;
1273}
1274
1275module_exit(mv_xor_exit);
1276#endif
1277
1278MODULE_AUTHOR("Saeed Bishara <saeed@marvell.com>");
1279MODULE_DESCRIPTION("DMA engine driver for Marvell's XOR engine");
1280MODULE_LICENSE("GPL");