Blame view

drivers/vme/vme.c 37.5 KB
a17a75e26   Martyn Welch   Staging: VME Fram...
1
2
3
  /*
   * VME Bridge Framework
   *
66bd8db52   Martyn Welch   Staging: vme: Ren...
4
5
   * Author: Martyn Welch <martyn.welch@ge.com>
   * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc.
a17a75e26   Martyn Welch   Staging: VME Fram...
6
7
8
9
10
11
12
13
14
   *
   * Based on work by Tom Armistead and Ajit Prem
   * Copyright 2004 Motorola Inc.
   *
   * This program is free software; you can redistribute  it and/or modify it
   * under  the terms of  the GNU General  Public License as published by the
   * Free Software Foundation;  either version 2 of the  License, or (at your
   * option) any later version.
   */
a17a75e26   Martyn Welch   Staging: VME Fram...
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/mm.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/errno.h>
  #include <linux/pci.h>
  #include <linux/poll.h>
  #include <linux/highmem.h>
  #include <linux/interrupt.h>
  #include <linux/pagemap.h>
  #include <linux/device.h>
  #include <linux/dma-mapping.h>
  #include <linux/syscalls.h>
400822fec   Martyn Welch   Staging: Use prop...
29
  #include <linux/mutex.h>
a17a75e26   Martyn Welch   Staging: VME Fram...
30
  #include <linux/spinlock.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
31
  #include <linux/slab.h>
db3b9e990   Greg Kroah-Hartman   Staging: VME: mov...
32
  #include <linux/vme.h>
a17a75e26   Martyn Welch   Staging: VME Fram...
33

a17a75e26   Martyn Welch   Staging: VME Fram...
34
  #include "vme_bridge.h"
733e3ef0d   Manohar Vanga   staging: vme: kee...
35
  /* Bitmask and list of registered buses both protected by common mutex */
a17a75e26   Martyn Welch   Staging: VME Fram...
36
  static unsigned int vme_bus_numbers;
733e3ef0d   Manohar Vanga   staging: vme: kee...
37
38
  static LIST_HEAD(vme_bus_list);
  static DEFINE_MUTEX(vme_buses_lock);
a17a75e26   Martyn Welch   Staging: VME Fram...
39

ead1f3e30   Martyn Welch   Staging: vme: Fix...
40
41
  static void __exit vme_exit(void);
  static int __init vme_init(void);
a17a75e26   Martyn Welch   Staging: VME Fram...
42

8f966dc44   Manohar Vanga   staging: vme: add...
43
  static struct vme_dev *dev_to_vme_dev(struct device *dev)
a17a75e26   Martyn Welch   Staging: VME Fram...
44
  {
8f966dc44   Manohar Vanga   staging: vme: add...
45
  	return container_of(dev, struct vme_dev, dev);
a17a75e26   Martyn Welch   Staging: VME Fram...
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  }
  
  /*
   * Find the bridge that the resource is associated with.
   */
  static struct vme_bridge *find_bridge(struct vme_resource *resource)
  {
  	/* Get list to search */
  	switch (resource->type) {
  	case VME_MASTER:
  		return list_entry(resource->entry, struct vme_master_resource,
  			list)->parent;
  		break;
  	case VME_SLAVE:
  		return list_entry(resource->entry, struct vme_slave_resource,
  			list)->parent;
  		break;
  	case VME_DMA:
  		return list_entry(resource->entry, struct vme_dma_resource,
  			list)->parent;
  		break;
42fb50312   Martyn Welch   Staging: vme: add...
67
68
69
70
  	case VME_LM:
  		return list_entry(resource->entry, struct vme_lm_resource,
  			list)->parent;
  		break;
a17a75e26   Martyn Welch   Staging: VME Fram...
71
72
73
74
75
76
77
78
79
80
81
  	default:
  		printk(KERN_ERR "Unknown resource type
  ");
  		return NULL;
  		break;
  	}
  }
  
  /*
   * Allocate a contiguous block of memory for use by the driver. This is used to
   * create the buffers for the slave windows.
a17a75e26   Martyn Welch   Staging: VME Fram...
82
   */
ead1f3e30   Martyn Welch   Staging: vme: Fix...
83
  void *vme_alloc_consistent(struct vme_resource *resource, size_t size,
a17a75e26   Martyn Welch   Staging: VME Fram...
84
85
86
  	dma_addr_t *dma)
  {
  	struct vme_bridge *bridge;
a17a75e26   Martyn Welch   Staging: VME Fram...
87

ead1f3e30   Martyn Welch   Staging: vme: Fix...
88
89
90
  	if (resource == NULL) {
  		printk(KERN_ERR "No resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
91
92
93
94
  		return NULL;
  	}
  
  	bridge = find_bridge(resource);
ead1f3e30   Martyn Welch   Staging: vme: Fix...
95
96
97
  	if (bridge == NULL) {
  		printk(KERN_ERR "Can't find bridge
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
98
99
  		return NULL;
  	}
a17a75e26   Martyn Welch   Staging: VME Fram...
100
  	if (bridge->parent == NULL) {
25958ce32   Greg Kroah-Hartman   staging: vme: vme...
101
102
  		printk(KERN_ERR "Dev entry NULL for bridge %s
  ", bridge->name);
7f58f0255   Manohar Vanga   staging: vme: mak...
103
104
105
106
  		return NULL;
  	}
  
  	if (bridge->alloc_consistent == NULL) {
25958ce32   Greg Kroah-Hartman   staging: vme: vme...
107
108
109
  		printk(KERN_ERR "alloc_consistent not supported by bridge %s
  ",
  		       bridge->name);
a17a75e26   Martyn Welch   Staging: VME Fram...
110
111
  		return NULL;
  	}
a17a75e26   Martyn Welch   Staging: VME Fram...
112

7f58f0255   Manohar Vanga   staging: vme: mak...
113
  	return bridge->alloc_consistent(bridge->parent, size, dma);
a17a75e26   Martyn Welch   Staging: VME Fram...
114
115
116
117
118
  }
  EXPORT_SYMBOL(vme_alloc_consistent);
  
  /*
   * Free previously allocated contiguous block of memory.
a17a75e26   Martyn Welch   Staging: VME Fram...
119
120
121
122
123
   */
  void vme_free_consistent(struct vme_resource *resource, size_t size,
  	void *vaddr, dma_addr_t dma)
  {
  	struct vme_bridge *bridge;
a17a75e26   Martyn Welch   Staging: VME Fram...
124

ead1f3e30   Martyn Welch   Staging: vme: Fix...
125
126
127
  	if (resource == NULL) {
  		printk(KERN_ERR "No resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
128
129
130
131
  		return;
  	}
  
  	bridge = find_bridge(resource);
ead1f3e30   Martyn Welch   Staging: vme: Fix...
132
133
134
  	if (bridge == NULL) {
  		printk(KERN_ERR "Can't find bridge
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
135
136
  		return;
  	}
7f58f0255   Manohar Vanga   staging: vme: mak...
137
  	if (bridge->parent == NULL) {
25958ce32   Greg Kroah-Hartman   staging: vme: vme...
138
139
  		printk(KERN_ERR "Dev entry NULL for bridge %s
  ", bridge->name);
7f58f0255   Manohar Vanga   staging: vme: mak...
140
141
142
143
  		return;
  	}
  
  	if (bridge->free_consistent == NULL) {
25958ce32   Greg Kroah-Hartman   staging: vme: vme...
144
145
146
  		printk(KERN_ERR "free_consistent not supported by bridge %s
  ",
  		       bridge->name);
7f58f0255   Manohar Vanga   staging: vme: mak...
147
148
  		return;
  	}
a17a75e26   Martyn Welch   Staging: VME Fram...
149

7f58f0255   Manohar Vanga   staging: vme: mak...
150
  	bridge->free_consistent(bridge->parent, size, vaddr, dma);
a17a75e26   Martyn Welch   Staging: VME Fram...
151
152
153
154
155
156
157
158
  }
  EXPORT_SYMBOL(vme_free_consistent);
  
  size_t vme_get_size(struct vme_resource *resource)
  {
  	int enabled, retval;
  	unsigned long long base, size;
  	dma_addr_t buf_base;
6af04b065   Martyn Welch   Staging: VME: Rem...
159
  	u32 aspace, cycle, dwidth;
a17a75e26   Martyn Welch   Staging: VME Fram...
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
  
  	switch (resource->type) {
  	case VME_MASTER:
  		retval = vme_master_get(resource, &enabled, &base, &size,
  			&aspace, &cycle, &dwidth);
  
  		return size;
  		break;
  	case VME_SLAVE:
  		retval = vme_slave_get(resource, &enabled, &base, &size,
  			&buf_base, &aspace, &cycle);
  
  		return size;
  		break;
  	case VME_DMA:
  		return 0;
  		break;
  	default:
  		printk(KERN_ERR "Unknown resource type
  ");
  		return 0;
  		break;
  	}
  }
  EXPORT_SYMBOL(vme_get_size);
ef73f886b   Dmitry Kalinkin   vme: export vme_c...
185
186
  int vme_check_window(u32 aspace, unsigned long long vme_base,
  		     unsigned long long size)
a17a75e26   Martyn Welch   Staging: VME Fram...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
  {
  	int retval = 0;
  
  	switch (aspace) {
  	case VME_A16:
  		if (((vme_base + size) > VME_A16_MAX) ||
  				(vme_base > VME_A16_MAX))
  			retval = -EFAULT;
  		break;
  	case VME_A24:
  		if (((vme_base + size) > VME_A24_MAX) ||
  				(vme_base > VME_A24_MAX))
  			retval = -EFAULT;
  		break;
  	case VME_A32:
  		if (((vme_base + size) > VME_A32_MAX) ||
  				(vme_base > VME_A32_MAX))
  			retval = -EFAULT;
  		break;
  	case VME_A64:
e7fd80cbb   Dmitry Kalinkin   vme: check for A6...
207
208
  		if ((size != 0) && (vme_base > U64_MAX + 1 - size))
  			retval = -EFAULT;
a17a75e26   Martyn Welch   Staging: VME Fram...
209
210
211
212
213
214
215
216
217
218
219
220
221
  		break;
  	case VME_CRCSR:
  		if (((vme_base + size) > VME_CRCSR_MAX) ||
  				(vme_base > VME_CRCSR_MAX))
  			retval = -EFAULT;
  		break;
  	case VME_USER1:
  	case VME_USER2:
  	case VME_USER3:
  	case VME_USER4:
  		/* User Defined */
  		break;
  	default:
ead1f3e30   Martyn Welch   Staging: vme: Fix...
222
223
  		printk(KERN_ERR "Invalid address space
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
224
225
226
227
228
229
  		retval = -EINVAL;
  		break;
  	}
  
  	return retval;
  }
ef73f886b   Dmitry Kalinkin   vme: export vme_c...
230
  EXPORT_SYMBOL(vme_check_window);
a17a75e26   Martyn Welch   Staging: VME Fram...
231

472f16f33   Dmitry Kalinkin   vme: include addr...
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
  static u32 vme_get_aspace(int am)
  {
  	switch (am) {
  	case 0x29:
  	case 0x2D:
  		return VME_A16;
  	case 0x38:
  	case 0x39:
  	case 0x3A:
  	case 0x3B:
  	case 0x3C:
  	case 0x3D:
  	case 0x3E:
  	case 0x3F:
  		return VME_A24;
  	case 0x8:
  	case 0x9:
  	case 0xA:
  	case 0xB:
  	case 0xC:
  	case 0xD:
  	case 0xE:
  	case 0xF:
  		return VME_A32;
  	case 0x0:
  	case 0x1:
  	case 0x3:
  		return VME_A64;
  	}
  
  	return 0;
  }
a17a75e26   Martyn Welch   Staging: VME Fram...
264
265
266
267
  /*
   * Request a slave image with specific attributes, return some unique
   * identifier.
   */
6af04b065   Martyn Welch   Staging: VME: Rem...
268
269
  struct vme_resource *vme_slave_request(struct vme_dev *vdev, u32 address,
  	u32 cycle)
a17a75e26   Martyn Welch   Staging: VME Fram...
270
271
272
273
274
275
  {
  	struct vme_bridge *bridge;
  	struct list_head *slave_pos = NULL;
  	struct vme_slave_resource *allocated_image = NULL;
  	struct vme_slave_resource *slave_image = NULL;
  	struct vme_resource *resource = NULL;
8f966dc44   Manohar Vanga   staging: vme: add...
276
  	bridge = vdev->bridge;
a17a75e26   Martyn Welch   Staging: VME Fram...
277
278
279
280
281
282
283
  	if (bridge == NULL) {
  		printk(KERN_ERR "Can't find VME bus
  ");
  		goto err_bus;
  	}
  
  	/* Loop through slave resources */
886953e9b   Emilio G. Cota   staging: vme: sty...
284
  	list_for_each(slave_pos, &bridge->slave_resources) {
a17a75e26   Martyn Welch   Staging: VME Fram...
285
286
287
288
  		slave_image = list_entry(slave_pos,
  			struct vme_slave_resource, list);
  
  		if (slave_image == NULL) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
289
290
  			printk(KERN_ERR "Registered NULL Slave resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
291
292
293
294
  			continue;
  		}
  
  		/* Find an unlocked and compatible image */
886953e9b   Emilio G. Cota   staging: vme: sty...
295
  		mutex_lock(&slave_image->mtx);
ead1f3e30   Martyn Welch   Staging: vme: Fix...
296
  		if (((slave_image->address_attr & address) == address) &&
a17a75e26   Martyn Welch   Staging: VME Fram...
297
298
299
300
  			((slave_image->cycle_attr & cycle) == cycle) &&
  			(slave_image->locked == 0)) {
  
  			slave_image->locked = 1;
886953e9b   Emilio G. Cota   staging: vme: sty...
301
  			mutex_unlock(&slave_image->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
302
303
304
  			allocated_image = slave_image;
  			break;
  		}
886953e9b   Emilio G. Cota   staging: vme: sty...
305
  		mutex_unlock(&slave_image->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
306
307
308
309
310
311
312
313
314
315
316
317
318
  	}
  
  	/* No free image */
  	if (allocated_image == NULL)
  		goto err_image;
  
  	resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL);
  	if (resource == NULL) {
  		printk(KERN_WARNING "Unable to allocate resource structure
  ");
  		goto err_alloc;
  	}
  	resource->type = VME_SLAVE;
886953e9b   Emilio G. Cota   staging: vme: sty...
319
  	resource->entry = &allocated_image->list;
a17a75e26   Martyn Welch   Staging: VME Fram...
320
321
322
323
324
  
  	return resource;
  
  err_alloc:
  	/* Unlock image */
886953e9b   Emilio G. Cota   staging: vme: sty...
325
  	mutex_lock(&slave_image->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
326
  	slave_image->locked = 0;
886953e9b   Emilio G. Cota   staging: vme: sty...
327
  	mutex_unlock(&slave_image->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
328
329
330
331
332
  err_image:
  err_bus:
  	return NULL;
  }
  EXPORT_SYMBOL(vme_slave_request);
ead1f3e30   Martyn Welch   Staging: vme: Fix...
333
  int vme_slave_set(struct vme_resource *resource, int enabled,
a17a75e26   Martyn Welch   Staging: VME Fram...
334
  	unsigned long long vme_base, unsigned long long size,
6af04b065   Martyn Welch   Staging: VME: Rem...
335
  	dma_addr_t buf_base, u32 aspace, u32 cycle)
a17a75e26   Martyn Welch   Staging: VME Fram...
336
337
338
339
340
341
  {
  	struct vme_bridge *bridge = find_bridge(resource);
  	struct vme_slave_resource *image;
  	int retval;
  
  	if (resource->type != VME_SLAVE) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
342
343
  		printk(KERN_ERR "Not a slave resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
344
345
346
347
348
349
  		return -EINVAL;
  	}
  
  	image = list_entry(resource->entry, struct vme_slave_resource, list);
  
  	if (bridge->slave_set == NULL) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
350
351
  		printk(KERN_ERR "Function not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
352
353
  		return -ENOSYS;
  	}
ead1f3e30   Martyn Welch   Staging: vme: Fix...
354
  	if (!(((image->address_attr & aspace) == aspace) &&
a17a75e26   Martyn Welch   Staging: VME Fram...
355
  		((image->cycle_attr & cycle) == cycle))) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
356
357
  		printk(KERN_ERR "Invalid attributes
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
358
359
360
361
  		return -EINVAL;
  	}
  
  	retval = vme_check_window(aspace, vme_base, size);
ead1f3e30   Martyn Welch   Staging: vme: Fix...
362
  	if (retval)
a17a75e26   Martyn Welch   Staging: VME Fram...
363
364
365
366
367
368
  		return retval;
  
  	return bridge->slave_set(image, enabled, vme_base, size, buf_base,
  		aspace, cycle);
  }
  EXPORT_SYMBOL(vme_slave_set);
ead1f3e30   Martyn Welch   Staging: vme: Fix...
369
  int vme_slave_get(struct vme_resource *resource, int *enabled,
a17a75e26   Martyn Welch   Staging: VME Fram...
370
  	unsigned long long *vme_base, unsigned long long *size,
6af04b065   Martyn Welch   Staging: VME: Rem...
371
  	dma_addr_t *buf_base, u32 *aspace, u32 *cycle)
a17a75e26   Martyn Welch   Staging: VME Fram...
372
373
374
375
376
  {
  	struct vme_bridge *bridge = find_bridge(resource);
  	struct vme_slave_resource *image;
  
  	if (resource->type != VME_SLAVE) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
377
378
  		printk(KERN_ERR "Not a slave resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
379
380
381
382
  		return -EINVAL;
  	}
  
  	image = list_entry(resource->entry, struct vme_slave_resource, list);
51a569f75   Emilio G. Cota   Staging: vme: fix...
383
  	if (bridge->slave_get == NULL) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
384
385
  		printk(KERN_ERR "vme_slave_get not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
386
387
388
389
390
391
392
393
394
395
396
397
398
  		return -EINVAL;
  	}
  
  	return bridge->slave_get(image, enabled, vme_base, size, buf_base,
  		aspace, cycle);
  }
  EXPORT_SYMBOL(vme_slave_get);
  
  void vme_slave_free(struct vme_resource *resource)
  {
  	struct vme_slave_resource *slave_image;
  
  	if (resource->type != VME_SLAVE) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
399
400
  		printk(KERN_ERR "Not a slave resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
401
402
403
404
405
406
  		return;
  	}
  
  	slave_image = list_entry(resource->entry, struct vme_slave_resource,
  		list);
  	if (slave_image == NULL) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
407
408
  		printk(KERN_ERR "Can't find slave resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
409
410
411
412
  		return;
  	}
  
  	/* Unlock image */
886953e9b   Emilio G. Cota   staging: vme: sty...
413
  	mutex_lock(&slave_image->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
414
415
416
417
418
  	if (slave_image->locked == 0)
  		printk(KERN_ERR "Image is already free
  ");
  
  	slave_image->locked = 0;
886953e9b   Emilio G. Cota   staging: vme: sty...
419
  	mutex_unlock(&slave_image->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
420
421
422
423
424
425
426
427
428
429
  
  	/* Free up resource memory */
  	kfree(resource);
  }
  EXPORT_SYMBOL(vme_slave_free);
  
  /*
   * Request a master image with specific attributes, return some unique
   * identifier.
   */
6af04b065   Martyn Welch   Staging: VME: Rem...
430
431
  struct vme_resource *vme_master_request(struct vme_dev *vdev, u32 address,
  	u32 cycle, u32 dwidth)
a17a75e26   Martyn Welch   Staging: VME Fram...
432
433
434
435
436
437
  {
  	struct vme_bridge *bridge;
  	struct list_head *master_pos = NULL;
  	struct vme_master_resource *allocated_image = NULL;
  	struct vme_master_resource *master_image = NULL;
  	struct vme_resource *resource = NULL;
8f966dc44   Manohar Vanga   staging: vme: add...
438
  	bridge = vdev->bridge;
a17a75e26   Martyn Welch   Staging: VME Fram...
439
440
441
442
443
444
445
  	if (bridge == NULL) {
  		printk(KERN_ERR "Can't find VME bus
  ");
  		goto err_bus;
  	}
  
  	/* Loop through master resources */
886953e9b   Emilio G. Cota   staging: vme: sty...
446
  	list_for_each(master_pos, &bridge->master_resources) {
a17a75e26   Martyn Welch   Staging: VME Fram...
447
448
449
450
451
452
453
454
455
456
  		master_image = list_entry(master_pos,
  			struct vme_master_resource, list);
  
  		if (master_image == NULL) {
  			printk(KERN_WARNING "Registered NULL master resource
  ");
  			continue;
  		}
  
  		/* Find an unlocked and compatible image */
886953e9b   Emilio G. Cota   staging: vme: sty...
457
  		spin_lock(&master_image->lock);
ead1f3e30   Martyn Welch   Staging: vme: Fix...
458
  		if (((master_image->address_attr & address) == address) &&
a17a75e26   Martyn Welch   Staging: VME Fram...
459
460
461
462
463
  			((master_image->cycle_attr & cycle) == cycle) &&
  			((master_image->width_attr & dwidth) == dwidth) &&
  			(master_image->locked == 0)) {
  
  			master_image->locked = 1;
886953e9b   Emilio G. Cota   staging: vme: sty...
464
  			spin_unlock(&master_image->lock);
a17a75e26   Martyn Welch   Staging: VME Fram...
465
466
467
  			allocated_image = master_image;
  			break;
  		}
886953e9b   Emilio G. Cota   staging: vme: sty...
468
  		spin_unlock(&master_image->lock);
a17a75e26   Martyn Welch   Staging: VME Fram...
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
  	}
  
  	/* Check to see if we found a resource */
  	if (allocated_image == NULL) {
  		printk(KERN_ERR "Can't find a suitable resource
  ");
  		goto err_image;
  	}
  
  	resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL);
  	if (resource == NULL) {
  		printk(KERN_ERR "Unable to allocate resource structure
  ");
  		goto err_alloc;
  	}
  	resource->type = VME_MASTER;
886953e9b   Emilio G. Cota   staging: vme: sty...
485
  	resource->entry = &allocated_image->list;
a17a75e26   Martyn Welch   Staging: VME Fram...
486
487
  
  	return resource;
a17a75e26   Martyn Welch   Staging: VME Fram...
488
489
  err_alloc:
  	/* Unlock image */
886953e9b   Emilio G. Cota   staging: vme: sty...
490
  	spin_lock(&master_image->lock);
a17a75e26   Martyn Welch   Staging: VME Fram...
491
  	master_image->locked = 0;
886953e9b   Emilio G. Cota   staging: vme: sty...
492
  	spin_unlock(&master_image->lock);
a17a75e26   Martyn Welch   Staging: VME Fram...
493
494
495
496
497
  err_image:
  err_bus:
  	return NULL;
  }
  EXPORT_SYMBOL(vme_master_request);
ead1f3e30   Martyn Welch   Staging: vme: Fix...
498
  int vme_master_set(struct vme_resource *resource, int enabled,
6af04b065   Martyn Welch   Staging: VME: Rem...
499
500
  	unsigned long long vme_base, unsigned long long size, u32 aspace,
  	u32 cycle, u32 dwidth)
a17a75e26   Martyn Welch   Staging: VME Fram...
501
502
503
504
505
506
  {
  	struct vme_bridge *bridge = find_bridge(resource);
  	struct vme_master_resource *image;
  	int retval;
  
  	if (resource->type != VME_MASTER) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
507
508
  		printk(KERN_ERR "Not a master resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
509
510
511
512
513
514
  		return -EINVAL;
  	}
  
  	image = list_entry(resource->entry, struct vme_master_resource, list);
  
  	if (bridge->master_set == NULL) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
515
516
  		printk(KERN_WARNING "vme_master_set not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
517
518
  		return -EINVAL;
  	}
ead1f3e30   Martyn Welch   Staging: vme: Fix...
519
  	if (!(((image->address_attr & aspace) == aspace) &&
a17a75e26   Martyn Welch   Staging: VME Fram...
520
521
  		((image->cycle_attr & cycle) == cycle) &&
  		((image->width_attr & dwidth) == dwidth))) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
522
523
  		printk(KERN_WARNING "Invalid attributes
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
524
525
526
527
  		return -EINVAL;
  	}
  
  	retval = vme_check_window(aspace, vme_base, size);
ead1f3e30   Martyn Welch   Staging: vme: Fix...
528
  	if (retval)
a17a75e26   Martyn Welch   Staging: VME Fram...
529
530
531
532
533
534
  		return retval;
  
  	return bridge->master_set(image, enabled, vme_base, size, aspace,
  		cycle, dwidth);
  }
  EXPORT_SYMBOL(vme_master_set);
ead1f3e30   Martyn Welch   Staging: vme: Fix...
535
  int vme_master_get(struct vme_resource *resource, int *enabled,
6af04b065   Martyn Welch   Staging: VME: Rem...
536
537
  	unsigned long long *vme_base, unsigned long long *size, u32 *aspace,
  	u32 *cycle, u32 *dwidth)
a17a75e26   Martyn Welch   Staging: VME Fram...
538
539
540
541
542
  {
  	struct vme_bridge *bridge = find_bridge(resource);
  	struct vme_master_resource *image;
  
  	if (resource->type != VME_MASTER) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
543
544
  		printk(KERN_ERR "Not a master resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
545
546
547
548
  		return -EINVAL;
  	}
  
  	image = list_entry(resource->entry, struct vme_master_resource, list);
51a569f75   Emilio G. Cota   Staging: vme: fix...
549
  	if (bridge->master_get == NULL) {
c1038307c   Julia Lawall   vme: fix misspell...
550
551
  		printk(KERN_WARNING "%s not supported
  ", __func__);
a17a75e26   Martyn Welch   Staging: VME Fram...
552
553
554
555
556
557
558
559
560
561
562
  		return -EINVAL;
  	}
  
  	return bridge->master_get(image, enabled, vme_base, size, aspace,
  		cycle, dwidth);
  }
  EXPORT_SYMBOL(vme_master_get);
  
  /*
   * Read data out of VME space into a buffer.
   */
ead1f3e30   Martyn Welch   Staging: vme: Fix...
563
  ssize_t vme_master_read(struct vme_resource *resource, void *buf, size_t count,
a17a75e26   Martyn Welch   Staging: VME Fram...
564
565
566
567
568
569
570
  	loff_t offset)
  {
  	struct vme_bridge *bridge = find_bridge(resource);
  	struct vme_master_resource *image;
  	size_t length;
  
  	if (bridge->master_read == NULL) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
571
572
  		printk(KERN_WARNING "Reading from resource not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
573
574
575
576
  		return -EINVAL;
  	}
  
  	if (resource->type != VME_MASTER) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
577
578
  		printk(KERN_ERR "Not a master resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
579
580
581
582
583
584
585
586
  		return -EINVAL;
  	}
  
  	image = list_entry(resource->entry, struct vme_master_resource, list);
  
  	length = vme_get_size(resource);
  
  	if (offset > length) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
587
588
  		printk(KERN_WARNING "Invalid Offset
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
589
590
591
592
593
594
595
596
597
598
599
600
601
602
  		return -EFAULT;
  	}
  
  	if ((offset + count) > length)
  		count = length - offset;
  
  	return bridge->master_read(image, buf, count, offset);
  
  }
  EXPORT_SYMBOL(vme_master_read);
  
  /*
   * Write data out to VME space from a buffer.
   */
ead1f3e30   Martyn Welch   Staging: vme: Fix...
603
  ssize_t vme_master_write(struct vme_resource *resource, void *buf,
a17a75e26   Martyn Welch   Staging: VME Fram...
604
605
606
607
608
609
610
  	size_t count, loff_t offset)
  {
  	struct vme_bridge *bridge = find_bridge(resource);
  	struct vme_master_resource *image;
  	size_t length;
  
  	if (bridge->master_write == NULL) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
611
612
  		printk(KERN_WARNING "Writing to resource not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
613
614
615
616
  		return -EINVAL;
  	}
  
  	if (resource->type != VME_MASTER) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
617
618
  		printk(KERN_ERR "Not a master resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
619
620
621
622
623
624
625
626
  		return -EINVAL;
  	}
  
  	image = list_entry(resource->entry, struct vme_master_resource, list);
  
  	length = vme_get_size(resource);
  
  	if (offset > length) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
627
628
  		printk(KERN_WARNING "Invalid Offset
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
629
630
631
632
633
634
635
636
637
638
639
640
641
  		return -EFAULT;
  	}
  
  	if ((offset + count) > length)
  		count = length - offset;
  
  	return bridge->master_write(image, buf, count, offset);
  }
  EXPORT_SYMBOL(vme_master_write);
  
  /*
   * Perform RMW cycle to provided location.
   */
ead1f3e30   Martyn Welch   Staging: vme: Fix...
642
  unsigned int vme_master_rmw(struct vme_resource *resource, unsigned int mask,
a17a75e26   Martyn Welch   Staging: VME Fram...
643
644
645
646
647
648
  	unsigned int compare, unsigned int swap, loff_t offset)
  {
  	struct vme_bridge *bridge = find_bridge(resource);
  	struct vme_master_resource *image;
  
  	if (bridge->master_rmw == NULL) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
649
650
  		printk(KERN_WARNING "Writing to resource not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
651
652
653
654
  		return -EINVAL;
  	}
  
  	if (resource->type != VME_MASTER) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
655
656
  		printk(KERN_ERR "Not a master resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
657
658
659
660
661
662
663
664
  		return -EINVAL;
  	}
  
  	image = list_entry(resource->entry, struct vme_master_resource, list);
  
  	return bridge->master_rmw(image, mask, compare, swap, offset);
  }
  EXPORT_SYMBOL(vme_master_rmw);
c74a804f1   Dmitry Kalinkin   staging: vme: mma...
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
  int vme_master_mmap(struct vme_resource *resource, struct vm_area_struct *vma)
  {
  	struct vme_master_resource *image;
  	phys_addr_t phys_addr;
  	unsigned long vma_size;
  
  	if (resource->type != VME_MASTER) {
  		pr_err("Not a master resource
  ");
  		return -EINVAL;
  	}
  
  	image = list_entry(resource->entry, struct vme_master_resource, list);
  	phys_addr = image->bus_resource.start + (vma->vm_pgoff << PAGE_SHIFT);
  	vma_size = vma->vm_end - vma->vm_start;
  
  	if (phys_addr + vma_size > image->bus_resource.end + 1) {
  		pr_err("Map size cannot exceed the window size
  ");
  		return -EFAULT;
  	}
  
  	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
  
  	return vm_iomap_memory(vma, phys_addr, vma->vm_end - vma->vm_start);
  }
  EXPORT_SYMBOL(vme_master_mmap);
a17a75e26   Martyn Welch   Staging: VME Fram...
692
693
694
695
696
  void vme_master_free(struct vme_resource *resource)
  {
  	struct vme_master_resource *master_image;
  
  	if (resource->type != VME_MASTER) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
697
698
  		printk(KERN_ERR "Not a master resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
699
700
701
702
703
704
  		return;
  	}
  
  	master_image = list_entry(resource->entry, struct vme_master_resource,
  		list);
  	if (master_image == NULL) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
705
706
  		printk(KERN_ERR "Can't find master resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
707
708
709
710
  		return;
  	}
  
  	/* Unlock image */
886953e9b   Emilio G. Cota   staging: vme: sty...
711
  	spin_lock(&master_image->lock);
a17a75e26   Martyn Welch   Staging: VME Fram...
712
713
714
715
716
  	if (master_image->locked == 0)
  		printk(KERN_ERR "Image is already free
  ");
  
  	master_image->locked = 0;
886953e9b   Emilio G. Cota   staging: vme: sty...
717
  	spin_unlock(&master_image->lock);
a17a75e26   Martyn Welch   Staging: VME Fram...
718
719
720
721
722
723
724
725
726
727
  
  	/* Free up resource memory */
  	kfree(resource);
  }
  EXPORT_SYMBOL(vme_master_free);
  
  /*
   * Request a DMA controller with specific attributes, return some unique
   * identifier.
   */
6af04b065   Martyn Welch   Staging: VME: Rem...
728
  struct vme_resource *vme_dma_request(struct vme_dev *vdev, u32 route)
a17a75e26   Martyn Welch   Staging: VME Fram...
729
730
731
732
733
734
735
736
737
738
  {
  	struct vme_bridge *bridge;
  	struct list_head *dma_pos = NULL;
  	struct vme_dma_resource *allocated_ctrlr = NULL;
  	struct vme_dma_resource *dma_ctrlr = NULL;
  	struct vme_resource *resource = NULL;
  
  	/* XXX Not checking resource attributes */
  	printk(KERN_ERR "No VME resource Attribute tests done
  ");
8f966dc44   Manohar Vanga   staging: vme: add...
739
  	bridge = vdev->bridge;
a17a75e26   Martyn Welch   Staging: VME Fram...
740
741
742
743
744
745
746
  	if (bridge == NULL) {
  		printk(KERN_ERR "Can't find VME bus
  ");
  		goto err_bus;
  	}
  
  	/* Loop through DMA resources */
886953e9b   Emilio G. Cota   staging: vme: sty...
747
  	list_for_each(dma_pos, &bridge->dma_resources) {
a17a75e26   Martyn Welch   Staging: VME Fram...
748
749
750
751
  		dma_ctrlr = list_entry(dma_pos,
  			struct vme_dma_resource, list);
  
  		if (dma_ctrlr == NULL) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
752
753
  			printk(KERN_ERR "Registered NULL DMA resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
754
755
  			continue;
  		}
4f723df45   Martyn Welch   Staging: vme: Att...
756
  		/* Find an unlocked and compatible controller */
886953e9b   Emilio G. Cota   staging: vme: sty...
757
  		mutex_lock(&dma_ctrlr->mtx);
4f723df45   Martyn Welch   Staging: vme: Att...
758
759
  		if (((dma_ctrlr->route_attr & route) == route) &&
  			(dma_ctrlr->locked == 0)) {
a17a75e26   Martyn Welch   Staging: VME Fram...
760
  			dma_ctrlr->locked = 1;
886953e9b   Emilio G. Cota   staging: vme: sty...
761
  			mutex_unlock(&dma_ctrlr->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
762
763
764
  			allocated_ctrlr = dma_ctrlr;
  			break;
  		}
886953e9b   Emilio G. Cota   staging: vme: sty...
765
  		mutex_unlock(&dma_ctrlr->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
766
767
768
769
770
771
772
773
774
775
776
777
778
  	}
  
  	/* Check to see if we found a resource */
  	if (allocated_ctrlr == NULL)
  		goto err_ctrlr;
  
  	resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL);
  	if (resource == NULL) {
  		printk(KERN_WARNING "Unable to allocate resource structure
  ");
  		goto err_alloc;
  	}
  	resource->type = VME_DMA;
886953e9b   Emilio G. Cota   staging: vme: sty...
779
  	resource->entry = &allocated_ctrlr->list;
a17a75e26   Martyn Welch   Staging: VME Fram...
780
781
782
783
784
  
  	return resource;
  
  err_alloc:
  	/* Unlock image */
886953e9b   Emilio G. Cota   staging: vme: sty...
785
  	mutex_lock(&dma_ctrlr->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
786
  	dma_ctrlr->locked = 0;
886953e9b   Emilio G. Cota   staging: vme: sty...
787
  	mutex_unlock(&dma_ctrlr->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
788
789
790
791
  err_ctrlr:
  err_bus:
  	return NULL;
  }
58e507987   Martyn Welch   Staging: vme: Ren...
792
  EXPORT_SYMBOL(vme_dma_request);
a17a75e26   Martyn Welch   Staging: VME Fram...
793
794
795
796
797
798
799
800
801
802
  
  /*
   * Start new list
   */
  struct vme_dma_list *vme_new_dma_list(struct vme_resource *resource)
  {
  	struct vme_dma_resource *ctrlr;
  	struct vme_dma_list *dma_list;
  
  	if (resource->type != VME_DMA) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
803
804
  		printk(KERN_ERR "Not a DMA resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
805
806
807
808
  		return NULL;
  	}
  
  	ctrlr = list_entry(resource->entry, struct vme_dma_resource, list);
ead1f3e30   Martyn Welch   Staging: vme: Fix...
809
810
  	dma_list = kmalloc(sizeof(struct vme_dma_list), GFP_KERNEL);
  	if (dma_list == NULL) {
f56c3d4f5   Aaron Sierra   vme: trivial spel...
811
812
  		printk(KERN_ERR "Unable to allocate memory for new DMA list
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
813
814
  		return NULL;
  	}
886953e9b   Emilio G. Cota   staging: vme: sty...
815
  	INIT_LIST_HEAD(&dma_list->entries);
a17a75e26   Martyn Welch   Staging: VME Fram...
816
  	dma_list->parent = ctrlr;
886953e9b   Emilio G. Cota   staging: vme: sty...
817
  	mutex_init(&dma_list->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
818
819
820
821
822
823
824
825
  
  	return dma_list;
  }
  EXPORT_SYMBOL(vme_new_dma_list);
  
  /*
   * Create "Pattern" type attributes
   */
6af04b065   Martyn Welch   Staging: VME: Rem...
826
  struct vme_dma_attr *vme_dma_pattern_attribute(u32 pattern, u32 type)
a17a75e26   Martyn Welch   Staging: VME Fram...
827
828
829
  {
  	struct vme_dma_attr *attributes;
  	struct vme_dma_pattern *pattern_attr;
ead1f3e30   Martyn Welch   Staging: vme: Fix...
830
831
  	attributes = kmalloc(sizeof(struct vme_dma_attr), GFP_KERNEL);
  	if (attributes == NULL) {
25958ce32   Greg Kroah-Hartman   staging: vme: vme...
832
833
  		printk(KERN_ERR "Unable to allocate memory for attributes structure
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
834
835
  		goto err_attr;
  	}
ead1f3e30   Martyn Welch   Staging: vme: Fix...
836
837
  	pattern_attr = kmalloc(sizeof(struct vme_dma_pattern), GFP_KERNEL);
  	if (pattern_attr == NULL) {
25958ce32   Greg Kroah-Hartman   staging: vme: vme...
838
839
  		printk(KERN_ERR "Unable to allocate memory for pattern attributes
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
840
841
842
843
844
845
846
847
848
849
  		goto err_pat;
  	}
  
  	attributes->type = VME_DMA_PATTERN;
  	attributes->private = (void *)pattern_attr;
  
  	pattern_attr->pattern = pattern;
  	pattern_attr->type = type;
  
  	return attributes;
a17a75e26   Martyn Welch   Staging: VME Fram...
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
  err_pat:
  	kfree(attributes);
  err_attr:
  	return NULL;
  }
  EXPORT_SYMBOL(vme_dma_pattern_attribute);
  
  /*
   * Create "PCI" type attributes
   */
  struct vme_dma_attr *vme_dma_pci_attribute(dma_addr_t address)
  {
  	struct vme_dma_attr *attributes;
  	struct vme_dma_pci *pci_attr;
  
  	/* XXX Run some sanity checks here */
ead1f3e30   Martyn Welch   Staging: vme: Fix...
866
867
  	attributes = kmalloc(sizeof(struct vme_dma_attr), GFP_KERNEL);
  	if (attributes == NULL) {
25958ce32   Greg Kroah-Hartman   staging: vme: vme...
868
869
  		printk(KERN_ERR "Unable to allocate memory for attributes structure
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
870
871
  		goto err_attr;
  	}
ead1f3e30   Martyn Welch   Staging: vme: Fix...
872
873
  	pci_attr = kmalloc(sizeof(struct vme_dma_pci), GFP_KERNEL);
  	if (pci_attr == NULL) {
f56c3d4f5   Aaron Sierra   vme: trivial spel...
874
875
  		printk(KERN_ERR "Unable to allocate memory for PCI attributes
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
876
877
878
879
880
881
882
883
884
885
886
  		goto err_pci;
  	}
  
  
  
  	attributes->type = VME_DMA_PCI;
  	attributes->private = (void *)pci_attr;
  
  	pci_attr->address = address;
  
  	return attributes;
a17a75e26   Martyn Welch   Staging: VME Fram...
887
888
889
890
891
892
893
894
895
896
897
  err_pci:
  	kfree(attributes);
  err_attr:
  	return NULL;
  }
  EXPORT_SYMBOL(vme_dma_pci_attribute);
  
  /*
   * Create "VME" type attributes
   */
  struct vme_dma_attr *vme_dma_vme_attribute(unsigned long long address,
6af04b065   Martyn Welch   Staging: VME: Rem...
898
  	u32 aspace, u32 cycle, u32 dwidth)
a17a75e26   Martyn Welch   Staging: VME Fram...
899
900
901
  {
  	struct vme_dma_attr *attributes;
  	struct vme_dma_vme *vme_attr;
ead1f3e30   Martyn Welch   Staging: vme: Fix...
902
  	attributes = kmalloc(
a17a75e26   Martyn Welch   Staging: VME Fram...
903
  		sizeof(struct vme_dma_attr), GFP_KERNEL);
ead1f3e30   Martyn Welch   Staging: vme: Fix...
904
  	if (attributes == NULL) {
25958ce32   Greg Kroah-Hartman   staging: vme: vme...
905
906
  		printk(KERN_ERR "Unable to allocate memory for attributes structure
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
907
908
  		goto err_attr;
  	}
ead1f3e30   Martyn Welch   Staging: vme: Fix...
909
910
  	vme_attr = kmalloc(sizeof(struct vme_dma_vme), GFP_KERNEL);
  	if (vme_attr == NULL) {
f56c3d4f5   Aaron Sierra   vme: trivial spel...
911
912
  		printk(KERN_ERR "Unable to allocate memory for VME attributes
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
913
914
915
916
917
918
919
920
921
922
923
924
  		goto err_vme;
  	}
  
  	attributes->type = VME_DMA_VME;
  	attributes->private = (void *)vme_attr;
  
  	vme_attr->address = address;
  	vme_attr->aspace = aspace;
  	vme_attr->cycle = cycle;
  	vme_attr->dwidth = dwidth;
  
  	return attributes;
a17a75e26   Martyn Welch   Staging: VME Fram...
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
  err_vme:
  	kfree(attributes);
  err_attr:
  	return NULL;
  }
  EXPORT_SYMBOL(vme_dma_vme_attribute);
  
  /*
   * Free attribute
   */
  void vme_dma_free_attribute(struct vme_dma_attr *attributes)
  {
  	kfree(attributes->private);
  	kfree(attributes);
  }
  EXPORT_SYMBOL(vme_dma_free_attribute);
  
  int vme_dma_list_add(struct vme_dma_list *list, struct vme_dma_attr *src,
  	struct vme_dma_attr *dest, size_t count)
  {
  	struct vme_bridge *bridge = list->parent->parent;
  	int retval;
  
  	if (bridge->dma_list_add == NULL) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
949
950
  		printk(KERN_WARNING "Link List DMA generation not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
951
952
  		return -EINVAL;
  	}
886953e9b   Emilio G. Cota   staging: vme: sty...
953
  	if (!mutex_trylock(&list->mtx)) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
954
955
  		printk(KERN_ERR "Link List already submitted
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
956
957
958
959
  		return -EINVAL;
  	}
  
  	retval = bridge->dma_list_add(list, src, dest, count);
886953e9b   Emilio G. Cota   staging: vme: sty...
960
  	mutex_unlock(&list->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
961
962
963
964
965
966
967
968
969
970
971
  
  	return retval;
  }
  EXPORT_SYMBOL(vme_dma_list_add);
  
  int vme_dma_list_exec(struct vme_dma_list *list)
  {
  	struct vme_bridge *bridge = list->parent->parent;
  	int retval;
  
  	if (bridge->dma_list_exec == NULL) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
972
973
  		printk(KERN_ERR "Link List DMA execution not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
974
975
  		return -EINVAL;
  	}
886953e9b   Emilio G. Cota   staging: vme: sty...
976
  	mutex_lock(&list->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
977
978
  
  	retval = bridge->dma_list_exec(list);
886953e9b   Emilio G. Cota   staging: vme: sty...
979
  	mutex_unlock(&list->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
980
981
982
983
984
985
986
987
988
989
990
  
  	return retval;
  }
  EXPORT_SYMBOL(vme_dma_list_exec);
  
  int vme_dma_list_free(struct vme_dma_list *list)
  {
  	struct vme_bridge *bridge = list->parent->parent;
  	int retval;
  
  	if (bridge->dma_list_empty == NULL) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
991
992
  		printk(KERN_WARNING "Emptying of Link Lists not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
993
994
  		return -EINVAL;
  	}
886953e9b   Emilio G. Cota   staging: vme: sty...
995
  	if (!mutex_trylock(&list->mtx)) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
996
997
  		printk(KERN_ERR "Link List in use
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
998
999
1000
1001
  		return -EINVAL;
  	}
  
  	/*
f56c3d4f5   Aaron Sierra   vme: trivial spel...
1002
1003
  	 * Empty out all of the entries from the DMA list. We need to go to the
  	 * low level driver as DMA entries are driver specific.
a17a75e26   Martyn Welch   Staging: VME Fram...
1004
1005
1006
  	 */
  	retval = bridge->dma_list_empty(list);
  	if (retval) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
1007
1008
  		printk(KERN_ERR "Unable to empty link-list entries
  ");
886953e9b   Emilio G. Cota   staging: vme: sty...
1009
  		mutex_unlock(&list->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
1010
1011
  		return retval;
  	}
886953e9b   Emilio G. Cota   staging: vme: sty...
1012
  	mutex_unlock(&list->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
  	kfree(list);
  
  	return retval;
  }
  EXPORT_SYMBOL(vme_dma_list_free);
  
  int vme_dma_free(struct vme_resource *resource)
  {
  	struct vme_dma_resource *ctrlr;
  
  	if (resource->type != VME_DMA) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
1024
1025
  		printk(KERN_ERR "Not a DMA resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1026
1027
1028
1029
  		return -EINVAL;
  	}
  
  	ctrlr = list_entry(resource->entry, struct vme_dma_resource, list);
886953e9b   Emilio G. Cota   staging: vme: sty...
1030
  	if (!mutex_trylock(&ctrlr->mtx)) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
1031
1032
  		printk(KERN_ERR "Resource busy, can't free
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1033
1034
  		return -EBUSY;
  	}
886953e9b   Emilio G. Cota   staging: vme: sty...
1035
  	if (!(list_empty(&ctrlr->pending) && list_empty(&ctrlr->running))) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
1036
1037
  		printk(KERN_WARNING "Resource still processing transfers
  ");
886953e9b   Emilio G. Cota   staging: vme: sty...
1038
  		mutex_unlock(&ctrlr->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
1039
1040
1041
1042
  		return -EBUSY;
  	}
  
  	ctrlr->locked = 0;
886953e9b   Emilio G. Cota   staging: vme: sty...
1043
  	mutex_unlock(&ctrlr->mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
1044

fd5c25614   Martyn Welch   vme: Free DMA res...
1045
  	kfree(resource);
a17a75e26   Martyn Welch   Staging: VME Fram...
1046
1047
1048
  	return 0;
  }
  EXPORT_SYMBOL(vme_dma_free);
e2c6393fd   Dmitry Kalinkin   vme: move tsi148 ...
1049
  void vme_bus_error_handler(struct vme_bridge *bridge,
472f16f33   Dmitry Kalinkin   vme: include addr...
1050
  			   unsigned long long address, int am)
e2c6393fd   Dmitry Kalinkin   vme: move tsi148 ...
1051
  {
0b0496625   Dmitry Kalinkin   vme: change bus e...
1052
1053
  	struct list_head *handler_pos = NULL;
  	struct vme_error_handler *handler;
448535a35   Dmitry Kalinkin   vme: print unhand...
1054
  	int handler_triggered = 0;
0b0496625   Dmitry Kalinkin   vme: change bus e...
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
  	u32 aspace = vme_get_aspace(am);
  
  	list_for_each(handler_pos, &bridge->vme_error_handlers) {
  		handler = list_entry(handler_pos, struct vme_error_handler,
  				     list);
  		if ((aspace == handler->aspace) &&
  		    (address >= handler->start) &&
  		    (address < handler->end)) {
  			if (!handler->num_errors)
  				handler->first_error = address;
  			if (handler->num_errors != UINT_MAX)
  				handler->num_errors++;
448535a35   Dmitry Kalinkin   vme: print unhand...
1067
  			handler_triggered = 1;
0b0496625   Dmitry Kalinkin   vme: change bus e...
1068
  		}
e2c6393fd   Dmitry Kalinkin   vme: move tsi148 ...
1069
  	}
448535a35   Dmitry Kalinkin   vme: print unhand...
1070
1071
1072
1073
1074
1075
  
  	if (!handler_triggered)
  		dev_err(bridge->parent,
  			"Unhandled VME access error at address 0x%llx
  ",
  			address);
e2c6393fd   Dmitry Kalinkin   vme: move tsi148 ...
1076
1077
  }
  EXPORT_SYMBOL(vme_bus_error_handler);
0b0496625   Dmitry Kalinkin   vme: change bus e...
1078
1079
1080
  struct vme_error_handler *vme_register_error_handler(
  	struct vme_bridge *bridge, u32 aspace,
  	unsigned long long address, size_t len)
e2c6393fd   Dmitry Kalinkin   vme: move tsi148 ...
1081
  {
0b0496625   Dmitry Kalinkin   vme: change bus e...
1082
  	struct vme_error_handler *handler;
e2c6393fd   Dmitry Kalinkin   vme: move tsi148 ...
1083

0b0496625   Dmitry Kalinkin   vme: change bus e...
1084
1085
1086
  	handler = kmalloc(sizeof(*handler), GFP_KERNEL);
  	if (!handler)
  		return NULL;
e2c6393fd   Dmitry Kalinkin   vme: move tsi148 ...
1087

0b0496625   Dmitry Kalinkin   vme: change bus e...
1088
1089
1090
1091
1092
1093
  	handler->aspace = aspace;
  	handler->start = address;
  	handler->end = address + len;
  	handler->num_errors = 0;
  	handler->first_error = 0;
  	list_add_tail(&handler->list, &bridge->vme_error_handlers);
e2c6393fd   Dmitry Kalinkin   vme: move tsi148 ...
1094

0b0496625   Dmitry Kalinkin   vme: change bus e...
1095
  	return handler;
e2c6393fd   Dmitry Kalinkin   vme: move tsi148 ...
1096
  }
0b0496625   Dmitry Kalinkin   vme: change bus e...
1097
  EXPORT_SYMBOL(vme_register_error_handler);
e2c6393fd   Dmitry Kalinkin   vme: move tsi148 ...
1098

0b0496625   Dmitry Kalinkin   vme: change bus e...
1099
  void vme_unregister_error_handler(struct vme_error_handler *handler)
e2c6393fd   Dmitry Kalinkin   vme: move tsi148 ...
1100
  {
0b0496625   Dmitry Kalinkin   vme: change bus e...
1101
1102
  	list_del(&handler->list);
  	kfree(handler);
e2c6393fd   Dmitry Kalinkin   vme: move tsi148 ...
1103
  }
0b0496625   Dmitry Kalinkin   vme: change bus e...
1104
  EXPORT_SYMBOL(vme_unregister_error_handler);
e2c6393fd   Dmitry Kalinkin   vme: move tsi148 ...
1105

c813f592a   Martyn Welch   Staging: vme: Pul...
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
  void vme_irq_handler(struct vme_bridge *bridge, int level, int statid)
  {
  	void (*call)(int, int, void *);
  	void *priv_data;
  
  	call = bridge->irq[level - 1].callback[statid].func;
  	priv_data = bridge->irq[level - 1].callback[statid].priv_data;
  
  	if (call != NULL)
  		call(level, statid, priv_data);
  	else
f56c3d4f5   Aaron Sierra   vme: trivial spel...
1117
1118
  		printk(KERN_WARNING "Spurious VME interrupt, level:%x, vector:%x
  ",
25958ce32   Greg Kroah-Hartman   staging: vme: vme...
1119
  		       level, statid);
c813f592a   Martyn Welch   Staging: vme: Pul...
1120
1121
  }
  EXPORT_SYMBOL(vme_irq_handler);
8f966dc44   Manohar Vanga   staging: vme: add...
1122
  int vme_irq_request(struct vme_dev *vdev, int level, int statid,
29848ac9f   Martyn Welch   Staging: vme: Ena...
1123
  	void (*callback)(int, int, void *),
a17a75e26   Martyn Welch   Staging: VME Fram...
1124
1125
1126
  	void *priv_data)
  {
  	struct vme_bridge *bridge;
8f966dc44   Manohar Vanga   staging: vme: add...
1127
  	bridge = vdev->bridge;
a17a75e26   Martyn Welch   Staging: VME Fram...
1128
1129
1130
1131
1132
  	if (bridge == NULL) {
  		printk(KERN_ERR "Can't find VME bus
  ");
  		return -EINVAL;
  	}
ead1f3e30   Martyn Welch   Staging: vme: Fix...
1133
  	if ((level < 1) || (level > 7)) {
c813f592a   Martyn Welch   Staging: vme: Pul...
1134
1135
  		printk(KERN_ERR "Invalid interrupt level
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1136
1137
  		return -EINVAL;
  	}
c813f592a   Martyn Welch   Staging: vme: Pul...
1138
1139
1140
  	if (bridge->irq_set == NULL) {
  		printk(KERN_ERR "Configuring interrupts not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1141
1142
  		return -EINVAL;
  	}
886953e9b   Emilio G. Cota   staging: vme: sty...
1143
  	mutex_lock(&bridge->irq_mtx);
c813f592a   Martyn Welch   Staging: vme: Pul...
1144
1145
  
  	if (bridge->irq[level - 1].callback[statid].func) {
886953e9b   Emilio G. Cota   staging: vme: sty...
1146
  		mutex_unlock(&bridge->irq_mtx);
c813f592a   Martyn Welch   Staging: vme: Pul...
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
  		printk(KERN_WARNING "VME Interrupt already taken
  ");
  		return -EBUSY;
  	}
  
  	bridge->irq[level - 1].count++;
  	bridge->irq[level - 1].callback[statid].priv_data = priv_data;
  	bridge->irq[level - 1].callback[statid].func = callback;
  
  	/* Enable IRQ level */
29848ac9f   Martyn Welch   Staging: vme: Ena...
1157
  	bridge->irq_set(bridge, level, 1, 1);
c813f592a   Martyn Welch   Staging: vme: Pul...
1158

886953e9b   Emilio G. Cota   staging: vme: sty...
1159
  	mutex_unlock(&bridge->irq_mtx);
c813f592a   Martyn Welch   Staging: vme: Pul...
1160
1161
  
  	return 0;
a17a75e26   Martyn Welch   Staging: VME Fram...
1162
  }
c813f592a   Martyn Welch   Staging: vme: Pul...
1163
  EXPORT_SYMBOL(vme_irq_request);
a17a75e26   Martyn Welch   Staging: VME Fram...
1164

8f966dc44   Manohar Vanga   staging: vme: add...
1165
  void vme_irq_free(struct vme_dev *vdev, int level, int statid)
a17a75e26   Martyn Welch   Staging: VME Fram...
1166
1167
  {
  	struct vme_bridge *bridge;
8f966dc44   Manohar Vanga   staging: vme: add...
1168
  	bridge = vdev->bridge;
a17a75e26   Martyn Welch   Staging: VME Fram...
1169
1170
1171
1172
1173
  	if (bridge == NULL) {
  		printk(KERN_ERR "Can't find VME bus
  ");
  		return;
  	}
ead1f3e30   Martyn Welch   Staging: vme: Fix...
1174
  	if ((level < 1) || (level > 7)) {
c813f592a   Martyn Welch   Staging: vme: Pul...
1175
1176
  		printk(KERN_ERR "Invalid interrupt level
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1177
1178
  		return;
  	}
c813f592a   Martyn Welch   Staging: vme: Pul...
1179
1180
1181
  	if (bridge->irq_set == NULL) {
  		printk(KERN_ERR "Configuring interrupts not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1182
1183
  		return;
  	}
886953e9b   Emilio G. Cota   staging: vme: sty...
1184
  	mutex_lock(&bridge->irq_mtx);
c813f592a   Martyn Welch   Staging: vme: Pul...
1185
1186
1187
1188
1189
  
  	bridge->irq[level - 1].count--;
  
  	/* Disable IRQ level if no more interrupts attached at this level*/
  	if (bridge->irq[level - 1].count == 0)
29848ac9f   Martyn Welch   Staging: vme: Ena...
1190
  		bridge->irq_set(bridge, level, 0, 1);
c813f592a   Martyn Welch   Staging: vme: Pul...
1191
1192
1193
  
  	bridge->irq[level - 1].callback[statid].func = NULL;
  	bridge->irq[level - 1].callback[statid].priv_data = NULL;
886953e9b   Emilio G. Cota   staging: vme: sty...
1194
  	mutex_unlock(&bridge->irq_mtx);
a17a75e26   Martyn Welch   Staging: VME Fram...
1195
  }
c813f592a   Martyn Welch   Staging: vme: Pul...
1196
  EXPORT_SYMBOL(vme_irq_free);
a17a75e26   Martyn Welch   Staging: VME Fram...
1197

8f966dc44   Manohar Vanga   staging: vme: add...
1198
  int vme_irq_generate(struct vme_dev *vdev, int level, int statid)
a17a75e26   Martyn Welch   Staging: VME Fram...
1199
1200
  {
  	struct vme_bridge *bridge;
8f966dc44   Manohar Vanga   staging: vme: add...
1201
  	bridge = vdev->bridge;
a17a75e26   Martyn Welch   Staging: VME Fram...
1202
1203
1204
1205
1206
  	if (bridge == NULL) {
  		printk(KERN_ERR "Can't find VME bus
  ");
  		return -EINVAL;
  	}
ead1f3e30   Martyn Welch   Staging: vme: Fix...
1207
  	if ((level < 1) || (level > 7)) {
a17a75e26   Martyn Welch   Staging: VME Fram...
1208
1209
1210
1211
  		printk(KERN_WARNING "Invalid interrupt level
  ");
  		return -EINVAL;
  	}
c813f592a   Martyn Welch   Staging: vme: Pul...
1212
  	if (bridge->irq_generate == NULL) {
ead1f3e30   Martyn Welch   Staging: vme: Fix...
1213
1214
  		printk(KERN_WARNING "Interrupt generation not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1215
1216
  		return -EINVAL;
  	}
29848ac9f   Martyn Welch   Staging: vme: Ena...
1217
  	return bridge->irq_generate(bridge, level, statid);
a17a75e26   Martyn Welch   Staging: VME Fram...
1218
  }
c813f592a   Martyn Welch   Staging: vme: Pul...
1219
  EXPORT_SYMBOL(vme_irq_generate);
a17a75e26   Martyn Welch   Staging: VME Fram...
1220

42fb50312   Martyn Welch   Staging: vme: add...
1221
1222
1223
  /*
   * Request the location monitor, return resource or NULL
   */
8f966dc44   Manohar Vanga   staging: vme: add...
1224
  struct vme_resource *vme_lm_request(struct vme_dev *vdev)
a17a75e26   Martyn Welch   Staging: VME Fram...
1225
1226
  {
  	struct vme_bridge *bridge;
42fb50312   Martyn Welch   Staging: vme: add...
1227
1228
1229
1230
  	struct list_head *lm_pos = NULL;
  	struct vme_lm_resource *allocated_lm = NULL;
  	struct vme_lm_resource *lm = NULL;
  	struct vme_resource *resource = NULL;
a17a75e26   Martyn Welch   Staging: VME Fram...
1231

8f966dc44   Manohar Vanga   staging: vme: add...
1232
  	bridge = vdev->bridge;
a17a75e26   Martyn Welch   Staging: VME Fram...
1233
1234
1235
  	if (bridge == NULL) {
  		printk(KERN_ERR "Can't find VME bus
  ");
42fb50312   Martyn Welch   Staging: vme: add...
1236
1237
1238
1239
  		goto err_bus;
  	}
  
  	/* Loop through DMA resources */
886953e9b   Emilio G. Cota   staging: vme: sty...
1240
  	list_for_each(lm_pos, &bridge->lm_resources) {
42fb50312   Martyn Welch   Staging: vme: add...
1241
1242
1243
1244
  		lm = list_entry(lm_pos,
  			struct vme_lm_resource, list);
  
  		if (lm == NULL) {
25958ce32   Greg Kroah-Hartman   staging: vme: vme...
1245
1246
  			printk(KERN_ERR "Registered NULL Location Monitor resource
  ");
42fb50312   Martyn Welch   Staging: vme: add...
1247
1248
1249
1250
  			continue;
  		}
  
  		/* Find an unlocked controller */
886953e9b   Emilio G. Cota   staging: vme: sty...
1251
  		mutex_lock(&lm->mtx);
42fb50312   Martyn Welch   Staging: vme: add...
1252
1253
  		if (lm->locked == 0) {
  			lm->locked = 1;
886953e9b   Emilio G. Cota   staging: vme: sty...
1254
  			mutex_unlock(&lm->mtx);
42fb50312   Martyn Welch   Staging: vme: add...
1255
1256
1257
  			allocated_lm = lm;
  			break;
  		}
886953e9b   Emilio G. Cota   staging: vme: sty...
1258
  		mutex_unlock(&lm->mtx);
42fb50312   Martyn Welch   Staging: vme: add...
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
  	}
  
  	/* Check to see if we found a resource */
  	if (allocated_lm == NULL)
  		goto err_lm;
  
  	resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL);
  	if (resource == NULL) {
  		printk(KERN_ERR "Unable to allocate resource structure
  ");
  		goto err_alloc;
  	}
  	resource->type = VME_LM;
886953e9b   Emilio G. Cota   staging: vme: sty...
1272
  	resource->entry = &allocated_lm->list;
42fb50312   Martyn Welch   Staging: vme: add...
1273
1274
1275
1276
1277
  
  	return resource;
  
  err_alloc:
  	/* Unlock image */
886953e9b   Emilio G. Cota   staging: vme: sty...
1278
  	mutex_lock(&lm->mtx);
42fb50312   Martyn Welch   Staging: vme: add...
1279
  	lm->locked = 0;
886953e9b   Emilio G. Cota   staging: vme: sty...
1280
  	mutex_unlock(&lm->mtx);
42fb50312   Martyn Welch   Staging: vme: add...
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
  err_lm:
  err_bus:
  	return NULL;
  }
  EXPORT_SYMBOL(vme_lm_request);
  
  int vme_lm_count(struct vme_resource *resource)
  {
  	struct vme_lm_resource *lm;
  
  	if (resource->type != VME_LM) {
  		printk(KERN_ERR "Not a Location Monitor resource
  ");
  		return -EINVAL;
  	}
  
  	lm = list_entry(resource->entry, struct vme_lm_resource, list);
  
  	return lm->monitors;
  }
  EXPORT_SYMBOL(vme_lm_count);
  
  int vme_lm_set(struct vme_resource *resource, unsigned long long lm_base,
6af04b065   Martyn Welch   Staging: VME: Rem...
1304
  	u32 aspace, u32 cycle)
42fb50312   Martyn Welch   Staging: vme: add...
1305
1306
1307
1308
1309
1310
1311
  {
  	struct vme_bridge *bridge = find_bridge(resource);
  	struct vme_lm_resource *lm;
  
  	if (resource->type != VME_LM) {
  		printk(KERN_ERR "Not a Location Monitor resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1312
1313
  		return -EINVAL;
  	}
42fb50312   Martyn Welch   Staging: vme: add...
1314
  	lm = list_entry(resource->entry, struct vme_lm_resource, list);
a17a75e26   Martyn Welch   Staging: VME Fram...
1315
  	if (bridge->lm_set == NULL) {
42fb50312   Martyn Welch   Staging: vme: add...
1316
1317
  		printk(KERN_ERR "vme_lm_set not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1318
1319
  		return -EINVAL;
  	}
8be9226c8   Martyn Welch   Staging: vme: Cor...
1320
  	return bridge->lm_set(lm, lm_base, aspace, cycle);
a17a75e26   Martyn Welch   Staging: VME Fram...
1321
1322
  }
  EXPORT_SYMBOL(vme_lm_set);
42fb50312   Martyn Welch   Staging: vme: add...
1323
  int vme_lm_get(struct vme_resource *resource, unsigned long long *lm_base,
6af04b065   Martyn Welch   Staging: VME: Rem...
1324
  	u32 *aspace, u32 *cycle)
a17a75e26   Martyn Welch   Staging: VME Fram...
1325
  {
42fb50312   Martyn Welch   Staging: vme: add...
1326
1327
  	struct vme_bridge *bridge = find_bridge(resource);
  	struct vme_lm_resource *lm;
a17a75e26   Martyn Welch   Staging: VME Fram...
1328

42fb50312   Martyn Welch   Staging: vme: add...
1329
1330
1331
  	if (resource->type != VME_LM) {
  		printk(KERN_ERR "Not a Location Monitor resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1332
1333
  		return -EINVAL;
  	}
42fb50312   Martyn Welch   Staging: vme: add...
1334
  	lm = list_entry(resource->entry, struct vme_lm_resource, list);
a17a75e26   Martyn Welch   Staging: VME Fram...
1335
  	if (bridge->lm_get == NULL) {
42fb50312   Martyn Welch   Staging: vme: add...
1336
1337
  		printk(KERN_ERR "vme_lm_get not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1338
1339
  		return -EINVAL;
  	}
42fb50312   Martyn Welch   Staging: vme: add...
1340
  	return bridge->lm_get(lm, lm_base, aspace, cycle);
a17a75e26   Martyn Welch   Staging: VME Fram...
1341
1342
  }
  EXPORT_SYMBOL(vme_lm_get);
42fb50312   Martyn Welch   Staging: vme: add...
1343
1344
  int vme_lm_attach(struct vme_resource *resource, int monitor,
  	void (*callback)(int))
a17a75e26   Martyn Welch   Staging: VME Fram...
1345
  {
42fb50312   Martyn Welch   Staging: vme: add...
1346
1347
  	struct vme_bridge *bridge = find_bridge(resource);
  	struct vme_lm_resource *lm;
a17a75e26   Martyn Welch   Staging: VME Fram...
1348

42fb50312   Martyn Welch   Staging: vme: add...
1349
1350
1351
  	if (resource->type != VME_LM) {
  		printk(KERN_ERR "Not a Location Monitor resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1352
1353
  		return -EINVAL;
  	}
42fb50312   Martyn Welch   Staging: vme: add...
1354
  	lm = list_entry(resource->entry, struct vme_lm_resource, list);
a17a75e26   Martyn Welch   Staging: VME Fram...
1355
  	if (bridge->lm_attach == NULL) {
42fb50312   Martyn Welch   Staging: vme: add...
1356
1357
  		printk(KERN_ERR "vme_lm_attach not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1358
1359
  		return -EINVAL;
  	}
42fb50312   Martyn Welch   Staging: vme: add...
1360
  	return bridge->lm_attach(lm, monitor, callback);
a17a75e26   Martyn Welch   Staging: VME Fram...
1361
1362
  }
  EXPORT_SYMBOL(vme_lm_attach);
42fb50312   Martyn Welch   Staging: vme: add...
1363
  int vme_lm_detach(struct vme_resource *resource, int monitor)
a17a75e26   Martyn Welch   Staging: VME Fram...
1364
  {
42fb50312   Martyn Welch   Staging: vme: add...
1365
1366
  	struct vme_bridge *bridge = find_bridge(resource);
  	struct vme_lm_resource *lm;
a17a75e26   Martyn Welch   Staging: VME Fram...
1367

42fb50312   Martyn Welch   Staging: vme: add...
1368
1369
1370
  	if (resource->type != VME_LM) {
  		printk(KERN_ERR "Not a Location Monitor resource
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1371
1372
  		return -EINVAL;
  	}
42fb50312   Martyn Welch   Staging: vme: add...
1373
  	lm = list_entry(resource->entry, struct vme_lm_resource, list);
a17a75e26   Martyn Welch   Staging: VME Fram...
1374
  	if (bridge->lm_detach == NULL) {
42fb50312   Martyn Welch   Staging: vme: add...
1375
1376
  		printk(KERN_ERR "vme_lm_detach not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1377
1378
  		return -EINVAL;
  	}
42fb50312   Martyn Welch   Staging: vme: add...
1379
  	return bridge->lm_detach(lm, monitor);
a17a75e26   Martyn Welch   Staging: VME Fram...
1380
1381
  }
  EXPORT_SYMBOL(vme_lm_detach);
42fb50312   Martyn Welch   Staging: vme: add...
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
  void vme_lm_free(struct vme_resource *resource)
  {
  	struct vme_lm_resource *lm;
  
  	if (resource->type != VME_LM) {
  		printk(KERN_ERR "Not a Location Monitor resource
  ");
  		return;
  	}
  
  	lm = list_entry(resource->entry, struct vme_lm_resource, list);
886953e9b   Emilio G. Cota   staging: vme: sty...
1393
  	mutex_lock(&lm->mtx);
42fb50312   Martyn Welch   Staging: vme: add...
1394

8be9226c8   Martyn Welch   Staging: vme: Cor...
1395
1396
1397
1398
  	/* XXX
  	 * Check to see that there aren't any callbacks still attached, if
  	 * there are we should probably be detaching them!
  	 */
42fb50312   Martyn Welch   Staging: vme: add...
1399
1400
  
  	lm->locked = 0;
886953e9b   Emilio G. Cota   staging: vme: sty...
1401
  	mutex_unlock(&lm->mtx);
8be9226c8   Martyn Welch   Staging: vme: Cor...
1402
1403
  
  	kfree(resource);
42fb50312   Martyn Welch   Staging: vme: add...
1404
1405
  }
  EXPORT_SYMBOL(vme_lm_free);
d7729f0fc   Martyn Welch   VME: Rename vme_s...
1406
  int vme_slot_num(struct vme_dev *vdev)
a17a75e26   Martyn Welch   Staging: VME Fram...
1407
1408
  {
  	struct vme_bridge *bridge;
8f966dc44   Manohar Vanga   staging: vme: add...
1409
  	bridge = vdev->bridge;
a17a75e26   Martyn Welch   Staging: VME Fram...
1410
1411
1412
1413
1414
1415
1416
  	if (bridge == NULL) {
  		printk(KERN_ERR "Can't find VME bus
  ");
  		return -EINVAL;
  	}
  
  	if (bridge->slot_get == NULL) {
d7729f0fc   Martyn Welch   VME: Rename vme_s...
1417
1418
  		printk(KERN_WARNING "vme_slot_num not supported
  ");
a17a75e26   Martyn Welch   Staging: VME Fram...
1419
1420
  		return -EINVAL;
  	}
29848ac9f   Martyn Welch   Staging: vme: Ena...
1421
  	return bridge->slot_get(bridge);
a17a75e26   Martyn Welch   Staging: VME Fram...
1422
  }
d7729f0fc   Martyn Welch   VME: Rename vme_s...
1423
  EXPORT_SYMBOL(vme_slot_num);
a17a75e26   Martyn Welch   Staging: VME Fram...
1424

978f47d64   Martyn Welch   VME: Provide acce...
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
  int vme_bus_num(struct vme_dev *vdev)
  {
  	struct vme_bridge *bridge;
  
  	bridge = vdev->bridge;
  	if (bridge == NULL) {
  		pr_err("Can't find VME bus
  ");
  		return -EINVAL;
  	}
  
  	return bridge->num;
  }
  EXPORT_SYMBOL(vme_bus_num);
a17a75e26   Martyn Welch   Staging: VME Fram...
1439
1440
  
  /* - Bridge Registration --------------------------------------------------- */
5b93c2a2f   Manohar Vanga   staging: vme: rem...
1441
1442
1443
1444
  static void vme_dev_release(struct device *dev)
  {
  	kfree(dev_to_vme_dev(dev));
  }
326071b3c   Aaron Sierra   vme: add vme_init...
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
  /* Common bridge initialization */
  struct vme_bridge *vme_init_bridge(struct vme_bridge *bridge)
  {
  	INIT_LIST_HEAD(&bridge->vme_error_handlers);
  	INIT_LIST_HEAD(&bridge->master_resources);
  	INIT_LIST_HEAD(&bridge->slave_resources);
  	INIT_LIST_HEAD(&bridge->dma_resources);
  	INIT_LIST_HEAD(&bridge->lm_resources);
  	mutex_init(&bridge->irq_mtx);
  
  	return bridge;
  }
  EXPORT_SYMBOL(vme_init_bridge);
5b93c2a2f   Manohar Vanga   staging: vme: rem...
1458
  int vme_register_bridge(struct vme_bridge *bridge)
a17a75e26   Martyn Welch   Staging: VME Fram...
1459
1460
  {
  	int i;
733e3ef0d   Manohar Vanga   staging: vme: kee...
1461
  	int ret = -1;
a17a75e26   Martyn Welch   Staging: VME Fram...
1462

733e3ef0d   Manohar Vanga   staging: vme: kee...
1463
  	mutex_lock(&vme_buses_lock);
a17a75e26   Martyn Welch   Staging: VME Fram...
1464
  	for (i = 0; i < sizeof(vme_bus_numbers) * 8; i++) {
733e3ef0d   Manohar Vanga   staging: vme: kee...
1465
1466
1467
  		if ((vme_bus_numbers & (1 << i)) == 0) {
  			vme_bus_numbers |= (1 << i);
  			bridge->num = i;
5d6abf379   Manohar Vanga   staging: vme: mak...
1468
  			INIT_LIST_HEAD(&bridge->devices);
733e3ef0d   Manohar Vanga   staging: vme: kee...
1469
1470
  			list_add_tail(&bridge->bus_list, &vme_bus_list);
  			ret = 0;
a17a75e26   Martyn Welch   Staging: VME Fram...
1471
1472
1473
  			break;
  		}
  	}
733e3ef0d   Manohar Vanga   staging: vme: kee...
1474
  	mutex_unlock(&vme_buses_lock);
a17a75e26   Martyn Welch   Staging: VME Fram...
1475

733e3ef0d   Manohar Vanga   staging: vme: kee...
1476
  	return ret;
a17a75e26   Martyn Welch   Staging: VME Fram...
1477
  }
5b93c2a2f   Manohar Vanga   staging: vme: rem...
1478
  EXPORT_SYMBOL(vme_register_bridge);
a17a75e26   Martyn Welch   Staging: VME Fram...
1479

5b93c2a2f   Manohar Vanga   staging: vme: rem...
1480
  void vme_unregister_bridge(struct vme_bridge *bridge)
a17a75e26   Martyn Welch   Staging: VME Fram...
1481
  {
5d6abf379   Manohar Vanga   staging: vme: mak...
1482
1483
  	struct vme_dev *vdev;
  	struct vme_dev *tmp;
733e3ef0d   Manohar Vanga   staging: vme: kee...
1484
1485
  	mutex_lock(&vme_buses_lock);
  	vme_bus_numbers &= ~(1 << bridge->num);
5d6abf379   Manohar Vanga   staging: vme: mak...
1486
1487
1488
1489
1490
  	list_for_each_entry_safe(vdev, tmp, &bridge->devices, bridge_list) {
  		list_del(&vdev->drv_list);
  		list_del(&vdev->bridge_list);
  		device_unregister(&vdev->dev);
  	}
733e3ef0d   Manohar Vanga   staging: vme: kee...
1491
1492
  	list_del(&bridge->bus_list);
  	mutex_unlock(&vme_buses_lock);
a17a75e26   Martyn Welch   Staging: VME Fram...
1493
  }
5d6abf379   Manohar Vanga   staging: vme: mak...
1494
  EXPORT_SYMBOL(vme_unregister_bridge);
a17a75e26   Martyn Welch   Staging: VME Fram...
1495

5d6abf379   Manohar Vanga   staging: vme: mak...
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
  /* - Driver Registration --------------------------------------------------- */
  
  static int __vme_register_driver_bus(struct vme_driver *drv,
  	struct vme_bridge *bridge, unsigned int ndevs)
  {
  	int err;
  	unsigned int i;
  	struct vme_dev *vdev;
  	struct vme_dev *tmp;
  
  	for (i = 0; i < ndevs; i++) {
  		vdev = kzalloc(sizeof(struct vme_dev), GFP_KERNEL);
  		if (!vdev) {
  			err = -ENOMEM;
f6c39d4f2   Manohar Vanga   staging: vme: cha...
1510
1511
  			goto err_devalloc;
  		}
a916a391d   Manohar Vanga   staging: vme: get...
1512
  		vdev->num = i;
8f966dc44   Manohar Vanga   staging: vme: add...
1513
  		vdev->bridge = bridge;
5d6abf379   Manohar Vanga   staging: vme: mak...
1514
1515
  		vdev->dev.platform_data = drv;
  		vdev->dev.release = vme_dev_release;
8f966dc44   Manohar Vanga   staging: vme: add...
1516
1517
  		vdev->dev.parent = bridge->parent;
  		vdev->dev.bus = &vme_bus_type;
a916a391d   Manohar Vanga   staging: vme: get...
1518
1519
  		dev_set_name(&vdev->dev, "%s.%u-%u", drv->name, bridge->num,
  			vdev->num);
a17a75e26   Martyn Welch   Staging: VME Fram...
1520

5d6abf379   Manohar Vanga   staging: vme: mak...
1521
1522
  		err = device_register(&vdev->dev);
  		if (err)
a17a75e26   Martyn Welch   Staging: VME Fram...
1523
  			goto err_reg;
a17a75e26   Martyn Welch   Staging: VME Fram...
1524

5d6abf379   Manohar Vanga   staging: vme: mak...
1525
1526
1527
1528
1529
1530
1531
  		if (vdev->dev.platform_data) {
  			list_add_tail(&vdev->drv_list, &drv->devices);
  			list_add_tail(&vdev->bridge_list, &bridge->devices);
  		} else
  			device_unregister(&vdev->dev);
  	}
  	return 0;
a17a75e26   Martyn Welch   Staging: VME Fram...
1532

a17a75e26   Martyn Welch   Staging: VME Fram...
1533
  err_reg:
def1820d2   Emilio G. Cota   vme: add missing ...
1534
  	put_device(&vdev->dev);
8f966dc44   Manohar Vanga   staging: vme: add...
1535
  	kfree(vdev);
f6c39d4f2   Manohar Vanga   staging: vme: cha...
1536
  err_devalloc:
5d6abf379   Manohar Vanga   staging: vme: mak...
1537
1538
1539
  	list_for_each_entry_safe(vdev, tmp, &drv->devices, drv_list) {
  		list_del(&vdev->drv_list);
  		list_del(&vdev->bridge_list);
8f966dc44   Manohar Vanga   staging: vme: add...
1540
  		device_unregister(&vdev->dev);
a17a75e26   Martyn Welch   Staging: VME Fram...
1541
  	}
5d6abf379   Manohar Vanga   staging: vme: mak...
1542
  	return err;
a17a75e26   Martyn Welch   Staging: VME Fram...
1543
  }
a17a75e26   Martyn Welch   Staging: VME Fram...
1544

5d6abf379   Manohar Vanga   staging: vme: mak...
1545
  static int __vme_register_driver(struct vme_driver *drv, unsigned int ndevs)
a17a75e26   Martyn Welch   Staging: VME Fram...
1546
  {
5d6abf379   Manohar Vanga   staging: vme: mak...
1547
1548
  	struct vme_bridge *bridge;
  	int err = 0;
a17a75e26   Martyn Welch   Staging: VME Fram...
1549

5d6abf379   Manohar Vanga   staging: vme: mak...
1550
1551
1552
1553
1554
1555
1556
  	mutex_lock(&vme_buses_lock);
  	list_for_each_entry(bridge, &vme_bus_list, bus_list) {
  		/*
  		 * This cannot cause trouble as we already have vme_buses_lock
  		 * and if the bridge is removed, it will have to go through
  		 * vme_unregister_bridge() to do it (which calls remove() on
  		 * the bridge which in turn tries to acquire vme_buses_lock and
c26f61129   Manohar Vanga   staging: vme: fix...
1557
  		 * will have to wait).
5d6abf379   Manohar Vanga   staging: vme: mak...
1558
1559
1560
1561
  		 */
  		err = __vme_register_driver_bus(drv, bridge, ndevs);
  		if (err)
  			break;
a17a75e26   Martyn Welch   Staging: VME Fram...
1562
  	}
5d6abf379   Manohar Vanga   staging: vme: mak...
1563
1564
  	mutex_unlock(&vme_buses_lock);
  	return err;
a17a75e26   Martyn Welch   Staging: VME Fram...
1565
  }
a17a75e26   Martyn Welch   Staging: VME Fram...
1566

5d6abf379   Manohar Vanga   staging: vme: mak...
1567
  int vme_register_driver(struct vme_driver *drv, unsigned int ndevs)
a17a75e26   Martyn Welch   Staging: VME Fram...
1568
  {
5d6abf379   Manohar Vanga   staging: vme: mak...
1569
  	int err;
a17a75e26   Martyn Welch   Staging: VME Fram...
1570
1571
  	drv->driver.name = drv->name;
  	drv->driver.bus = &vme_bus_type;
5d6abf379   Manohar Vanga   staging: vme: mak...
1572
1573
1574
1575
1576
  	INIT_LIST_HEAD(&drv->devices);
  
  	err = driver_register(&drv->driver);
  	if (err)
  		return err;
a17a75e26   Martyn Welch   Staging: VME Fram...
1577

5d6abf379   Manohar Vanga   staging: vme: mak...
1578
1579
1580
1581
1582
  	err = __vme_register_driver(drv, ndevs);
  	if (err)
  		driver_unregister(&drv->driver);
  
  	return err;
a17a75e26   Martyn Welch   Staging: VME Fram...
1583
1584
  }
  EXPORT_SYMBOL(vme_register_driver);
ead1f3e30   Martyn Welch   Staging: vme: Fix...
1585
  void vme_unregister_driver(struct vme_driver *drv)
a17a75e26   Martyn Welch   Staging: VME Fram...
1586
  {
5d6abf379   Manohar Vanga   staging: vme: mak...
1587
1588
1589
1590
1591
1592
1593
1594
1595
  	struct vme_dev *dev, *dev_tmp;
  
  	mutex_lock(&vme_buses_lock);
  	list_for_each_entry_safe(dev, dev_tmp, &drv->devices, drv_list) {
  		list_del(&dev->drv_list);
  		list_del(&dev->bridge_list);
  		device_unregister(&dev->dev);
  	}
  	mutex_unlock(&vme_buses_lock);
a17a75e26   Martyn Welch   Staging: VME Fram...
1596
1597
1598
1599
1600
  	driver_unregister(&drv->driver);
  }
  EXPORT_SYMBOL(vme_unregister_driver);
  
  /* - Bus Registration ------------------------------------------------------ */
a17a75e26   Martyn Welch   Staging: VME Fram...
1601
1602
  static int vme_bus_match(struct device *dev, struct device_driver *drv)
  {
5d6abf379   Manohar Vanga   staging: vme: mak...
1603
  	struct vme_driver *vme_drv;
a17a75e26   Martyn Welch   Staging: VME Fram...
1604

5d6abf379   Manohar Vanga   staging: vme: mak...
1605
  	vme_drv = container_of(drv, struct vme_driver, driver);
a17a75e26   Martyn Welch   Staging: VME Fram...
1606

5d6abf379   Manohar Vanga   staging: vme: mak...
1607
1608
  	if (dev->platform_data == vme_drv) {
  		struct vme_dev *vdev = dev_to_vme_dev(dev);
a17a75e26   Martyn Welch   Staging: VME Fram...
1609

5d6abf379   Manohar Vanga   staging: vme: mak...
1610
1611
  		if (vme_drv->match && vme_drv->match(vdev))
  			return 1;
a37b0dad8   Martyn Welch   Staging: vme: Ext...
1612

5d6abf379   Manohar Vanga   staging: vme: mak...
1613
  		dev->platform_data = NULL;
a17a75e26   Martyn Welch   Staging: VME Fram...
1614
  	}
a17a75e26   Martyn Welch   Staging: VME Fram...
1615
1616
1617
1618
1619
  	return 0;
  }
  
  static int vme_bus_probe(struct device *dev)
  {
a17a75e26   Martyn Welch   Staging: VME Fram...
1620
  	int retval = -ENODEV;
5d6abf379   Manohar Vanga   staging: vme: mak...
1621
1622
  	struct vme_driver *driver;
  	struct vme_dev *vdev = dev_to_vme_dev(dev);
a17a75e26   Martyn Welch   Staging: VME Fram...
1623

5d6abf379   Manohar Vanga   staging: vme: mak...
1624
  	driver = dev->platform_data;
a17a75e26   Martyn Welch   Staging: VME Fram...
1625

ead1f3e30   Martyn Welch   Staging: vme: Fix...
1626
  	if (driver->probe != NULL)
8f966dc44   Manohar Vanga   staging: vme: add...
1627
  		retval = driver->probe(vdev);
a17a75e26   Martyn Welch   Staging: VME Fram...
1628
1629
1630
1631
1632
1633
  
  	return retval;
  }
  
  static int vme_bus_remove(struct device *dev)
  {
a17a75e26   Martyn Welch   Staging: VME Fram...
1634
  	int retval = -ENODEV;
5d6abf379   Manohar Vanga   staging: vme: mak...
1635
1636
  	struct vme_driver *driver;
  	struct vme_dev *vdev = dev_to_vme_dev(dev);
a17a75e26   Martyn Welch   Staging: VME Fram...
1637

5d6abf379   Manohar Vanga   staging: vme: mak...
1638
  	driver = dev->platform_data;
a17a75e26   Martyn Welch   Staging: VME Fram...
1639

ead1f3e30   Martyn Welch   Staging: vme: Fix...
1640
  	if (driver->remove != NULL)
8f966dc44   Manohar Vanga   staging: vme: add...
1641
  		retval = driver->remove(vdev);
a17a75e26   Martyn Welch   Staging: VME Fram...
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
  
  	return retval;
  }
  
  struct bus_type vme_bus_type = {
  	.name = "vme",
  	.match = vme_bus_match,
  	.probe = vme_bus_probe,
  	.remove = vme_bus_remove,
  };
  EXPORT_SYMBOL(vme_bus_type);
ead1f3e30   Martyn Welch   Staging: vme: Fix...
1653
  static int __init vme_init(void)
a17a75e26   Martyn Welch   Staging: VME Fram...
1654
1655
1656
  {
  	return bus_register(&vme_bus_type);
  }
ead1f3e30   Martyn Welch   Staging: vme: Fix...
1657
  static void __exit vme_exit(void)
a17a75e26   Martyn Welch   Staging: VME Fram...
1658
1659
1660
  {
  	bus_unregister(&vme_bus_type);
  }
c326cc023   Aaron Sierra   vme: Convert VME ...
1661
  subsys_initcall(vme_init);
a17a75e26   Martyn Welch   Staging: VME Fram...
1662
  module_exit(vme_exit);