xref: /openbmc/linux/drivers/staging/vme_user/vme.c (revision 1bff15cd)
135ba63b8SArnd Bergmann // SPDX-License-Identifier: GPL-2.0-or-later
235ba63b8SArnd Bergmann /*
335ba63b8SArnd Bergmann  * VME Bridge Framework
435ba63b8SArnd Bergmann  *
535ba63b8SArnd Bergmann  * Author: Martyn Welch <martyn.welch@ge.com>
635ba63b8SArnd Bergmann  * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc.
735ba63b8SArnd Bergmann  *
835ba63b8SArnd Bergmann  * Based on work by Tom Armistead and Ajit Prem
935ba63b8SArnd Bergmann  * Copyright 2004 Motorola Inc.
1035ba63b8SArnd Bergmann  */
1135ba63b8SArnd Bergmann 
1235ba63b8SArnd Bergmann #include <linux/init.h>
1335ba63b8SArnd Bergmann #include <linux/export.h>
1435ba63b8SArnd Bergmann #include <linux/mm.h>
1535ba63b8SArnd Bergmann #include <linux/types.h>
1635ba63b8SArnd Bergmann #include <linux/kernel.h>
1735ba63b8SArnd Bergmann #include <linux/errno.h>
1835ba63b8SArnd Bergmann #include <linux/pci.h>
1935ba63b8SArnd Bergmann #include <linux/poll.h>
2035ba63b8SArnd Bergmann #include <linux/highmem.h>
2135ba63b8SArnd Bergmann #include <linux/interrupt.h>
2235ba63b8SArnd Bergmann #include <linux/pagemap.h>
2335ba63b8SArnd Bergmann #include <linux/device.h>
2435ba63b8SArnd Bergmann #include <linux/dma-mapping.h>
2535ba63b8SArnd Bergmann #include <linux/syscalls.h>
2635ba63b8SArnd Bergmann #include <linux/mutex.h>
2735ba63b8SArnd Bergmann #include <linux/spinlock.h>
2835ba63b8SArnd Bergmann #include <linux/slab.h>
2935ba63b8SArnd Bergmann 
3035ba63b8SArnd Bergmann #include "vme.h"
3135ba63b8SArnd Bergmann #include "vme_bridge.h"
3235ba63b8SArnd Bergmann 
3335ba63b8SArnd Bergmann /* Bitmask and list of registered buses both protected by common mutex */
3435ba63b8SArnd Bergmann static unsigned int vme_bus_numbers;
3535ba63b8SArnd Bergmann static LIST_HEAD(vme_bus_list);
3635ba63b8SArnd Bergmann static DEFINE_MUTEX(vme_buses_lock);
3735ba63b8SArnd Bergmann 
3835ba63b8SArnd Bergmann static int __init vme_init(void);
3935ba63b8SArnd Bergmann 
dev_to_vme_dev(struct device * dev)4035ba63b8SArnd Bergmann static struct vme_dev *dev_to_vme_dev(struct device *dev)
4135ba63b8SArnd Bergmann {
4235ba63b8SArnd Bergmann 	return container_of(dev, struct vme_dev, dev);
4335ba63b8SArnd Bergmann }
4435ba63b8SArnd Bergmann 
4535ba63b8SArnd Bergmann /*
4635ba63b8SArnd Bergmann  * Find the bridge that the resource is associated with.
4735ba63b8SArnd Bergmann  */
find_bridge(struct vme_resource * resource)4835ba63b8SArnd Bergmann static struct vme_bridge *find_bridge(struct vme_resource *resource)
4935ba63b8SArnd Bergmann {
5035ba63b8SArnd Bergmann 	/* Get list to search */
5135ba63b8SArnd Bergmann 	switch (resource->type) {
5235ba63b8SArnd Bergmann 	case VME_MASTER:
5335ba63b8SArnd Bergmann 		return list_entry(resource->entry, struct vme_master_resource,
5435ba63b8SArnd Bergmann 			list)->parent;
5535ba63b8SArnd Bergmann 	case VME_SLAVE:
5635ba63b8SArnd Bergmann 		return list_entry(resource->entry, struct vme_slave_resource,
5735ba63b8SArnd Bergmann 			list)->parent;
5835ba63b8SArnd Bergmann 	case VME_DMA:
5935ba63b8SArnd Bergmann 		return list_entry(resource->entry, struct vme_dma_resource,
6035ba63b8SArnd Bergmann 			list)->parent;
6135ba63b8SArnd Bergmann 	case VME_LM:
6235ba63b8SArnd Bergmann 		return list_entry(resource->entry, struct vme_lm_resource,
6335ba63b8SArnd Bergmann 			list)->parent;
6435ba63b8SArnd Bergmann 	default:
6535ba63b8SArnd Bergmann 		printk(KERN_ERR "Unknown resource type\n");
6635ba63b8SArnd Bergmann 		return NULL;
6735ba63b8SArnd Bergmann 	}
6835ba63b8SArnd Bergmann }
6935ba63b8SArnd Bergmann 
7035ba63b8SArnd Bergmann /**
7135ba63b8SArnd Bergmann  * vme_alloc_consistent - Allocate contiguous memory.
7235ba63b8SArnd Bergmann  * @resource: Pointer to VME resource.
7335ba63b8SArnd Bergmann  * @size: Size of allocation required.
7435ba63b8SArnd Bergmann  * @dma: Pointer to variable to store physical address of allocation.
7535ba63b8SArnd Bergmann  *
7635ba63b8SArnd Bergmann  * Allocate a contiguous block of memory for use by the driver. This is used to
7735ba63b8SArnd Bergmann  * create the buffers for the slave windows.
7835ba63b8SArnd Bergmann  *
7935ba63b8SArnd Bergmann  * Return: Virtual address of allocation on success, NULL on failure.
8035ba63b8SArnd Bergmann  */
vme_alloc_consistent(struct vme_resource * resource,size_t size,dma_addr_t * dma)8135ba63b8SArnd Bergmann void *vme_alloc_consistent(struct vme_resource *resource, size_t size,
8235ba63b8SArnd Bergmann 			   dma_addr_t *dma)
8335ba63b8SArnd Bergmann {
8435ba63b8SArnd Bergmann 	struct vme_bridge *bridge;
8535ba63b8SArnd Bergmann 
8635ba63b8SArnd Bergmann 	if (!resource) {
8735ba63b8SArnd Bergmann 		printk(KERN_ERR "No resource\n");
8835ba63b8SArnd Bergmann 		return NULL;
8935ba63b8SArnd Bergmann 	}
9035ba63b8SArnd Bergmann 
9135ba63b8SArnd Bergmann 	bridge = find_bridge(resource);
9235ba63b8SArnd Bergmann 	if (!bridge) {
9335ba63b8SArnd Bergmann 		printk(KERN_ERR "Can't find bridge\n");
9435ba63b8SArnd Bergmann 		return NULL;
9535ba63b8SArnd Bergmann 	}
9635ba63b8SArnd Bergmann 
9735ba63b8SArnd Bergmann 	if (!bridge->parent) {
9835ba63b8SArnd Bergmann 		printk(KERN_ERR "Dev entry NULL for bridge %s\n", bridge->name);
9935ba63b8SArnd Bergmann 		return NULL;
10035ba63b8SArnd Bergmann 	}
10135ba63b8SArnd Bergmann 
10235ba63b8SArnd Bergmann 	if (!bridge->alloc_consistent) {
10335ba63b8SArnd Bergmann 		printk(KERN_ERR "alloc_consistent not supported by bridge %s\n",
10435ba63b8SArnd Bergmann 		       bridge->name);
10535ba63b8SArnd Bergmann 		return NULL;
10635ba63b8SArnd Bergmann 	}
10735ba63b8SArnd Bergmann 
10835ba63b8SArnd Bergmann 	return bridge->alloc_consistent(bridge->parent, size, dma);
10935ba63b8SArnd Bergmann }
11035ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_alloc_consistent);
11135ba63b8SArnd Bergmann 
11235ba63b8SArnd Bergmann /**
11335ba63b8SArnd Bergmann  * vme_free_consistent - Free previously allocated memory.
11435ba63b8SArnd Bergmann  * @resource: Pointer to VME resource.
11535ba63b8SArnd Bergmann  * @size: Size of allocation to free.
11635ba63b8SArnd Bergmann  * @vaddr: Virtual address of allocation.
11735ba63b8SArnd Bergmann  * @dma: Physical address of allocation.
11835ba63b8SArnd Bergmann  *
11935ba63b8SArnd Bergmann  * Free previously allocated block of contiguous memory.
12035ba63b8SArnd Bergmann  */
vme_free_consistent(struct vme_resource * resource,size_t size,void * vaddr,dma_addr_t dma)12135ba63b8SArnd Bergmann void vme_free_consistent(struct vme_resource *resource, size_t size,
12235ba63b8SArnd Bergmann 			 void *vaddr, dma_addr_t dma)
12335ba63b8SArnd Bergmann {
12435ba63b8SArnd Bergmann 	struct vme_bridge *bridge;
12535ba63b8SArnd Bergmann 
12635ba63b8SArnd Bergmann 	if (!resource) {
12735ba63b8SArnd Bergmann 		printk(KERN_ERR "No resource\n");
12835ba63b8SArnd Bergmann 		return;
12935ba63b8SArnd Bergmann 	}
13035ba63b8SArnd Bergmann 
13135ba63b8SArnd Bergmann 	bridge = find_bridge(resource);
13235ba63b8SArnd Bergmann 	if (!bridge) {
13335ba63b8SArnd Bergmann 		printk(KERN_ERR "Can't find bridge\n");
13435ba63b8SArnd Bergmann 		return;
13535ba63b8SArnd Bergmann 	}
13635ba63b8SArnd Bergmann 
13735ba63b8SArnd Bergmann 	if (!bridge->parent) {
13835ba63b8SArnd Bergmann 		printk(KERN_ERR "Dev entry NULL for bridge %s\n", bridge->name);
13935ba63b8SArnd Bergmann 		return;
14035ba63b8SArnd Bergmann 	}
14135ba63b8SArnd Bergmann 
14235ba63b8SArnd Bergmann 	if (!bridge->free_consistent) {
14335ba63b8SArnd Bergmann 		printk(KERN_ERR "free_consistent not supported by bridge %s\n",
14435ba63b8SArnd Bergmann 		       bridge->name);
14535ba63b8SArnd Bergmann 		return;
14635ba63b8SArnd Bergmann 	}
14735ba63b8SArnd Bergmann 
14835ba63b8SArnd Bergmann 	bridge->free_consistent(bridge->parent, size, vaddr, dma);
14935ba63b8SArnd Bergmann }
15035ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_free_consistent);
15135ba63b8SArnd Bergmann 
15235ba63b8SArnd Bergmann /**
15335ba63b8SArnd Bergmann  * vme_get_size - Helper function returning size of a VME window
15435ba63b8SArnd Bergmann  * @resource: Pointer to VME slave or master resource.
15535ba63b8SArnd Bergmann  *
15635ba63b8SArnd Bergmann  * Determine the size of the VME window provided. This is a helper
15735ba63b8SArnd Bergmann  * function, wrappering the call to vme_master_get or vme_slave_get
15835ba63b8SArnd Bergmann  * depending on the type of window resource handed to it.
15935ba63b8SArnd Bergmann  *
16035ba63b8SArnd Bergmann  * Return: Size of the window on success, zero on failure.
16135ba63b8SArnd Bergmann  */
vme_get_size(struct vme_resource * resource)16235ba63b8SArnd Bergmann size_t vme_get_size(struct vme_resource *resource)
16335ba63b8SArnd Bergmann {
16435ba63b8SArnd Bergmann 	int enabled, retval;
16535ba63b8SArnd Bergmann 	unsigned long long base, size;
16635ba63b8SArnd Bergmann 	dma_addr_t buf_base;
16735ba63b8SArnd Bergmann 	u32 aspace, cycle, dwidth;
16835ba63b8SArnd Bergmann 
16935ba63b8SArnd Bergmann 	switch (resource->type) {
17035ba63b8SArnd Bergmann 	case VME_MASTER:
17135ba63b8SArnd Bergmann 		retval = vme_master_get(resource, &enabled, &base, &size,
17235ba63b8SArnd Bergmann 					&aspace, &cycle, &dwidth);
17335ba63b8SArnd Bergmann 		if (retval)
17435ba63b8SArnd Bergmann 			return 0;
17535ba63b8SArnd Bergmann 
17635ba63b8SArnd Bergmann 		return size;
17735ba63b8SArnd Bergmann 	case VME_SLAVE:
17835ba63b8SArnd Bergmann 		retval = vme_slave_get(resource, &enabled, &base, &size,
17935ba63b8SArnd Bergmann 				       &buf_base, &aspace, &cycle);
18035ba63b8SArnd Bergmann 		if (retval)
18135ba63b8SArnd Bergmann 			return 0;
18235ba63b8SArnd Bergmann 
18335ba63b8SArnd Bergmann 		return size;
18435ba63b8SArnd Bergmann 	case VME_DMA:
18535ba63b8SArnd Bergmann 		return 0;
18635ba63b8SArnd Bergmann 	default:
18735ba63b8SArnd Bergmann 		printk(KERN_ERR "Unknown resource type\n");
18835ba63b8SArnd Bergmann 		return 0;
18935ba63b8SArnd Bergmann 	}
19035ba63b8SArnd Bergmann }
19135ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_get_size);
19235ba63b8SArnd Bergmann 
vme_check_window(u32 aspace,unsigned long long vme_base,unsigned long long size)19335ba63b8SArnd Bergmann int vme_check_window(u32 aspace, unsigned long long vme_base,
19435ba63b8SArnd Bergmann 		     unsigned long long size)
19535ba63b8SArnd Bergmann {
19635ba63b8SArnd Bergmann 	int retval = 0;
19735ba63b8SArnd Bergmann 
19835ba63b8SArnd Bergmann 	if (vme_base + size < size)
19935ba63b8SArnd Bergmann 		return -EINVAL;
20035ba63b8SArnd Bergmann 
20135ba63b8SArnd Bergmann 	switch (aspace) {
20235ba63b8SArnd Bergmann 	case VME_A16:
20335ba63b8SArnd Bergmann 		if (vme_base + size > VME_A16_MAX)
20435ba63b8SArnd Bergmann 			retval = -EFAULT;
20535ba63b8SArnd Bergmann 		break;
20635ba63b8SArnd Bergmann 	case VME_A24:
20735ba63b8SArnd Bergmann 		if (vme_base + size > VME_A24_MAX)
20835ba63b8SArnd Bergmann 			retval = -EFAULT;
20935ba63b8SArnd Bergmann 		break;
21035ba63b8SArnd Bergmann 	case VME_A32:
21135ba63b8SArnd Bergmann 		if (vme_base + size > VME_A32_MAX)
21235ba63b8SArnd Bergmann 			retval = -EFAULT;
21335ba63b8SArnd Bergmann 		break;
21435ba63b8SArnd Bergmann 	case VME_A64:
21535ba63b8SArnd Bergmann 		/* The VME_A64_MAX limit is actually U64_MAX + 1 */
21635ba63b8SArnd Bergmann 		break;
21735ba63b8SArnd Bergmann 	case VME_CRCSR:
21835ba63b8SArnd Bergmann 		if (vme_base + size > VME_CRCSR_MAX)
21935ba63b8SArnd Bergmann 			retval = -EFAULT;
22035ba63b8SArnd Bergmann 		break;
22135ba63b8SArnd Bergmann 	case VME_USER1:
22235ba63b8SArnd Bergmann 	case VME_USER2:
22335ba63b8SArnd Bergmann 	case VME_USER3:
22435ba63b8SArnd Bergmann 	case VME_USER4:
22535ba63b8SArnd Bergmann 		/* User Defined */
22635ba63b8SArnd Bergmann 		break;
22735ba63b8SArnd Bergmann 	default:
22835ba63b8SArnd Bergmann 		printk(KERN_ERR "Invalid address space\n");
22935ba63b8SArnd Bergmann 		retval = -EINVAL;
23035ba63b8SArnd Bergmann 		break;
23135ba63b8SArnd Bergmann 	}
23235ba63b8SArnd Bergmann 
23335ba63b8SArnd Bergmann 	return retval;
23435ba63b8SArnd Bergmann }
23535ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_check_window);
23635ba63b8SArnd Bergmann 
vme_get_aspace(int am)23735ba63b8SArnd Bergmann static u32 vme_get_aspace(int am)
23835ba63b8SArnd Bergmann {
23935ba63b8SArnd Bergmann 	switch (am) {
24035ba63b8SArnd Bergmann 	case 0x29:
24135ba63b8SArnd Bergmann 	case 0x2D:
24235ba63b8SArnd Bergmann 		return VME_A16;
24335ba63b8SArnd Bergmann 	case 0x38:
24435ba63b8SArnd Bergmann 	case 0x39:
24535ba63b8SArnd Bergmann 	case 0x3A:
24635ba63b8SArnd Bergmann 	case 0x3B:
24735ba63b8SArnd Bergmann 	case 0x3C:
24835ba63b8SArnd Bergmann 	case 0x3D:
24935ba63b8SArnd Bergmann 	case 0x3E:
25035ba63b8SArnd Bergmann 	case 0x3F:
25135ba63b8SArnd Bergmann 		return VME_A24;
25235ba63b8SArnd Bergmann 	case 0x8:
25335ba63b8SArnd Bergmann 	case 0x9:
25435ba63b8SArnd Bergmann 	case 0xA:
25535ba63b8SArnd Bergmann 	case 0xB:
25635ba63b8SArnd Bergmann 	case 0xC:
25735ba63b8SArnd Bergmann 	case 0xD:
25835ba63b8SArnd Bergmann 	case 0xE:
25935ba63b8SArnd Bergmann 	case 0xF:
26035ba63b8SArnd Bergmann 		return VME_A32;
26135ba63b8SArnd Bergmann 	case 0x0:
26235ba63b8SArnd Bergmann 	case 0x1:
26335ba63b8SArnd Bergmann 	case 0x3:
26435ba63b8SArnd Bergmann 		return VME_A64;
26535ba63b8SArnd Bergmann 	}
26635ba63b8SArnd Bergmann 
26735ba63b8SArnd Bergmann 	return 0;
26835ba63b8SArnd Bergmann }
26935ba63b8SArnd Bergmann 
27035ba63b8SArnd Bergmann /**
27135ba63b8SArnd Bergmann  * vme_slave_request - Request a VME slave window resource.
27235ba63b8SArnd Bergmann  * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
27335ba63b8SArnd Bergmann  * @address: Required VME address space.
27435ba63b8SArnd Bergmann  * @cycle: Required VME data transfer cycle type.
27535ba63b8SArnd Bergmann  *
27635ba63b8SArnd Bergmann  * Request use of a VME window resource capable of being set for the requested
27735ba63b8SArnd Bergmann  * address space and data transfer cycle.
27835ba63b8SArnd Bergmann  *
27935ba63b8SArnd Bergmann  * Return: Pointer to VME resource on success, NULL on failure.
28035ba63b8SArnd Bergmann  */
vme_slave_request(struct vme_dev * vdev,u32 address,u32 cycle)28135ba63b8SArnd Bergmann struct vme_resource *vme_slave_request(struct vme_dev *vdev, u32 address,
28235ba63b8SArnd Bergmann 				       u32 cycle)
28335ba63b8SArnd Bergmann {
28435ba63b8SArnd Bergmann 	struct vme_bridge *bridge;
28535ba63b8SArnd Bergmann 	struct list_head *slave_pos = NULL;
28635ba63b8SArnd Bergmann 	struct vme_slave_resource *allocated_image = NULL;
28735ba63b8SArnd Bergmann 	struct vme_slave_resource *slave_image = NULL;
28835ba63b8SArnd Bergmann 	struct vme_resource *resource = NULL;
28935ba63b8SArnd Bergmann 
29035ba63b8SArnd Bergmann 	bridge = vdev->bridge;
29135ba63b8SArnd Bergmann 	if (!bridge) {
29235ba63b8SArnd Bergmann 		printk(KERN_ERR "Can't find VME bus\n");
29335ba63b8SArnd Bergmann 		goto err_bus;
29435ba63b8SArnd Bergmann 	}
29535ba63b8SArnd Bergmann 
29635ba63b8SArnd Bergmann 	/* Loop through slave resources */
29735ba63b8SArnd Bergmann 	list_for_each(slave_pos, &bridge->slave_resources) {
29835ba63b8SArnd Bergmann 		slave_image = list_entry(slave_pos,
29935ba63b8SArnd Bergmann 					 struct vme_slave_resource, list);
30035ba63b8SArnd Bergmann 
30135ba63b8SArnd Bergmann 		if (!slave_image) {
30235ba63b8SArnd Bergmann 			printk(KERN_ERR "Registered NULL Slave resource\n");
30335ba63b8SArnd Bergmann 			continue;
30435ba63b8SArnd Bergmann 		}
30535ba63b8SArnd Bergmann 
30635ba63b8SArnd Bergmann 		/* Find an unlocked and compatible image */
30735ba63b8SArnd Bergmann 		mutex_lock(&slave_image->mtx);
30835ba63b8SArnd Bergmann 		if (((slave_image->address_attr & address) == address) &&
30935ba63b8SArnd Bergmann 		    ((slave_image->cycle_attr & cycle) == cycle) &&
31035ba63b8SArnd Bergmann 		    (slave_image->locked == 0)) {
31135ba63b8SArnd Bergmann 			slave_image->locked = 1;
31235ba63b8SArnd Bergmann 			mutex_unlock(&slave_image->mtx);
31335ba63b8SArnd Bergmann 			allocated_image = slave_image;
31435ba63b8SArnd Bergmann 			break;
31535ba63b8SArnd Bergmann 		}
31635ba63b8SArnd Bergmann 		mutex_unlock(&slave_image->mtx);
31735ba63b8SArnd Bergmann 	}
31835ba63b8SArnd Bergmann 
31935ba63b8SArnd Bergmann 	/* No free image */
32035ba63b8SArnd Bergmann 	if (!allocated_image)
32135ba63b8SArnd Bergmann 		goto err_image;
32235ba63b8SArnd Bergmann 
32335ba63b8SArnd Bergmann 	resource = kmalloc(sizeof(*resource), GFP_KERNEL);
32435ba63b8SArnd Bergmann 	if (!resource)
32535ba63b8SArnd Bergmann 		goto err_alloc;
32635ba63b8SArnd Bergmann 
32735ba63b8SArnd Bergmann 	resource->type = VME_SLAVE;
32835ba63b8SArnd Bergmann 	resource->entry = &allocated_image->list;
32935ba63b8SArnd Bergmann 
33035ba63b8SArnd Bergmann 	return resource;
33135ba63b8SArnd Bergmann 
33235ba63b8SArnd Bergmann err_alloc:
33335ba63b8SArnd Bergmann 	/* Unlock image */
33435ba63b8SArnd Bergmann 	mutex_lock(&slave_image->mtx);
33535ba63b8SArnd Bergmann 	slave_image->locked = 0;
33635ba63b8SArnd Bergmann 	mutex_unlock(&slave_image->mtx);
33735ba63b8SArnd Bergmann err_image:
33835ba63b8SArnd Bergmann err_bus:
33935ba63b8SArnd Bergmann 	return NULL;
34035ba63b8SArnd Bergmann }
34135ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_slave_request);
34235ba63b8SArnd Bergmann 
34335ba63b8SArnd Bergmann /**
34435ba63b8SArnd Bergmann  * vme_slave_set - Set VME slave window configuration.
34535ba63b8SArnd Bergmann  * @resource: Pointer to VME slave resource.
34635ba63b8SArnd Bergmann  * @enabled: State to which the window should be configured.
34735ba63b8SArnd Bergmann  * @vme_base: Base address for the window.
34835ba63b8SArnd Bergmann  * @size: Size of the VME window.
34935ba63b8SArnd Bergmann  * @buf_base: Based address of buffer used to provide VME slave window storage.
35035ba63b8SArnd Bergmann  * @aspace: VME address space for the VME window.
35135ba63b8SArnd Bergmann  * @cycle: VME data transfer cycle type for the VME window.
35235ba63b8SArnd Bergmann  *
35335ba63b8SArnd Bergmann  * Set configuration for provided VME slave window.
35435ba63b8SArnd Bergmann  *
35535ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL if operation is not supported on this
35635ba63b8SArnd Bergmann  *         device, if an invalid resource has been provided or invalid
35735ba63b8SArnd Bergmann  *         attributes are provided. Hardware specific errors may also be
35835ba63b8SArnd Bergmann  *         returned.
35935ba63b8SArnd Bergmann  */
vme_slave_set(struct vme_resource * resource,int enabled,unsigned long long vme_base,unsigned long long size,dma_addr_t buf_base,u32 aspace,u32 cycle)36035ba63b8SArnd Bergmann int vme_slave_set(struct vme_resource *resource, int enabled,
36135ba63b8SArnd Bergmann 		  unsigned long long vme_base, unsigned long long size,
36235ba63b8SArnd Bergmann 		  dma_addr_t buf_base, u32 aspace, u32 cycle)
36335ba63b8SArnd Bergmann {
36435ba63b8SArnd Bergmann 	struct vme_bridge *bridge = find_bridge(resource);
36535ba63b8SArnd Bergmann 	struct vme_slave_resource *image;
36635ba63b8SArnd Bergmann 	int retval;
36735ba63b8SArnd Bergmann 
36835ba63b8SArnd Bergmann 	if (resource->type != VME_SLAVE) {
36935ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a slave resource\n");
37035ba63b8SArnd Bergmann 		return -EINVAL;
37135ba63b8SArnd Bergmann 	}
37235ba63b8SArnd Bergmann 
37335ba63b8SArnd Bergmann 	image = list_entry(resource->entry, struct vme_slave_resource, list);
37435ba63b8SArnd Bergmann 
37535ba63b8SArnd Bergmann 	if (!bridge->slave_set) {
37635ba63b8SArnd Bergmann 		printk(KERN_ERR "Function not supported\n");
37735ba63b8SArnd Bergmann 		return -ENOSYS;
37835ba63b8SArnd Bergmann 	}
37935ba63b8SArnd Bergmann 
38035ba63b8SArnd Bergmann 	if (!(((image->address_attr & aspace) == aspace) &&
38135ba63b8SArnd Bergmann 	      ((image->cycle_attr & cycle) == cycle))) {
38235ba63b8SArnd Bergmann 		printk(KERN_ERR "Invalid attributes\n");
38335ba63b8SArnd Bergmann 		return -EINVAL;
38435ba63b8SArnd Bergmann 	}
38535ba63b8SArnd Bergmann 
38635ba63b8SArnd Bergmann 	retval = vme_check_window(aspace, vme_base, size);
38735ba63b8SArnd Bergmann 	if (retval)
38835ba63b8SArnd Bergmann 		return retval;
38935ba63b8SArnd Bergmann 
39035ba63b8SArnd Bergmann 	return bridge->slave_set(image, enabled, vme_base, size, buf_base,
39135ba63b8SArnd Bergmann 		aspace, cycle);
39235ba63b8SArnd Bergmann }
39335ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_slave_set);
39435ba63b8SArnd Bergmann 
39535ba63b8SArnd Bergmann /**
39635ba63b8SArnd Bergmann  * vme_slave_get - Retrieve VME slave window configuration.
39735ba63b8SArnd Bergmann  * @resource: Pointer to VME slave resource.
39835ba63b8SArnd Bergmann  * @enabled: Pointer to variable for storing state.
39935ba63b8SArnd Bergmann  * @vme_base: Pointer to variable for storing window base address.
40035ba63b8SArnd Bergmann  * @size: Pointer to variable for storing window size.
40135ba63b8SArnd Bergmann  * @buf_base: Pointer to variable for storing slave buffer base address.
40235ba63b8SArnd Bergmann  * @aspace: Pointer to variable for storing VME address space.
40335ba63b8SArnd Bergmann  * @cycle: Pointer to variable for storing VME data transfer cycle type.
40435ba63b8SArnd Bergmann  *
40535ba63b8SArnd Bergmann  * Return configuration for provided VME slave window.
40635ba63b8SArnd Bergmann  *
40735ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL if operation is not supported on this
40835ba63b8SArnd Bergmann  *         device or if an invalid resource has been provided.
40935ba63b8SArnd Bergmann  */
vme_slave_get(struct vme_resource * resource,int * enabled,unsigned long long * vme_base,unsigned long long * size,dma_addr_t * buf_base,u32 * aspace,u32 * cycle)41035ba63b8SArnd Bergmann int vme_slave_get(struct vme_resource *resource, int *enabled,
41135ba63b8SArnd Bergmann 		  unsigned long long *vme_base, unsigned long long *size,
41235ba63b8SArnd Bergmann 		  dma_addr_t *buf_base, u32 *aspace, u32 *cycle)
41335ba63b8SArnd Bergmann {
41435ba63b8SArnd Bergmann 	struct vme_bridge *bridge = find_bridge(resource);
41535ba63b8SArnd Bergmann 	struct vme_slave_resource *image;
41635ba63b8SArnd Bergmann 
41735ba63b8SArnd Bergmann 	if (resource->type != VME_SLAVE) {
41835ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a slave resource\n");
41935ba63b8SArnd Bergmann 		return -EINVAL;
42035ba63b8SArnd Bergmann 	}
42135ba63b8SArnd Bergmann 
42235ba63b8SArnd Bergmann 	image = list_entry(resource->entry, struct vme_slave_resource, list);
42335ba63b8SArnd Bergmann 
42435ba63b8SArnd Bergmann 	if (!bridge->slave_get) {
42535ba63b8SArnd Bergmann 		printk(KERN_ERR "vme_slave_get not supported\n");
42635ba63b8SArnd Bergmann 		return -EINVAL;
42735ba63b8SArnd Bergmann 	}
42835ba63b8SArnd Bergmann 
42935ba63b8SArnd Bergmann 	return bridge->slave_get(image, enabled, vme_base, size, buf_base,
43035ba63b8SArnd Bergmann 		aspace, cycle);
43135ba63b8SArnd Bergmann }
43235ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_slave_get);
43335ba63b8SArnd Bergmann 
43435ba63b8SArnd Bergmann /**
43535ba63b8SArnd Bergmann  * vme_slave_free - Free VME slave window
43635ba63b8SArnd Bergmann  * @resource: Pointer to VME slave resource.
43735ba63b8SArnd Bergmann  *
43835ba63b8SArnd Bergmann  * Free the provided slave resource so that it may be reallocated.
43935ba63b8SArnd Bergmann  */
vme_slave_free(struct vme_resource * resource)44035ba63b8SArnd Bergmann void vme_slave_free(struct vme_resource *resource)
44135ba63b8SArnd Bergmann {
44235ba63b8SArnd Bergmann 	struct vme_slave_resource *slave_image;
44335ba63b8SArnd Bergmann 
44435ba63b8SArnd Bergmann 	if (resource->type != VME_SLAVE) {
44535ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a slave resource\n");
44635ba63b8SArnd Bergmann 		return;
44735ba63b8SArnd Bergmann 	}
44835ba63b8SArnd Bergmann 
44935ba63b8SArnd Bergmann 	slave_image = list_entry(resource->entry, struct vme_slave_resource,
45035ba63b8SArnd Bergmann 				 list);
45135ba63b8SArnd Bergmann 	if (!slave_image) {
45235ba63b8SArnd Bergmann 		printk(KERN_ERR "Can't find slave resource\n");
45335ba63b8SArnd Bergmann 		return;
45435ba63b8SArnd Bergmann 	}
45535ba63b8SArnd Bergmann 
45635ba63b8SArnd Bergmann 	/* Unlock image */
45735ba63b8SArnd Bergmann 	mutex_lock(&slave_image->mtx);
45835ba63b8SArnd Bergmann 	if (slave_image->locked == 0)
45935ba63b8SArnd Bergmann 		printk(KERN_ERR "Image is already free\n");
46035ba63b8SArnd Bergmann 
46135ba63b8SArnd Bergmann 	slave_image->locked = 0;
46235ba63b8SArnd Bergmann 	mutex_unlock(&slave_image->mtx);
46335ba63b8SArnd Bergmann 
46435ba63b8SArnd Bergmann 	/* Free up resource memory */
46535ba63b8SArnd Bergmann 	kfree(resource);
46635ba63b8SArnd Bergmann }
46735ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_slave_free);
46835ba63b8SArnd Bergmann 
46935ba63b8SArnd Bergmann /**
47035ba63b8SArnd Bergmann  * vme_master_request - Request a VME master window resource.
47135ba63b8SArnd Bergmann  * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
47235ba63b8SArnd Bergmann  * @address: Required VME address space.
47335ba63b8SArnd Bergmann  * @cycle: Required VME data transfer cycle type.
47435ba63b8SArnd Bergmann  * @dwidth: Required VME data transfer width.
47535ba63b8SArnd Bergmann  *
47635ba63b8SArnd Bergmann  * Request use of a VME window resource capable of being set for the requested
47735ba63b8SArnd Bergmann  * address space, data transfer cycle and width.
47835ba63b8SArnd Bergmann  *
47935ba63b8SArnd Bergmann  * Return: Pointer to VME resource on success, NULL on failure.
48035ba63b8SArnd Bergmann  */
vme_master_request(struct vme_dev * vdev,u32 address,u32 cycle,u32 dwidth)48135ba63b8SArnd Bergmann struct vme_resource *vme_master_request(struct vme_dev *vdev, u32 address,
48235ba63b8SArnd Bergmann 					u32 cycle, u32 dwidth)
48335ba63b8SArnd Bergmann {
48435ba63b8SArnd Bergmann 	struct vme_bridge *bridge;
48535ba63b8SArnd Bergmann 	struct list_head *master_pos = NULL;
48635ba63b8SArnd Bergmann 	struct vme_master_resource *allocated_image = NULL;
48735ba63b8SArnd Bergmann 	struct vme_master_resource *master_image = NULL;
48835ba63b8SArnd Bergmann 	struct vme_resource *resource = NULL;
48935ba63b8SArnd Bergmann 
49035ba63b8SArnd Bergmann 	bridge = vdev->bridge;
49135ba63b8SArnd Bergmann 	if (!bridge) {
49235ba63b8SArnd Bergmann 		printk(KERN_ERR "Can't find VME bus\n");
49335ba63b8SArnd Bergmann 		goto err_bus;
49435ba63b8SArnd Bergmann 	}
49535ba63b8SArnd Bergmann 
49635ba63b8SArnd Bergmann 	/* Loop through master resources */
49735ba63b8SArnd Bergmann 	list_for_each(master_pos, &bridge->master_resources) {
49835ba63b8SArnd Bergmann 		master_image = list_entry(master_pos,
49935ba63b8SArnd Bergmann 					  struct vme_master_resource, list);
50035ba63b8SArnd Bergmann 
50135ba63b8SArnd Bergmann 		if (!master_image) {
50235ba63b8SArnd Bergmann 			printk(KERN_WARNING "Registered NULL master resource\n");
50335ba63b8SArnd Bergmann 			continue;
50435ba63b8SArnd Bergmann 		}
50535ba63b8SArnd Bergmann 
50635ba63b8SArnd Bergmann 		/* Find an unlocked and compatible image */
50735ba63b8SArnd Bergmann 		spin_lock(&master_image->lock);
50835ba63b8SArnd Bergmann 		if (((master_image->address_attr & address) == address) &&
50935ba63b8SArnd Bergmann 		    ((master_image->cycle_attr & cycle) == cycle) &&
51035ba63b8SArnd Bergmann 		    ((master_image->width_attr & dwidth) == dwidth) &&
51135ba63b8SArnd Bergmann 		    (master_image->locked == 0)) {
51235ba63b8SArnd Bergmann 			master_image->locked = 1;
51335ba63b8SArnd Bergmann 			spin_unlock(&master_image->lock);
51435ba63b8SArnd Bergmann 			allocated_image = master_image;
51535ba63b8SArnd Bergmann 			break;
51635ba63b8SArnd Bergmann 		}
51735ba63b8SArnd Bergmann 		spin_unlock(&master_image->lock);
51835ba63b8SArnd Bergmann 	}
51935ba63b8SArnd Bergmann 
52035ba63b8SArnd Bergmann 	/* Check to see if we found a resource */
52135ba63b8SArnd Bergmann 	if (!allocated_image) {
52235ba63b8SArnd Bergmann 		printk(KERN_ERR "Can't find a suitable resource\n");
52335ba63b8SArnd Bergmann 		goto err_image;
52435ba63b8SArnd Bergmann 	}
52535ba63b8SArnd Bergmann 
52635ba63b8SArnd Bergmann 	resource = kmalloc(sizeof(*resource), GFP_KERNEL);
52735ba63b8SArnd Bergmann 	if (!resource)
52835ba63b8SArnd Bergmann 		goto err_alloc;
52935ba63b8SArnd Bergmann 
53035ba63b8SArnd Bergmann 	resource->type = VME_MASTER;
53135ba63b8SArnd Bergmann 	resource->entry = &allocated_image->list;
53235ba63b8SArnd Bergmann 
53335ba63b8SArnd Bergmann 	return resource;
53435ba63b8SArnd Bergmann 
53535ba63b8SArnd Bergmann err_alloc:
53635ba63b8SArnd Bergmann 	/* Unlock image */
53735ba63b8SArnd Bergmann 	spin_lock(&master_image->lock);
53835ba63b8SArnd Bergmann 	master_image->locked = 0;
53935ba63b8SArnd Bergmann 	spin_unlock(&master_image->lock);
54035ba63b8SArnd Bergmann err_image:
54135ba63b8SArnd Bergmann err_bus:
54235ba63b8SArnd Bergmann 	return NULL;
54335ba63b8SArnd Bergmann }
54435ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_master_request);
54535ba63b8SArnd Bergmann 
54635ba63b8SArnd Bergmann /**
54735ba63b8SArnd Bergmann  * vme_master_set - Set VME master window configuration.
54835ba63b8SArnd Bergmann  * @resource: Pointer to VME master resource.
54935ba63b8SArnd Bergmann  * @enabled: State to which the window should be configured.
55035ba63b8SArnd Bergmann  * @vme_base: Base address for the window.
55135ba63b8SArnd Bergmann  * @size: Size of the VME window.
55235ba63b8SArnd Bergmann  * @aspace: VME address space for the VME window.
55335ba63b8SArnd Bergmann  * @cycle: VME data transfer cycle type for the VME window.
55435ba63b8SArnd Bergmann  * @dwidth: VME data transfer width for the VME window.
55535ba63b8SArnd Bergmann  *
55635ba63b8SArnd Bergmann  * Set configuration for provided VME master window.
55735ba63b8SArnd Bergmann  *
55835ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL if operation is not supported on this
55935ba63b8SArnd Bergmann  *         device, if an invalid resource has been provided or invalid
56035ba63b8SArnd Bergmann  *         attributes are provided. Hardware specific errors may also be
56135ba63b8SArnd Bergmann  *         returned.
56235ba63b8SArnd Bergmann  */
vme_master_set(struct vme_resource * resource,int enabled,unsigned long long vme_base,unsigned long long size,u32 aspace,u32 cycle,u32 dwidth)56335ba63b8SArnd Bergmann int vme_master_set(struct vme_resource *resource, int enabled,
5646a889dc7SAlexon Oliveira 		   unsigned long long vme_base, unsigned long long size,
5656a889dc7SAlexon Oliveira 		   u32 aspace, u32 cycle, u32 dwidth)
56635ba63b8SArnd Bergmann {
56735ba63b8SArnd Bergmann 	struct vme_bridge *bridge = find_bridge(resource);
56835ba63b8SArnd Bergmann 	struct vme_master_resource *image;
56935ba63b8SArnd Bergmann 	int retval;
57035ba63b8SArnd Bergmann 
57135ba63b8SArnd Bergmann 	if (resource->type != VME_MASTER) {
57235ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a master resource\n");
57335ba63b8SArnd Bergmann 		return -EINVAL;
57435ba63b8SArnd Bergmann 	}
57535ba63b8SArnd Bergmann 
57635ba63b8SArnd Bergmann 	image = list_entry(resource->entry, struct vme_master_resource, list);
57735ba63b8SArnd Bergmann 
57835ba63b8SArnd Bergmann 	if (!bridge->master_set) {
57935ba63b8SArnd Bergmann 		printk(KERN_WARNING "vme_master_set not supported\n");
58035ba63b8SArnd Bergmann 		return -EINVAL;
58135ba63b8SArnd Bergmann 	}
58235ba63b8SArnd Bergmann 
58335ba63b8SArnd Bergmann 	if (!(((image->address_attr & aspace) == aspace) &&
58435ba63b8SArnd Bergmann 	      ((image->cycle_attr & cycle) == cycle) &&
58535ba63b8SArnd Bergmann 	      ((image->width_attr & dwidth) == dwidth))) {
58635ba63b8SArnd Bergmann 		printk(KERN_WARNING "Invalid attributes\n");
58735ba63b8SArnd Bergmann 		return -EINVAL;
58835ba63b8SArnd Bergmann 	}
58935ba63b8SArnd Bergmann 
59035ba63b8SArnd Bergmann 	retval = vme_check_window(aspace, vme_base, size);
59135ba63b8SArnd Bergmann 	if (retval)
59235ba63b8SArnd Bergmann 		return retval;
59335ba63b8SArnd Bergmann 
59435ba63b8SArnd Bergmann 	return bridge->master_set(image, enabled, vme_base, size, aspace,
59535ba63b8SArnd Bergmann 		cycle, dwidth);
59635ba63b8SArnd Bergmann }
59735ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_master_set);
59835ba63b8SArnd Bergmann 
59935ba63b8SArnd Bergmann /**
60035ba63b8SArnd Bergmann  * vme_master_get - Retrieve VME master window configuration.
60135ba63b8SArnd Bergmann  * @resource: Pointer to VME master resource.
60235ba63b8SArnd Bergmann  * @enabled: Pointer to variable for storing state.
60335ba63b8SArnd Bergmann  * @vme_base: Pointer to variable for storing window base address.
60435ba63b8SArnd Bergmann  * @size: Pointer to variable for storing window size.
60535ba63b8SArnd Bergmann  * @aspace: Pointer to variable for storing VME address space.
60635ba63b8SArnd Bergmann  * @cycle: Pointer to variable for storing VME data transfer cycle type.
60735ba63b8SArnd Bergmann  * @dwidth: Pointer to variable for storing VME data transfer width.
60835ba63b8SArnd Bergmann  *
60935ba63b8SArnd Bergmann  * Return configuration for provided VME master window.
61035ba63b8SArnd Bergmann  *
61135ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL if operation is not supported on this
61235ba63b8SArnd Bergmann  *         device or if an invalid resource has been provided.
61335ba63b8SArnd Bergmann  */
vme_master_get(struct vme_resource * resource,int * enabled,unsigned long long * vme_base,unsigned long long * size,u32 * aspace,u32 * cycle,u32 * dwidth)61435ba63b8SArnd Bergmann int vme_master_get(struct vme_resource *resource, int *enabled,
6156a889dc7SAlexon Oliveira 		   unsigned long long *vme_base, unsigned long long *size,
6166a889dc7SAlexon Oliveira 		   u32 *aspace, u32 *cycle, u32 *dwidth)
61735ba63b8SArnd Bergmann {
61835ba63b8SArnd Bergmann 	struct vme_bridge *bridge = find_bridge(resource);
61935ba63b8SArnd Bergmann 	struct vme_master_resource *image;
62035ba63b8SArnd Bergmann 
62135ba63b8SArnd Bergmann 	if (resource->type != VME_MASTER) {
62235ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a master resource\n");
62335ba63b8SArnd Bergmann 		return -EINVAL;
62435ba63b8SArnd Bergmann 	}
62535ba63b8SArnd Bergmann 
62635ba63b8SArnd Bergmann 	image = list_entry(resource->entry, struct vme_master_resource, list);
62735ba63b8SArnd Bergmann 
62835ba63b8SArnd Bergmann 	if (!bridge->master_get) {
62935ba63b8SArnd Bergmann 		printk(KERN_WARNING "%s not supported\n", __func__);
63035ba63b8SArnd Bergmann 		return -EINVAL;
63135ba63b8SArnd Bergmann 	}
63235ba63b8SArnd Bergmann 
63335ba63b8SArnd Bergmann 	return bridge->master_get(image, enabled, vme_base, size, aspace,
63435ba63b8SArnd Bergmann 		cycle, dwidth);
63535ba63b8SArnd Bergmann }
63635ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_master_get);
63735ba63b8SArnd Bergmann 
63835ba63b8SArnd Bergmann /**
63935ba63b8SArnd Bergmann  * vme_master_read - Read data from VME space into a buffer.
64035ba63b8SArnd Bergmann  * @resource: Pointer to VME master resource.
64135ba63b8SArnd Bergmann  * @buf: Pointer to buffer where data should be transferred.
64235ba63b8SArnd Bergmann  * @count: Number of bytes to transfer.
64335ba63b8SArnd Bergmann  * @offset: Offset into VME master window at which to start transfer.
64435ba63b8SArnd Bergmann  *
64535ba63b8SArnd Bergmann  * Perform read of count bytes of data from location on VME bus which maps into
64635ba63b8SArnd Bergmann  * the VME master window at offset to buf.
64735ba63b8SArnd Bergmann  *
64835ba63b8SArnd Bergmann  * Return: Number of bytes read, -EINVAL if resource is not a VME master
64935ba63b8SArnd Bergmann  *         resource or read operation is not supported. -EFAULT returned if
65035ba63b8SArnd Bergmann  *         invalid offset is provided. Hardware specific errors may also be
65135ba63b8SArnd Bergmann  *         returned.
65235ba63b8SArnd Bergmann  */
vme_master_read(struct vme_resource * resource,void * buf,size_t count,loff_t offset)65335ba63b8SArnd Bergmann ssize_t vme_master_read(struct vme_resource *resource, void *buf, size_t count,
65435ba63b8SArnd Bergmann 			loff_t offset)
65535ba63b8SArnd Bergmann {
65635ba63b8SArnd Bergmann 	struct vme_bridge *bridge = find_bridge(resource);
65735ba63b8SArnd Bergmann 	struct vme_master_resource *image;
65835ba63b8SArnd Bergmann 	size_t length;
65935ba63b8SArnd Bergmann 
66035ba63b8SArnd Bergmann 	if (!bridge->master_read) {
66135ba63b8SArnd Bergmann 		printk(KERN_WARNING "Reading from resource not supported\n");
66235ba63b8SArnd Bergmann 		return -EINVAL;
66335ba63b8SArnd Bergmann 	}
66435ba63b8SArnd Bergmann 
66535ba63b8SArnd Bergmann 	if (resource->type != VME_MASTER) {
66635ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a master resource\n");
66735ba63b8SArnd Bergmann 		return -EINVAL;
66835ba63b8SArnd Bergmann 	}
66935ba63b8SArnd Bergmann 
67035ba63b8SArnd Bergmann 	image = list_entry(resource->entry, struct vme_master_resource, list);
67135ba63b8SArnd Bergmann 
67235ba63b8SArnd Bergmann 	length = vme_get_size(resource);
67335ba63b8SArnd Bergmann 
67435ba63b8SArnd Bergmann 	if (offset > length) {
67535ba63b8SArnd Bergmann 		printk(KERN_WARNING "Invalid Offset\n");
67635ba63b8SArnd Bergmann 		return -EFAULT;
67735ba63b8SArnd Bergmann 	}
67835ba63b8SArnd Bergmann 
67935ba63b8SArnd Bergmann 	if ((offset + count) > length)
68035ba63b8SArnd Bergmann 		count = length - offset;
68135ba63b8SArnd Bergmann 
68235ba63b8SArnd Bergmann 	return bridge->master_read(image, buf, count, offset);
68335ba63b8SArnd Bergmann }
68435ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_master_read);
68535ba63b8SArnd Bergmann 
68635ba63b8SArnd Bergmann /**
68735ba63b8SArnd Bergmann  * vme_master_write - Write data out to VME space from a buffer.
68835ba63b8SArnd Bergmann  * @resource: Pointer to VME master resource.
68935ba63b8SArnd Bergmann  * @buf: Pointer to buffer holding data to transfer.
69035ba63b8SArnd Bergmann  * @count: Number of bytes to transfer.
69135ba63b8SArnd Bergmann  * @offset: Offset into VME master window at which to start transfer.
69235ba63b8SArnd Bergmann  *
69335ba63b8SArnd Bergmann  * Perform write of count bytes of data from buf to location on VME bus which
69435ba63b8SArnd Bergmann  * maps into the VME master window at offset.
69535ba63b8SArnd Bergmann  *
69635ba63b8SArnd Bergmann  * Return: Number of bytes written, -EINVAL if resource is not a VME master
69735ba63b8SArnd Bergmann  *         resource or write operation is not supported. -EFAULT returned if
69835ba63b8SArnd Bergmann  *         invalid offset is provided. Hardware specific errors may also be
69935ba63b8SArnd Bergmann  *         returned.
70035ba63b8SArnd Bergmann  */
vme_master_write(struct vme_resource * resource,void * buf,size_t count,loff_t offset)70135ba63b8SArnd Bergmann ssize_t vme_master_write(struct vme_resource *resource, void *buf,
70235ba63b8SArnd Bergmann 			 size_t count, loff_t offset)
70335ba63b8SArnd Bergmann {
70435ba63b8SArnd Bergmann 	struct vme_bridge *bridge = find_bridge(resource);
70535ba63b8SArnd Bergmann 	struct vme_master_resource *image;
70635ba63b8SArnd Bergmann 	size_t length;
70735ba63b8SArnd Bergmann 
70835ba63b8SArnd Bergmann 	if (!bridge->master_write) {
70935ba63b8SArnd Bergmann 		printk(KERN_WARNING "Writing to resource not supported\n");
71035ba63b8SArnd Bergmann 		return -EINVAL;
71135ba63b8SArnd Bergmann 	}
71235ba63b8SArnd Bergmann 
71335ba63b8SArnd Bergmann 	if (resource->type != VME_MASTER) {
71435ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a master resource\n");
71535ba63b8SArnd Bergmann 		return -EINVAL;
71635ba63b8SArnd Bergmann 	}
71735ba63b8SArnd Bergmann 
71835ba63b8SArnd Bergmann 	image = list_entry(resource->entry, struct vme_master_resource, list);
71935ba63b8SArnd Bergmann 
72035ba63b8SArnd Bergmann 	length = vme_get_size(resource);
72135ba63b8SArnd Bergmann 
72235ba63b8SArnd Bergmann 	if (offset > length) {
72335ba63b8SArnd Bergmann 		printk(KERN_WARNING "Invalid Offset\n");
72435ba63b8SArnd Bergmann 		return -EFAULT;
72535ba63b8SArnd Bergmann 	}
72635ba63b8SArnd Bergmann 
72735ba63b8SArnd Bergmann 	if ((offset + count) > length)
72835ba63b8SArnd Bergmann 		count = length - offset;
72935ba63b8SArnd Bergmann 
73035ba63b8SArnd Bergmann 	return bridge->master_write(image, buf, count, offset);
73135ba63b8SArnd Bergmann }
73235ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_master_write);
73335ba63b8SArnd Bergmann 
73435ba63b8SArnd Bergmann /**
73535ba63b8SArnd Bergmann  * vme_master_rmw - Perform read-modify-write cycle.
73635ba63b8SArnd Bergmann  * @resource: Pointer to VME master resource.
73735ba63b8SArnd Bergmann  * @mask: Bits to be compared and swapped in operation.
73835ba63b8SArnd Bergmann  * @compare: Bits to be compared with data read from offset.
73935ba63b8SArnd Bergmann  * @swap: Bits to be swapped in data read from offset.
74035ba63b8SArnd Bergmann  * @offset: Offset into VME master window at which to perform operation.
74135ba63b8SArnd Bergmann  *
74235ba63b8SArnd Bergmann  * Perform read-modify-write cycle on provided location:
74335ba63b8SArnd Bergmann  * - Location on VME bus is read.
74435ba63b8SArnd Bergmann  * - Bits selected by mask are compared with compare.
74535ba63b8SArnd Bergmann  * - Where a selected bit matches that in compare and are selected in swap,
74635ba63b8SArnd Bergmann  * the bit is swapped.
74735ba63b8SArnd Bergmann  * - Result written back to location on VME bus.
74835ba63b8SArnd Bergmann  *
74935ba63b8SArnd Bergmann  * Return: Bytes written on success, -EINVAL if resource is not a VME master
75035ba63b8SArnd Bergmann  *         resource or RMW operation is not supported. Hardware specific
75135ba63b8SArnd Bergmann  *         errors may also be returned.
75235ba63b8SArnd Bergmann  */
vme_master_rmw(struct vme_resource * resource,unsigned int mask,unsigned int compare,unsigned int swap,loff_t offset)75335ba63b8SArnd Bergmann unsigned int vme_master_rmw(struct vme_resource *resource, unsigned int mask,
75435ba63b8SArnd Bergmann 			    unsigned int compare, unsigned int swap, loff_t offset)
75535ba63b8SArnd Bergmann {
75635ba63b8SArnd Bergmann 	struct vme_bridge *bridge = find_bridge(resource);
75735ba63b8SArnd Bergmann 	struct vme_master_resource *image;
75835ba63b8SArnd Bergmann 
75935ba63b8SArnd Bergmann 	if (!bridge->master_rmw) {
76035ba63b8SArnd Bergmann 		printk(KERN_WARNING "Writing to resource not supported\n");
76135ba63b8SArnd Bergmann 		return -EINVAL;
76235ba63b8SArnd Bergmann 	}
76335ba63b8SArnd Bergmann 
76435ba63b8SArnd Bergmann 	if (resource->type != VME_MASTER) {
76535ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a master resource\n");
76635ba63b8SArnd Bergmann 		return -EINVAL;
76735ba63b8SArnd Bergmann 	}
76835ba63b8SArnd Bergmann 
76935ba63b8SArnd Bergmann 	image = list_entry(resource->entry, struct vme_master_resource, list);
77035ba63b8SArnd Bergmann 
77135ba63b8SArnd Bergmann 	return bridge->master_rmw(image, mask, compare, swap, offset);
77235ba63b8SArnd Bergmann }
77335ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_master_rmw);
77435ba63b8SArnd Bergmann 
77535ba63b8SArnd Bergmann /**
77635ba63b8SArnd Bergmann  * vme_master_mmap - Mmap region of VME master window.
77735ba63b8SArnd Bergmann  * @resource: Pointer to VME master resource.
77835ba63b8SArnd Bergmann  * @vma: Pointer to definition of user mapping.
77935ba63b8SArnd Bergmann  *
78035ba63b8SArnd Bergmann  * Memory map a region of the VME master window into user space.
78135ba63b8SArnd Bergmann  *
78235ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL if resource is not a VME master
78335ba63b8SArnd Bergmann  *         resource or -EFAULT if map exceeds window size. Other generic mmap
78435ba63b8SArnd Bergmann  *         errors may also be returned.
78535ba63b8SArnd Bergmann  */
vme_master_mmap(struct vme_resource * resource,struct vm_area_struct * vma)78635ba63b8SArnd Bergmann int vme_master_mmap(struct vme_resource *resource, struct vm_area_struct *vma)
78735ba63b8SArnd Bergmann {
78835ba63b8SArnd Bergmann 	struct vme_master_resource *image;
78935ba63b8SArnd Bergmann 	phys_addr_t phys_addr;
79035ba63b8SArnd Bergmann 	unsigned long vma_size;
79135ba63b8SArnd Bergmann 
79235ba63b8SArnd Bergmann 	if (resource->type != VME_MASTER) {
79335ba63b8SArnd Bergmann 		pr_err("Not a master resource\n");
79435ba63b8SArnd Bergmann 		return -EINVAL;
79535ba63b8SArnd Bergmann 	}
79635ba63b8SArnd Bergmann 
79735ba63b8SArnd Bergmann 	image = list_entry(resource->entry, struct vme_master_resource, list);
79835ba63b8SArnd Bergmann 	phys_addr = image->bus_resource.start + (vma->vm_pgoff << PAGE_SHIFT);
79935ba63b8SArnd Bergmann 	vma_size = vma->vm_end - vma->vm_start;
80035ba63b8SArnd Bergmann 
80135ba63b8SArnd Bergmann 	if (phys_addr + vma_size > image->bus_resource.end + 1) {
80235ba63b8SArnd Bergmann 		pr_err("Map size cannot exceed the window size\n");
80335ba63b8SArnd Bergmann 		return -EFAULT;
80435ba63b8SArnd Bergmann 	}
80535ba63b8SArnd Bergmann 
80635ba63b8SArnd Bergmann 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
80735ba63b8SArnd Bergmann 
80835ba63b8SArnd Bergmann 	return vm_iomap_memory(vma, phys_addr, vma->vm_end - vma->vm_start);
80935ba63b8SArnd Bergmann }
81035ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_master_mmap);
81135ba63b8SArnd Bergmann 
81235ba63b8SArnd Bergmann /**
81335ba63b8SArnd Bergmann  * vme_master_free - Free VME master window
81435ba63b8SArnd Bergmann  * @resource: Pointer to VME master resource.
81535ba63b8SArnd Bergmann  *
81635ba63b8SArnd Bergmann  * Free the provided master resource so that it may be reallocated.
81735ba63b8SArnd Bergmann  */
vme_master_free(struct vme_resource * resource)81835ba63b8SArnd Bergmann void vme_master_free(struct vme_resource *resource)
81935ba63b8SArnd Bergmann {
82035ba63b8SArnd Bergmann 	struct vme_master_resource *master_image;
82135ba63b8SArnd Bergmann 
82235ba63b8SArnd Bergmann 	if (resource->type != VME_MASTER) {
82335ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a master resource\n");
82435ba63b8SArnd Bergmann 		return;
82535ba63b8SArnd Bergmann 	}
82635ba63b8SArnd Bergmann 
82735ba63b8SArnd Bergmann 	master_image = list_entry(resource->entry, struct vme_master_resource,
82835ba63b8SArnd Bergmann 				  list);
82935ba63b8SArnd Bergmann 	if (!master_image) {
83035ba63b8SArnd Bergmann 		printk(KERN_ERR "Can't find master resource\n");
83135ba63b8SArnd Bergmann 		return;
83235ba63b8SArnd Bergmann 	}
83335ba63b8SArnd Bergmann 
83435ba63b8SArnd Bergmann 	/* Unlock image */
83535ba63b8SArnd Bergmann 	spin_lock(&master_image->lock);
83635ba63b8SArnd Bergmann 	if (master_image->locked == 0)
83735ba63b8SArnd Bergmann 		printk(KERN_ERR "Image is already free\n");
83835ba63b8SArnd Bergmann 
83935ba63b8SArnd Bergmann 	master_image->locked = 0;
84035ba63b8SArnd Bergmann 	spin_unlock(&master_image->lock);
84135ba63b8SArnd Bergmann 
84235ba63b8SArnd Bergmann 	/* Free up resource memory */
84335ba63b8SArnd Bergmann 	kfree(resource);
84435ba63b8SArnd Bergmann }
84535ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_master_free);
84635ba63b8SArnd Bergmann 
84735ba63b8SArnd Bergmann /**
84835ba63b8SArnd Bergmann  * vme_dma_request - Request a DMA controller.
84935ba63b8SArnd Bergmann  * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
85035ba63b8SArnd Bergmann  * @route: Required src/destination combination.
85135ba63b8SArnd Bergmann  *
85235ba63b8SArnd Bergmann  * Request a VME DMA controller with capability to perform transfers bewteen
85335ba63b8SArnd Bergmann  * requested source/destination combination.
85435ba63b8SArnd Bergmann  *
85535ba63b8SArnd Bergmann  * Return: Pointer to VME DMA resource on success, NULL on failure.
85635ba63b8SArnd Bergmann  */
vme_dma_request(struct vme_dev * vdev,u32 route)85735ba63b8SArnd Bergmann struct vme_resource *vme_dma_request(struct vme_dev *vdev, u32 route)
85835ba63b8SArnd Bergmann {
85935ba63b8SArnd Bergmann 	struct vme_bridge *bridge;
86035ba63b8SArnd Bergmann 	struct list_head *dma_pos = NULL;
86135ba63b8SArnd Bergmann 	struct vme_dma_resource *allocated_ctrlr = NULL;
86235ba63b8SArnd Bergmann 	struct vme_dma_resource *dma_ctrlr = NULL;
86335ba63b8SArnd Bergmann 	struct vme_resource *resource = NULL;
86435ba63b8SArnd Bergmann 
86535ba63b8SArnd Bergmann 	/* XXX Not checking resource attributes */
86635ba63b8SArnd Bergmann 	printk(KERN_ERR "No VME resource Attribute tests done\n");
86735ba63b8SArnd Bergmann 
86835ba63b8SArnd Bergmann 	bridge = vdev->bridge;
86935ba63b8SArnd Bergmann 	if (!bridge) {
87035ba63b8SArnd Bergmann 		printk(KERN_ERR "Can't find VME bus\n");
87135ba63b8SArnd Bergmann 		goto err_bus;
87235ba63b8SArnd Bergmann 	}
87335ba63b8SArnd Bergmann 
87435ba63b8SArnd Bergmann 	/* Loop through DMA resources */
87535ba63b8SArnd Bergmann 	list_for_each(dma_pos, &bridge->dma_resources) {
87635ba63b8SArnd Bergmann 		dma_ctrlr = list_entry(dma_pos,
87735ba63b8SArnd Bergmann 				       struct vme_dma_resource, list);
87835ba63b8SArnd Bergmann 		if (!dma_ctrlr) {
87935ba63b8SArnd Bergmann 			printk(KERN_ERR "Registered NULL DMA resource\n");
88035ba63b8SArnd Bergmann 			continue;
88135ba63b8SArnd Bergmann 		}
88235ba63b8SArnd Bergmann 
88335ba63b8SArnd Bergmann 		/* Find an unlocked and compatible controller */
88435ba63b8SArnd Bergmann 		mutex_lock(&dma_ctrlr->mtx);
88535ba63b8SArnd Bergmann 		if (((dma_ctrlr->route_attr & route) == route) &&
88635ba63b8SArnd Bergmann 		    (dma_ctrlr->locked == 0)) {
88735ba63b8SArnd Bergmann 			dma_ctrlr->locked = 1;
88835ba63b8SArnd Bergmann 			mutex_unlock(&dma_ctrlr->mtx);
88935ba63b8SArnd Bergmann 			allocated_ctrlr = dma_ctrlr;
89035ba63b8SArnd Bergmann 			break;
89135ba63b8SArnd Bergmann 		}
89235ba63b8SArnd Bergmann 		mutex_unlock(&dma_ctrlr->mtx);
89335ba63b8SArnd Bergmann 	}
89435ba63b8SArnd Bergmann 
89535ba63b8SArnd Bergmann 	/* Check to see if we found a resource */
89635ba63b8SArnd Bergmann 	if (!allocated_ctrlr)
89735ba63b8SArnd Bergmann 		goto err_ctrlr;
89835ba63b8SArnd Bergmann 
89935ba63b8SArnd Bergmann 	resource = kmalloc(sizeof(*resource), GFP_KERNEL);
90035ba63b8SArnd Bergmann 	if (!resource)
90135ba63b8SArnd Bergmann 		goto err_alloc;
90235ba63b8SArnd Bergmann 
90335ba63b8SArnd Bergmann 	resource->type = VME_DMA;
90435ba63b8SArnd Bergmann 	resource->entry = &allocated_ctrlr->list;
90535ba63b8SArnd Bergmann 
90635ba63b8SArnd Bergmann 	return resource;
90735ba63b8SArnd Bergmann 
90835ba63b8SArnd Bergmann err_alloc:
90935ba63b8SArnd Bergmann 	/* Unlock image */
91035ba63b8SArnd Bergmann 	mutex_lock(&dma_ctrlr->mtx);
91135ba63b8SArnd Bergmann 	dma_ctrlr->locked = 0;
91235ba63b8SArnd Bergmann 	mutex_unlock(&dma_ctrlr->mtx);
91335ba63b8SArnd Bergmann err_ctrlr:
91435ba63b8SArnd Bergmann err_bus:
91535ba63b8SArnd Bergmann 	return NULL;
91635ba63b8SArnd Bergmann }
91735ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_dma_request);
91835ba63b8SArnd Bergmann 
91935ba63b8SArnd Bergmann /**
92035ba63b8SArnd Bergmann  * vme_new_dma_list - Create new VME DMA list.
92135ba63b8SArnd Bergmann  * @resource: Pointer to VME DMA resource.
92235ba63b8SArnd Bergmann  *
92335ba63b8SArnd Bergmann  * Create a new VME DMA list. It is the responsibility of the user to free
92435ba63b8SArnd Bergmann  * the list once it is no longer required with vme_dma_list_free().
92535ba63b8SArnd Bergmann  *
92635ba63b8SArnd Bergmann  * Return: Pointer to new VME DMA list, NULL on allocation failure or invalid
92735ba63b8SArnd Bergmann  *         VME DMA resource.
92835ba63b8SArnd Bergmann  */
vme_new_dma_list(struct vme_resource * resource)92935ba63b8SArnd Bergmann struct vme_dma_list *vme_new_dma_list(struct vme_resource *resource)
93035ba63b8SArnd Bergmann {
93135ba63b8SArnd Bergmann 	struct vme_dma_list *dma_list;
93235ba63b8SArnd Bergmann 
93335ba63b8SArnd Bergmann 	if (resource->type != VME_DMA) {
93435ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a DMA resource\n");
93535ba63b8SArnd Bergmann 		return NULL;
93635ba63b8SArnd Bergmann 	}
93735ba63b8SArnd Bergmann 
93835ba63b8SArnd Bergmann 	dma_list = kmalloc(sizeof(*dma_list), GFP_KERNEL);
93935ba63b8SArnd Bergmann 	if (!dma_list)
94035ba63b8SArnd Bergmann 		return NULL;
94135ba63b8SArnd Bergmann 
94235ba63b8SArnd Bergmann 	INIT_LIST_HEAD(&dma_list->entries);
94335ba63b8SArnd Bergmann 	dma_list->parent = list_entry(resource->entry,
94435ba63b8SArnd Bergmann 				      struct vme_dma_resource,
94535ba63b8SArnd Bergmann 				      list);
94635ba63b8SArnd Bergmann 	mutex_init(&dma_list->mtx);
94735ba63b8SArnd Bergmann 
94835ba63b8SArnd Bergmann 	return dma_list;
94935ba63b8SArnd Bergmann }
95035ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_new_dma_list);
95135ba63b8SArnd Bergmann 
95235ba63b8SArnd Bergmann /**
95335ba63b8SArnd Bergmann  * vme_dma_pattern_attribute - Create "Pattern" type VME DMA list attribute.
95435ba63b8SArnd Bergmann  * @pattern: Value to use used as pattern
95535ba63b8SArnd Bergmann  * @type: Type of pattern to be written.
95635ba63b8SArnd Bergmann  *
95735ba63b8SArnd Bergmann  * Create VME DMA list attribute for pattern generation. It is the
95835ba63b8SArnd Bergmann  * responsibility of the user to free used attributes using
95935ba63b8SArnd Bergmann  * vme_dma_free_attribute().
96035ba63b8SArnd Bergmann  *
96135ba63b8SArnd Bergmann  * Return: Pointer to VME DMA attribute, NULL on failure.
96235ba63b8SArnd Bergmann  */
vme_dma_pattern_attribute(u32 pattern,u32 type)96335ba63b8SArnd Bergmann struct vme_dma_attr *vme_dma_pattern_attribute(u32 pattern, u32 type)
96435ba63b8SArnd Bergmann {
96535ba63b8SArnd Bergmann 	struct vme_dma_attr *attributes;
96635ba63b8SArnd Bergmann 	struct vme_dma_pattern *pattern_attr;
96735ba63b8SArnd Bergmann 
96835ba63b8SArnd Bergmann 	attributes = kmalloc(sizeof(*attributes), GFP_KERNEL);
96935ba63b8SArnd Bergmann 	if (!attributes)
97035ba63b8SArnd Bergmann 		goto err_attr;
97135ba63b8SArnd Bergmann 
97235ba63b8SArnd Bergmann 	pattern_attr = kmalloc(sizeof(*pattern_attr), GFP_KERNEL);
97335ba63b8SArnd Bergmann 	if (!pattern_attr)
97435ba63b8SArnd Bergmann 		goto err_pat;
97535ba63b8SArnd Bergmann 
97635ba63b8SArnd Bergmann 	attributes->type = VME_DMA_PATTERN;
97735ba63b8SArnd Bergmann 	attributes->private = (void *)pattern_attr;
97835ba63b8SArnd Bergmann 
97935ba63b8SArnd Bergmann 	pattern_attr->pattern = pattern;
98035ba63b8SArnd Bergmann 	pattern_attr->type = type;
98135ba63b8SArnd Bergmann 
98235ba63b8SArnd Bergmann 	return attributes;
98335ba63b8SArnd Bergmann 
98435ba63b8SArnd Bergmann err_pat:
98535ba63b8SArnd Bergmann 	kfree(attributes);
98635ba63b8SArnd Bergmann err_attr:
98735ba63b8SArnd Bergmann 	return NULL;
98835ba63b8SArnd Bergmann }
98935ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_dma_pattern_attribute);
99035ba63b8SArnd Bergmann 
99135ba63b8SArnd Bergmann /**
99235ba63b8SArnd Bergmann  * vme_dma_pci_attribute - Create "PCI" type VME DMA list attribute.
99335ba63b8SArnd Bergmann  * @address: PCI base address for DMA transfer.
99435ba63b8SArnd Bergmann  *
99535ba63b8SArnd Bergmann  * Create VME DMA list attribute pointing to a location on PCI for DMA
99635ba63b8SArnd Bergmann  * transfers. It is the responsibility of the user to free used attributes
99735ba63b8SArnd Bergmann  * using vme_dma_free_attribute().
99835ba63b8SArnd Bergmann  *
99935ba63b8SArnd Bergmann  * Return: Pointer to VME DMA attribute, NULL on failure.
100035ba63b8SArnd Bergmann  */
vme_dma_pci_attribute(dma_addr_t address)100135ba63b8SArnd Bergmann struct vme_dma_attr *vme_dma_pci_attribute(dma_addr_t address)
100235ba63b8SArnd Bergmann {
100335ba63b8SArnd Bergmann 	struct vme_dma_attr *attributes;
100435ba63b8SArnd Bergmann 	struct vme_dma_pci *pci_attr;
100535ba63b8SArnd Bergmann 
100635ba63b8SArnd Bergmann 	/* XXX Run some sanity checks here */
100735ba63b8SArnd Bergmann 
100835ba63b8SArnd Bergmann 	attributes = kmalloc(sizeof(*attributes), GFP_KERNEL);
100935ba63b8SArnd Bergmann 	if (!attributes)
101035ba63b8SArnd Bergmann 		goto err_attr;
101135ba63b8SArnd Bergmann 
101235ba63b8SArnd Bergmann 	pci_attr = kmalloc(sizeof(*pci_attr), GFP_KERNEL);
101335ba63b8SArnd Bergmann 	if (!pci_attr)
101435ba63b8SArnd Bergmann 		goto err_pci;
101535ba63b8SArnd Bergmann 
101635ba63b8SArnd Bergmann 	attributes->type = VME_DMA_PCI;
101735ba63b8SArnd Bergmann 	attributes->private = (void *)pci_attr;
101835ba63b8SArnd Bergmann 
101935ba63b8SArnd Bergmann 	pci_attr->address = address;
102035ba63b8SArnd Bergmann 
102135ba63b8SArnd Bergmann 	return attributes;
102235ba63b8SArnd Bergmann 
102335ba63b8SArnd Bergmann err_pci:
102435ba63b8SArnd Bergmann 	kfree(attributes);
102535ba63b8SArnd Bergmann err_attr:
102635ba63b8SArnd Bergmann 	return NULL;
102735ba63b8SArnd Bergmann }
102835ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_dma_pci_attribute);
102935ba63b8SArnd Bergmann 
103035ba63b8SArnd Bergmann /**
103135ba63b8SArnd Bergmann  * vme_dma_vme_attribute - Create "VME" type VME DMA list attribute.
103235ba63b8SArnd Bergmann  * @address: VME base address for DMA transfer.
103335ba63b8SArnd Bergmann  * @aspace: VME address space to use for DMA transfer.
103435ba63b8SArnd Bergmann  * @cycle: VME bus cycle to use for DMA transfer.
103535ba63b8SArnd Bergmann  * @dwidth: VME data width to use for DMA transfer.
103635ba63b8SArnd Bergmann  *
103735ba63b8SArnd Bergmann  * Create VME DMA list attribute pointing to a location on the VME bus for DMA
103835ba63b8SArnd Bergmann  * transfers. It is the responsibility of the user to free used attributes
103935ba63b8SArnd Bergmann  * using vme_dma_free_attribute().
104035ba63b8SArnd Bergmann  *
104135ba63b8SArnd Bergmann  * Return: Pointer to VME DMA attribute, NULL on failure.
104235ba63b8SArnd Bergmann  */
vme_dma_vme_attribute(unsigned long long address,u32 aspace,u32 cycle,u32 dwidth)104335ba63b8SArnd Bergmann struct vme_dma_attr *vme_dma_vme_attribute(unsigned long long address,
104435ba63b8SArnd Bergmann 					   u32 aspace, u32 cycle, u32 dwidth)
104535ba63b8SArnd Bergmann {
104635ba63b8SArnd Bergmann 	struct vme_dma_attr *attributes;
104735ba63b8SArnd Bergmann 	struct vme_dma_vme *vme_attr;
104835ba63b8SArnd Bergmann 
104935ba63b8SArnd Bergmann 	attributes = kmalloc(sizeof(*attributes), GFP_KERNEL);
105035ba63b8SArnd Bergmann 	if (!attributes)
105135ba63b8SArnd Bergmann 		goto err_attr;
105235ba63b8SArnd Bergmann 
105335ba63b8SArnd Bergmann 	vme_attr = kmalloc(sizeof(*vme_attr), GFP_KERNEL);
105435ba63b8SArnd Bergmann 	if (!vme_attr)
105535ba63b8SArnd Bergmann 		goto err_vme;
105635ba63b8SArnd Bergmann 
105735ba63b8SArnd Bergmann 	attributes->type = VME_DMA_VME;
105835ba63b8SArnd Bergmann 	attributes->private = (void *)vme_attr;
105935ba63b8SArnd Bergmann 
106035ba63b8SArnd Bergmann 	vme_attr->address = address;
106135ba63b8SArnd Bergmann 	vme_attr->aspace = aspace;
106235ba63b8SArnd Bergmann 	vme_attr->cycle = cycle;
106335ba63b8SArnd Bergmann 	vme_attr->dwidth = dwidth;
106435ba63b8SArnd Bergmann 
106535ba63b8SArnd Bergmann 	return attributes;
106635ba63b8SArnd Bergmann 
106735ba63b8SArnd Bergmann err_vme:
106835ba63b8SArnd Bergmann 	kfree(attributes);
106935ba63b8SArnd Bergmann err_attr:
107035ba63b8SArnd Bergmann 	return NULL;
107135ba63b8SArnd Bergmann }
107235ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_dma_vme_attribute);
107335ba63b8SArnd Bergmann 
107435ba63b8SArnd Bergmann /**
107535ba63b8SArnd Bergmann  * vme_dma_free_attribute - Free DMA list attribute.
107635ba63b8SArnd Bergmann  * @attributes: Pointer to DMA list attribute.
107735ba63b8SArnd Bergmann  *
107835ba63b8SArnd Bergmann  * Free VME DMA list attribute. VME DMA list attributes can be safely freed
107935ba63b8SArnd Bergmann  * once vme_dma_list_add() has returned.
108035ba63b8SArnd Bergmann  */
vme_dma_free_attribute(struct vme_dma_attr * attributes)108135ba63b8SArnd Bergmann void vme_dma_free_attribute(struct vme_dma_attr *attributes)
108235ba63b8SArnd Bergmann {
108335ba63b8SArnd Bergmann 	kfree(attributes->private);
108435ba63b8SArnd Bergmann 	kfree(attributes);
108535ba63b8SArnd Bergmann }
108635ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_dma_free_attribute);
108735ba63b8SArnd Bergmann 
108835ba63b8SArnd Bergmann /**
108935ba63b8SArnd Bergmann  * vme_dma_list_add - Add enty to a VME DMA list.
109035ba63b8SArnd Bergmann  * @list: Pointer to VME list.
109135ba63b8SArnd Bergmann  * @src: Pointer to DMA list attribute to use as source.
109235ba63b8SArnd Bergmann  * @dest: Pointer to DMA list attribute to use as destination.
109335ba63b8SArnd Bergmann  * @count: Number of bytes to transfer.
109435ba63b8SArnd Bergmann  *
109535ba63b8SArnd Bergmann  * Add an entry to the provided VME DMA list. Entry requires pointers to source
109635ba63b8SArnd Bergmann  * and destination DMA attributes and a count.
109735ba63b8SArnd Bergmann  *
109835ba63b8SArnd Bergmann  * Please note, the attributes supported as source and destinations for
109935ba63b8SArnd Bergmann  * transfers are hardware dependent.
110035ba63b8SArnd Bergmann  *
110135ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL if operation is not supported on this
110235ba63b8SArnd Bergmann  *         device or if the link list has already been submitted for execution.
110335ba63b8SArnd Bergmann  *         Hardware specific errors also possible.
110435ba63b8SArnd Bergmann  */
vme_dma_list_add(struct vme_dma_list * list,struct vme_dma_attr * src,struct vme_dma_attr * dest,size_t count)110535ba63b8SArnd Bergmann int vme_dma_list_add(struct vme_dma_list *list, struct vme_dma_attr *src,
110635ba63b8SArnd Bergmann 		     struct vme_dma_attr *dest, size_t count)
110735ba63b8SArnd Bergmann {
110835ba63b8SArnd Bergmann 	struct vme_bridge *bridge = list->parent->parent;
110935ba63b8SArnd Bergmann 	int retval;
111035ba63b8SArnd Bergmann 
111135ba63b8SArnd Bergmann 	if (!bridge->dma_list_add) {
111235ba63b8SArnd Bergmann 		printk(KERN_WARNING "Link List DMA generation not supported\n");
111335ba63b8SArnd Bergmann 		return -EINVAL;
111435ba63b8SArnd Bergmann 	}
111535ba63b8SArnd Bergmann 
111635ba63b8SArnd Bergmann 	if (!mutex_trylock(&list->mtx)) {
111735ba63b8SArnd Bergmann 		printk(KERN_ERR "Link List already submitted\n");
111835ba63b8SArnd Bergmann 		return -EINVAL;
111935ba63b8SArnd Bergmann 	}
112035ba63b8SArnd Bergmann 
112135ba63b8SArnd Bergmann 	retval = bridge->dma_list_add(list, src, dest, count);
112235ba63b8SArnd Bergmann 
112335ba63b8SArnd Bergmann 	mutex_unlock(&list->mtx);
112435ba63b8SArnd Bergmann 
112535ba63b8SArnd Bergmann 	return retval;
112635ba63b8SArnd Bergmann }
112735ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_dma_list_add);
112835ba63b8SArnd Bergmann 
112935ba63b8SArnd Bergmann /**
113035ba63b8SArnd Bergmann  * vme_dma_list_exec - Queue a VME DMA list for execution.
113135ba63b8SArnd Bergmann  * @list: Pointer to VME list.
113235ba63b8SArnd Bergmann  *
113335ba63b8SArnd Bergmann  * Queue the provided VME DMA list for execution. The call will return once the
113435ba63b8SArnd Bergmann  * list has been executed.
113535ba63b8SArnd Bergmann  *
113635ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL if operation is not supported on this
113735ba63b8SArnd Bergmann  *         device. Hardware specific errors also possible.
113835ba63b8SArnd Bergmann  */
vme_dma_list_exec(struct vme_dma_list * list)113935ba63b8SArnd Bergmann int vme_dma_list_exec(struct vme_dma_list *list)
114035ba63b8SArnd Bergmann {
114135ba63b8SArnd Bergmann 	struct vme_bridge *bridge = list->parent->parent;
114235ba63b8SArnd Bergmann 	int retval;
114335ba63b8SArnd Bergmann 
114435ba63b8SArnd Bergmann 	if (!bridge->dma_list_exec) {
114535ba63b8SArnd Bergmann 		printk(KERN_ERR "Link List DMA execution not supported\n");
114635ba63b8SArnd Bergmann 		return -EINVAL;
114735ba63b8SArnd Bergmann 	}
114835ba63b8SArnd Bergmann 
114935ba63b8SArnd Bergmann 	mutex_lock(&list->mtx);
115035ba63b8SArnd Bergmann 
115135ba63b8SArnd Bergmann 	retval = bridge->dma_list_exec(list);
115235ba63b8SArnd Bergmann 
115335ba63b8SArnd Bergmann 	mutex_unlock(&list->mtx);
115435ba63b8SArnd Bergmann 
115535ba63b8SArnd Bergmann 	return retval;
115635ba63b8SArnd Bergmann }
115735ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_dma_list_exec);
115835ba63b8SArnd Bergmann 
115935ba63b8SArnd Bergmann /**
116035ba63b8SArnd Bergmann  * vme_dma_list_free - Free a VME DMA list.
116135ba63b8SArnd Bergmann  * @list: Pointer to VME list.
116235ba63b8SArnd Bergmann  *
116335ba63b8SArnd Bergmann  * Free the provided DMA list and all its entries.
116435ba63b8SArnd Bergmann  *
116535ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL on invalid VME resource, -EBUSY if resource
116635ba63b8SArnd Bergmann  *         is still in use. Hardware specific errors also possible.
116735ba63b8SArnd Bergmann  */
vme_dma_list_free(struct vme_dma_list * list)116835ba63b8SArnd Bergmann int vme_dma_list_free(struct vme_dma_list *list)
116935ba63b8SArnd Bergmann {
117035ba63b8SArnd Bergmann 	struct vme_bridge *bridge = list->parent->parent;
117135ba63b8SArnd Bergmann 	int retval;
117235ba63b8SArnd Bergmann 
117335ba63b8SArnd Bergmann 	if (!bridge->dma_list_empty) {
117435ba63b8SArnd Bergmann 		printk(KERN_WARNING "Emptying of Link Lists not supported\n");
117535ba63b8SArnd Bergmann 		return -EINVAL;
117635ba63b8SArnd Bergmann 	}
117735ba63b8SArnd Bergmann 
117835ba63b8SArnd Bergmann 	if (!mutex_trylock(&list->mtx)) {
117935ba63b8SArnd Bergmann 		printk(KERN_ERR "Link List in use\n");
118035ba63b8SArnd Bergmann 		return -EBUSY;
118135ba63b8SArnd Bergmann 	}
118235ba63b8SArnd Bergmann 
118335ba63b8SArnd Bergmann 	/*
118435ba63b8SArnd Bergmann 	 * Empty out all of the entries from the DMA list. We need to go to the
118535ba63b8SArnd Bergmann 	 * low level driver as DMA entries are driver specific.
118635ba63b8SArnd Bergmann 	 */
118735ba63b8SArnd Bergmann 	retval = bridge->dma_list_empty(list);
118835ba63b8SArnd Bergmann 	if (retval) {
118935ba63b8SArnd Bergmann 		printk(KERN_ERR "Unable to empty link-list entries\n");
119035ba63b8SArnd Bergmann 		mutex_unlock(&list->mtx);
119135ba63b8SArnd Bergmann 		return retval;
119235ba63b8SArnd Bergmann 	}
119335ba63b8SArnd Bergmann 	mutex_unlock(&list->mtx);
119435ba63b8SArnd Bergmann 	kfree(list);
119535ba63b8SArnd Bergmann 
119635ba63b8SArnd Bergmann 	return retval;
119735ba63b8SArnd Bergmann }
119835ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_dma_list_free);
119935ba63b8SArnd Bergmann 
120035ba63b8SArnd Bergmann /**
120135ba63b8SArnd Bergmann  * vme_dma_free - Free a VME DMA resource.
120235ba63b8SArnd Bergmann  * @resource: Pointer to VME DMA resource.
120335ba63b8SArnd Bergmann  *
120435ba63b8SArnd Bergmann  * Free the provided DMA resource so that it may be reallocated.
120535ba63b8SArnd Bergmann  *
120635ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL on invalid VME resource, -EBUSY if resource
120735ba63b8SArnd Bergmann  *         is still active.
120835ba63b8SArnd Bergmann  */
vme_dma_free(struct vme_resource * resource)120935ba63b8SArnd Bergmann int vme_dma_free(struct vme_resource *resource)
121035ba63b8SArnd Bergmann {
121135ba63b8SArnd Bergmann 	struct vme_dma_resource *ctrlr;
121235ba63b8SArnd Bergmann 
121335ba63b8SArnd Bergmann 	if (resource->type != VME_DMA) {
121435ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a DMA resource\n");
121535ba63b8SArnd Bergmann 		return -EINVAL;
121635ba63b8SArnd Bergmann 	}
121735ba63b8SArnd Bergmann 
121835ba63b8SArnd Bergmann 	ctrlr = list_entry(resource->entry, struct vme_dma_resource, list);
121935ba63b8SArnd Bergmann 
122035ba63b8SArnd Bergmann 	if (!mutex_trylock(&ctrlr->mtx)) {
122135ba63b8SArnd Bergmann 		printk(KERN_ERR "Resource busy, can't free\n");
122235ba63b8SArnd Bergmann 		return -EBUSY;
122335ba63b8SArnd Bergmann 	}
122435ba63b8SArnd Bergmann 
122535ba63b8SArnd Bergmann 	if (!(list_empty(&ctrlr->pending) && list_empty(&ctrlr->running))) {
122635ba63b8SArnd Bergmann 		printk(KERN_WARNING "Resource still processing transfers\n");
122735ba63b8SArnd Bergmann 		mutex_unlock(&ctrlr->mtx);
122835ba63b8SArnd Bergmann 		return -EBUSY;
122935ba63b8SArnd Bergmann 	}
123035ba63b8SArnd Bergmann 
123135ba63b8SArnd Bergmann 	ctrlr->locked = 0;
123235ba63b8SArnd Bergmann 
123335ba63b8SArnd Bergmann 	mutex_unlock(&ctrlr->mtx);
123435ba63b8SArnd Bergmann 
123535ba63b8SArnd Bergmann 	kfree(resource);
123635ba63b8SArnd Bergmann 
123735ba63b8SArnd Bergmann 	return 0;
123835ba63b8SArnd Bergmann }
123935ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_dma_free);
124035ba63b8SArnd Bergmann 
vme_bus_error_handler(struct vme_bridge * bridge,unsigned long long address,int am)124135ba63b8SArnd Bergmann void vme_bus_error_handler(struct vme_bridge *bridge,
124235ba63b8SArnd Bergmann 			   unsigned long long address, int am)
124335ba63b8SArnd Bergmann {
124435ba63b8SArnd Bergmann 	struct list_head *handler_pos = NULL;
124535ba63b8SArnd Bergmann 	struct vme_error_handler *handler;
124635ba63b8SArnd Bergmann 	int handler_triggered = 0;
124735ba63b8SArnd Bergmann 	u32 aspace = vme_get_aspace(am);
124835ba63b8SArnd Bergmann 
124935ba63b8SArnd Bergmann 	list_for_each(handler_pos, &bridge->vme_error_handlers) {
125035ba63b8SArnd Bergmann 		handler = list_entry(handler_pos, struct vme_error_handler,
125135ba63b8SArnd Bergmann 				     list);
125235ba63b8SArnd Bergmann 		if ((aspace == handler->aspace) &&
125335ba63b8SArnd Bergmann 		    (address >= handler->start) &&
125435ba63b8SArnd Bergmann 		    (address < handler->end)) {
125535ba63b8SArnd Bergmann 			if (!handler->num_errors)
125635ba63b8SArnd Bergmann 				handler->first_error = address;
125735ba63b8SArnd Bergmann 			if (handler->num_errors != UINT_MAX)
125835ba63b8SArnd Bergmann 				handler->num_errors++;
125935ba63b8SArnd Bergmann 			handler_triggered = 1;
126035ba63b8SArnd Bergmann 		}
126135ba63b8SArnd Bergmann 	}
126235ba63b8SArnd Bergmann 
126335ba63b8SArnd Bergmann 	if (!handler_triggered)
126435ba63b8SArnd Bergmann 		dev_err(bridge->parent,
126535ba63b8SArnd Bergmann 			"Unhandled VME access error at address 0x%llx\n",
126635ba63b8SArnd Bergmann 			address);
126735ba63b8SArnd Bergmann }
126835ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_bus_error_handler);
126935ba63b8SArnd Bergmann 
vme_register_error_handler(struct vme_bridge * bridge,u32 aspace,unsigned long long address,size_t len)1270*1bff15cdSAlexon Oliveira struct vme_error_handler *vme_register_error_handler(struct vme_bridge *bridge, u32 aspace,
127135ba63b8SArnd Bergmann 						     unsigned long long address, size_t len)
127235ba63b8SArnd Bergmann {
127335ba63b8SArnd Bergmann 	struct vme_error_handler *handler;
127435ba63b8SArnd Bergmann 
127535ba63b8SArnd Bergmann 	handler = kmalloc(sizeof(*handler), GFP_ATOMIC);
127635ba63b8SArnd Bergmann 	if (!handler)
127735ba63b8SArnd Bergmann 		return NULL;
127835ba63b8SArnd Bergmann 
127935ba63b8SArnd Bergmann 	handler->aspace = aspace;
128035ba63b8SArnd Bergmann 	handler->start = address;
128135ba63b8SArnd Bergmann 	handler->end = address + len;
128235ba63b8SArnd Bergmann 	handler->num_errors = 0;
128335ba63b8SArnd Bergmann 	handler->first_error = 0;
128435ba63b8SArnd Bergmann 	list_add_tail(&handler->list, &bridge->vme_error_handlers);
128535ba63b8SArnd Bergmann 
128635ba63b8SArnd Bergmann 	return handler;
128735ba63b8SArnd Bergmann }
128835ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_register_error_handler);
128935ba63b8SArnd Bergmann 
vme_unregister_error_handler(struct vme_error_handler * handler)129035ba63b8SArnd Bergmann void vme_unregister_error_handler(struct vme_error_handler *handler)
129135ba63b8SArnd Bergmann {
129235ba63b8SArnd Bergmann 	list_del(&handler->list);
129335ba63b8SArnd Bergmann 	kfree(handler);
129435ba63b8SArnd Bergmann }
129535ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_unregister_error_handler);
129635ba63b8SArnd Bergmann 
vme_irq_handler(struct vme_bridge * bridge,int level,int statid)129735ba63b8SArnd Bergmann void vme_irq_handler(struct vme_bridge *bridge, int level, int statid)
129835ba63b8SArnd Bergmann {
129935ba63b8SArnd Bergmann 	void (*call)(int, int, void *);
130035ba63b8SArnd Bergmann 	void *priv_data;
130135ba63b8SArnd Bergmann 
130235ba63b8SArnd Bergmann 	call = bridge->irq[level - 1].callback[statid].func;
130335ba63b8SArnd Bergmann 	priv_data = bridge->irq[level - 1].callback[statid].priv_data;
130435ba63b8SArnd Bergmann 	if (call)
130535ba63b8SArnd Bergmann 		call(level, statid, priv_data);
130635ba63b8SArnd Bergmann 	else
130735ba63b8SArnd Bergmann 		printk(KERN_WARNING "Spurious VME interrupt, level:%x, vector:%x\n",
130835ba63b8SArnd Bergmann 		       level, statid);
130935ba63b8SArnd Bergmann }
131035ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_irq_handler);
131135ba63b8SArnd Bergmann 
131235ba63b8SArnd Bergmann /**
131335ba63b8SArnd Bergmann  * vme_irq_request - Request a specific VME interrupt.
131435ba63b8SArnd Bergmann  * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
131535ba63b8SArnd Bergmann  * @level: Interrupt priority being requested.
131635ba63b8SArnd Bergmann  * @statid: Interrupt vector being requested.
131735ba63b8SArnd Bergmann  * @callback: Pointer to callback function called when VME interrupt/vector
131835ba63b8SArnd Bergmann  *            received.
131935ba63b8SArnd Bergmann  * @priv_data: Generic pointer that will be passed to the callback function.
132035ba63b8SArnd Bergmann  *
132135ba63b8SArnd Bergmann  * Request callback to be attached as a handler for VME interrupts with provided
132235ba63b8SArnd Bergmann  * level and statid.
132335ba63b8SArnd Bergmann  *
132435ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL on invalid vme device, level or if the
132535ba63b8SArnd Bergmann  *         function is not supported, -EBUSY if the level/statid combination is
132635ba63b8SArnd Bergmann  *         already in use. Hardware specific errors also possible.
132735ba63b8SArnd Bergmann  */
vme_irq_request(struct vme_dev * vdev,int level,int statid,void (* callback)(int,int,void *),void * priv_data)132835ba63b8SArnd Bergmann int vme_irq_request(struct vme_dev *vdev, int level, int statid,
132935ba63b8SArnd Bergmann 		    void (*callback)(int, int, void *),
133035ba63b8SArnd Bergmann 		    void *priv_data)
133135ba63b8SArnd Bergmann {
133235ba63b8SArnd Bergmann 	struct vme_bridge *bridge;
133335ba63b8SArnd Bergmann 
133435ba63b8SArnd Bergmann 	bridge = vdev->bridge;
133535ba63b8SArnd Bergmann 	if (!bridge) {
133635ba63b8SArnd Bergmann 		printk(KERN_ERR "Can't find VME bus\n");
133735ba63b8SArnd Bergmann 		return -EINVAL;
133835ba63b8SArnd Bergmann 	}
133935ba63b8SArnd Bergmann 
134035ba63b8SArnd Bergmann 	if ((level < 1) || (level > 7)) {
134135ba63b8SArnd Bergmann 		printk(KERN_ERR "Invalid interrupt level\n");
134235ba63b8SArnd Bergmann 		return -EINVAL;
134335ba63b8SArnd Bergmann 	}
134435ba63b8SArnd Bergmann 
134535ba63b8SArnd Bergmann 	if (!bridge->irq_set) {
134635ba63b8SArnd Bergmann 		printk(KERN_ERR "Configuring interrupts not supported\n");
134735ba63b8SArnd Bergmann 		return -EINVAL;
134835ba63b8SArnd Bergmann 	}
134935ba63b8SArnd Bergmann 
135035ba63b8SArnd Bergmann 	mutex_lock(&bridge->irq_mtx);
135135ba63b8SArnd Bergmann 
135235ba63b8SArnd Bergmann 	if (bridge->irq[level - 1].callback[statid].func) {
135335ba63b8SArnd Bergmann 		mutex_unlock(&bridge->irq_mtx);
135435ba63b8SArnd Bergmann 		printk(KERN_WARNING "VME Interrupt already taken\n");
135535ba63b8SArnd Bergmann 		return -EBUSY;
135635ba63b8SArnd Bergmann 	}
135735ba63b8SArnd Bergmann 
135835ba63b8SArnd Bergmann 	bridge->irq[level - 1].count++;
135935ba63b8SArnd Bergmann 	bridge->irq[level - 1].callback[statid].priv_data = priv_data;
136035ba63b8SArnd Bergmann 	bridge->irq[level - 1].callback[statid].func = callback;
136135ba63b8SArnd Bergmann 
136235ba63b8SArnd Bergmann 	/* Enable IRQ level */
136335ba63b8SArnd Bergmann 	bridge->irq_set(bridge, level, 1, 1);
136435ba63b8SArnd Bergmann 
136535ba63b8SArnd Bergmann 	mutex_unlock(&bridge->irq_mtx);
136635ba63b8SArnd Bergmann 
136735ba63b8SArnd Bergmann 	return 0;
136835ba63b8SArnd Bergmann }
136935ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_irq_request);
137035ba63b8SArnd Bergmann 
137135ba63b8SArnd Bergmann /**
137235ba63b8SArnd Bergmann  * vme_irq_free - Free a VME interrupt.
137335ba63b8SArnd Bergmann  * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
137435ba63b8SArnd Bergmann  * @level: Interrupt priority of interrupt being freed.
137535ba63b8SArnd Bergmann  * @statid: Interrupt vector of interrupt being freed.
137635ba63b8SArnd Bergmann  *
137735ba63b8SArnd Bergmann  * Remove previously attached callback from VME interrupt priority/vector.
137835ba63b8SArnd Bergmann  */
vme_irq_free(struct vme_dev * vdev,int level,int statid)137935ba63b8SArnd Bergmann void vme_irq_free(struct vme_dev *vdev, int level, int statid)
138035ba63b8SArnd Bergmann {
138135ba63b8SArnd Bergmann 	struct vme_bridge *bridge;
138235ba63b8SArnd Bergmann 
138335ba63b8SArnd Bergmann 	bridge = vdev->bridge;
138435ba63b8SArnd Bergmann 	if (!bridge) {
138535ba63b8SArnd Bergmann 		printk(KERN_ERR "Can't find VME bus\n");
138635ba63b8SArnd Bergmann 		return;
138735ba63b8SArnd Bergmann 	}
138835ba63b8SArnd Bergmann 
138935ba63b8SArnd Bergmann 	if ((level < 1) || (level > 7)) {
139035ba63b8SArnd Bergmann 		printk(KERN_ERR "Invalid interrupt level\n");
139135ba63b8SArnd Bergmann 		return;
139235ba63b8SArnd Bergmann 	}
139335ba63b8SArnd Bergmann 
139435ba63b8SArnd Bergmann 	if (!bridge->irq_set) {
139535ba63b8SArnd Bergmann 		printk(KERN_ERR "Configuring interrupts not supported\n");
139635ba63b8SArnd Bergmann 		return;
139735ba63b8SArnd Bergmann 	}
139835ba63b8SArnd Bergmann 
139935ba63b8SArnd Bergmann 	mutex_lock(&bridge->irq_mtx);
140035ba63b8SArnd Bergmann 
140135ba63b8SArnd Bergmann 	bridge->irq[level - 1].count--;
140235ba63b8SArnd Bergmann 
140335ba63b8SArnd Bergmann 	/* Disable IRQ level if no more interrupts attached at this level*/
140435ba63b8SArnd Bergmann 	if (bridge->irq[level - 1].count == 0)
140535ba63b8SArnd Bergmann 		bridge->irq_set(bridge, level, 0, 1);
140635ba63b8SArnd Bergmann 
140735ba63b8SArnd Bergmann 	bridge->irq[level - 1].callback[statid].func = NULL;
140835ba63b8SArnd Bergmann 	bridge->irq[level - 1].callback[statid].priv_data = NULL;
140935ba63b8SArnd Bergmann 
141035ba63b8SArnd Bergmann 	mutex_unlock(&bridge->irq_mtx);
141135ba63b8SArnd Bergmann }
141235ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_irq_free);
141335ba63b8SArnd Bergmann 
141435ba63b8SArnd Bergmann /**
141535ba63b8SArnd Bergmann  * vme_irq_generate - Generate VME interrupt.
141635ba63b8SArnd Bergmann  * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
141735ba63b8SArnd Bergmann  * @level: Interrupt priority at which to assert the interrupt.
141835ba63b8SArnd Bergmann  * @statid: Interrupt vector to associate with the interrupt.
141935ba63b8SArnd Bergmann  *
142035ba63b8SArnd Bergmann  * Generate a VME interrupt of the provided level and with the provided
142135ba63b8SArnd Bergmann  * statid.
142235ba63b8SArnd Bergmann  *
142335ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL on invalid vme device, level or if the
142435ba63b8SArnd Bergmann  *         function is not supported. Hardware specific errors also possible.
142535ba63b8SArnd Bergmann  */
vme_irq_generate(struct vme_dev * vdev,int level,int statid)142635ba63b8SArnd Bergmann int vme_irq_generate(struct vme_dev *vdev, int level, int statid)
142735ba63b8SArnd Bergmann {
142835ba63b8SArnd Bergmann 	struct vme_bridge *bridge;
142935ba63b8SArnd Bergmann 
143035ba63b8SArnd Bergmann 	bridge = vdev->bridge;
143135ba63b8SArnd Bergmann 	if (!bridge) {
143235ba63b8SArnd Bergmann 		printk(KERN_ERR "Can't find VME bus\n");
143335ba63b8SArnd Bergmann 		return -EINVAL;
143435ba63b8SArnd Bergmann 	}
143535ba63b8SArnd Bergmann 
143635ba63b8SArnd Bergmann 	if ((level < 1) || (level > 7)) {
143735ba63b8SArnd Bergmann 		printk(KERN_WARNING "Invalid interrupt level\n");
143835ba63b8SArnd Bergmann 		return -EINVAL;
143935ba63b8SArnd Bergmann 	}
144035ba63b8SArnd Bergmann 
144135ba63b8SArnd Bergmann 	if (!bridge->irq_generate) {
144235ba63b8SArnd Bergmann 		printk(KERN_WARNING "Interrupt generation not supported\n");
144335ba63b8SArnd Bergmann 		return -EINVAL;
144435ba63b8SArnd Bergmann 	}
144535ba63b8SArnd Bergmann 
144635ba63b8SArnd Bergmann 	return bridge->irq_generate(bridge, level, statid);
144735ba63b8SArnd Bergmann }
144835ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_irq_generate);
144935ba63b8SArnd Bergmann 
145035ba63b8SArnd Bergmann /**
145135ba63b8SArnd Bergmann  * vme_lm_request - Request a VME location monitor
145235ba63b8SArnd Bergmann  * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
145335ba63b8SArnd Bergmann  *
145435ba63b8SArnd Bergmann  * Allocate a location monitor resource to the driver. A location monitor
145535ba63b8SArnd Bergmann  * allows the driver to monitor accesses to a contiguous number of
145635ba63b8SArnd Bergmann  * addresses on the VME bus.
145735ba63b8SArnd Bergmann  *
145835ba63b8SArnd Bergmann  * Return: Pointer to a VME resource on success or NULL on failure.
145935ba63b8SArnd Bergmann  */
vme_lm_request(struct vme_dev * vdev)146035ba63b8SArnd Bergmann struct vme_resource *vme_lm_request(struct vme_dev *vdev)
146135ba63b8SArnd Bergmann {
146235ba63b8SArnd Bergmann 	struct vme_bridge *bridge;
146335ba63b8SArnd Bergmann 	struct list_head *lm_pos = NULL;
146435ba63b8SArnd Bergmann 	struct vme_lm_resource *allocated_lm = NULL;
146535ba63b8SArnd Bergmann 	struct vme_lm_resource *lm = NULL;
146635ba63b8SArnd Bergmann 	struct vme_resource *resource = NULL;
146735ba63b8SArnd Bergmann 
146835ba63b8SArnd Bergmann 	bridge = vdev->bridge;
146935ba63b8SArnd Bergmann 	if (!bridge) {
147035ba63b8SArnd Bergmann 		printk(KERN_ERR "Can't find VME bus\n");
147135ba63b8SArnd Bergmann 		goto err_bus;
147235ba63b8SArnd Bergmann 	}
147335ba63b8SArnd Bergmann 
147435ba63b8SArnd Bergmann 	/* Loop through LM resources */
147535ba63b8SArnd Bergmann 	list_for_each(lm_pos, &bridge->lm_resources) {
147635ba63b8SArnd Bergmann 		lm = list_entry(lm_pos,
147735ba63b8SArnd Bergmann 				struct vme_lm_resource, list);
147835ba63b8SArnd Bergmann 		if (!lm) {
147935ba63b8SArnd Bergmann 			printk(KERN_ERR "Registered NULL Location Monitor resource\n");
148035ba63b8SArnd Bergmann 			continue;
148135ba63b8SArnd Bergmann 		}
148235ba63b8SArnd Bergmann 
148335ba63b8SArnd Bergmann 		/* Find an unlocked controller */
148435ba63b8SArnd Bergmann 		mutex_lock(&lm->mtx);
148535ba63b8SArnd Bergmann 		if (lm->locked == 0) {
148635ba63b8SArnd Bergmann 			lm->locked = 1;
148735ba63b8SArnd Bergmann 			mutex_unlock(&lm->mtx);
148835ba63b8SArnd Bergmann 			allocated_lm = lm;
148935ba63b8SArnd Bergmann 			break;
149035ba63b8SArnd Bergmann 		}
149135ba63b8SArnd Bergmann 		mutex_unlock(&lm->mtx);
149235ba63b8SArnd Bergmann 	}
149335ba63b8SArnd Bergmann 
149435ba63b8SArnd Bergmann 	/* Check to see if we found a resource */
149535ba63b8SArnd Bergmann 	if (!allocated_lm)
149635ba63b8SArnd Bergmann 		goto err_lm;
149735ba63b8SArnd Bergmann 
149835ba63b8SArnd Bergmann 	resource = kmalloc(sizeof(*resource), GFP_KERNEL);
149935ba63b8SArnd Bergmann 	if (!resource)
150035ba63b8SArnd Bergmann 		goto err_alloc;
150135ba63b8SArnd Bergmann 
150235ba63b8SArnd Bergmann 	resource->type = VME_LM;
150335ba63b8SArnd Bergmann 	resource->entry = &allocated_lm->list;
150435ba63b8SArnd Bergmann 
150535ba63b8SArnd Bergmann 	return resource;
150635ba63b8SArnd Bergmann 
150735ba63b8SArnd Bergmann err_alloc:
150835ba63b8SArnd Bergmann 	/* Unlock image */
150935ba63b8SArnd Bergmann 	mutex_lock(&lm->mtx);
151035ba63b8SArnd Bergmann 	lm->locked = 0;
151135ba63b8SArnd Bergmann 	mutex_unlock(&lm->mtx);
151235ba63b8SArnd Bergmann err_lm:
151335ba63b8SArnd Bergmann err_bus:
151435ba63b8SArnd Bergmann 	return NULL;
151535ba63b8SArnd Bergmann }
151635ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_lm_request);
151735ba63b8SArnd Bergmann 
151835ba63b8SArnd Bergmann /**
151935ba63b8SArnd Bergmann  * vme_lm_count - Determine number of VME Addresses monitored
152035ba63b8SArnd Bergmann  * @resource: Pointer to VME location monitor resource.
152135ba63b8SArnd Bergmann  *
152235ba63b8SArnd Bergmann  * The number of contiguous addresses monitored is hardware dependent.
152335ba63b8SArnd Bergmann  * Return the number of contiguous addresses monitored by the
152435ba63b8SArnd Bergmann  * location monitor.
152535ba63b8SArnd Bergmann  *
152635ba63b8SArnd Bergmann  * Return: Count of addresses monitored or -EINVAL when provided with an
152735ba63b8SArnd Bergmann  *	   invalid location monitor resource.
152835ba63b8SArnd Bergmann  */
vme_lm_count(struct vme_resource * resource)152935ba63b8SArnd Bergmann int vme_lm_count(struct vme_resource *resource)
153035ba63b8SArnd Bergmann {
153135ba63b8SArnd Bergmann 	struct vme_lm_resource *lm;
153235ba63b8SArnd Bergmann 
153335ba63b8SArnd Bergmann 	if (resource->type != VME_LM) {
153435ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a Location Monitor resource\n");
153535ba63b8SArnd Bergmann 		return -EINVAL;
153635ba63b8SArnd Bergmann 	}
153735ba63b8SArnd Bergmann 
153835ba63b8SArnd Bergmann 	lm = list_entry(resource->entry, struct vme_lm_resource, list);
153935ba63b8SArnd Bergmann 
154035ba63b8SArnd Bergmann 	return lm->monitors;
154135ba63b8SArnd Bergmann }
154235ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_lm_count);
154335ba63b8SArnd Bergmann 
154435ba63b8SArnd Bergmann /**
154535ba63b8SArnd Bergmann  * vme_lm_set - Configure location monitor
154635ba63b8SArnd Bergmann  * @resource: Pointer to VME location monitor resource.
154735ba63b8SArnd Bergmann  * @lm_base: Base address to monitor.
154835ba63b8SArnd Bergmann  * @aspace: VME address space to monitor.
154935ba63b8SArnd Bergmann  * @cycle: VME bus cycle type to monitor.
155035ba63b8SArnd Bergmann  *
155135ba63b8SArnd Bergmann  * Set the base address, address space and cycle type of accesses to be
155235ba63b8SArnd Bergmann  * monitored by the location monitor.
155335ba63b8SArnd Bergmann  *
155435ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL when provided with an invalid location
155535ba63b8SArnd Bergmann  *	   monitor resource or function is not supported. Hardware specific
155635ba63b8SArnd Bergmann  *	   errors may also be returned.
155735ba63b8SArnd Bergmann  */
vme_lm_set(struct vme_resource * resource,unsigned long long lm_base,u32 aspace,u32 cycle)155835ba63b8SArnd Bergmann int vme_lm_set(struct vme_resource *resource, unsigned long long lm_base,
155935ba63b8SArnd Bergmann 	       u32 aspace, u32 cycle)
156035ba63b8SArnd Bergmann {
156135ba63b8SArnd Bergmann 	struct vme_bridge *bridge = find_bridge(resource);
156235ba63b8SArnd Bergmann 	struct vme_lm_resource *lm;
156335ba63b8SArnd Bergmann 
156435ba63b8SArnd Bergmann 	if (resource->type != VME_LM) {
156535ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a Location Monitor resource\n");
156635ba63b8SArnd Bergmann 		return -EINVAL;
156735ba63b8SArnd Bergmann 	}
156835ba63b8SArnd Bergmann 
156935ba63b8SArnd Bergmann 	lm = list_entry(resource->entry, struct vme_lm_resource, list);
157035ba63b8SArnd Bergmann 
157135ba63b8SArnd Bergmann 	if (!bridge->lm_set) {
157235ba63b8SArnd Bergmann 		printk(KERN_ERR "vme_lm_set not supported\n");
157335ba63b8SArnd Bergmann 		return -EINVAL;
157435ba63b8SArnd Bergmann 	}
157535ba63b8SArnd Bergmann 
157635ba63b8SArnd Bergmann 	return bridge->lm_set(lm, lm_base, aspace, cycle);
157735ba63b8SArnd Bergmann }
157835ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_lm_set);
157935ba63b8SArnd Bergmann 
158035ba63b8SArnd Bergmann /**
158135ba63b8SArnd Bergmann  * vme_lm_get - Retrieve location monitor settings
158235ba63b8SArnd Bergmann  * @resource: Pointer to VME location monitor resource.
158335ba63b8SArnd Bergmann  * @lm_base: Pointer used to output the base address monitored.
158435ba63b8SArnd Bergmann  * @aspace: Pointer used to output the address space monitored.
158535ba63b8SArnd Bergmann  * @cycle: Pointer used to output the VME bus cycle type monitored.
158635ba63b8SArnd Bergmann  *
158735ba63b8SArnd Bergmann  * Retrieve the base address, address space and cycle type of accesses to
158835ba63b8SArnd Bergmann  * be monitored by the location monitor.
158935ba63b8SArnd Bergmann  *
159035ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL when provided with an invalid location
159135ba63b8SArnd Bergmann  *	   monitor resource or function is not supported. Hardware specific
159235ba63b8SArnd Bergmann  *	   errors may also be returned.
159335ba63b8SArnd Bergmann  */
vme_lm_get(struct vme_resource * resource,unsigned long long * lm_base,u32 * aspace,u32 * cycle)159435ba63b8SArnd Bergmann int vme_lm_get(struct vme_resource *resource, unsigned long long *lm_base,
159535ba63b8SArnd Bergmann 	       u32 *aspace, u32 *cycle)
159635ba63b8SArnd Bergmann {
159735ba63b8SArnd Bergmann 	struct vme_bridge *bridge = find_bridge(resource);
159835ba63b8SArnd Bergmann 	struct vme_lm_resource *lm;
159935ba63b8SArnd Bergmann 
160035ba63b8SArnd Bergmann 	if (resource->type != VME_LM) {
160135ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a Location Monitor resource\n");
160235ba63b8SArnd Bergmann 		return -EINVAL;
160335ba63b8SArnd Bergmann 	}
160435ba63b8SArnd Bergmann 
160535ba63b8SArnd Bergmann 	lm = list_entry(resource->entry, struct vme_lm_resource, list);
160635ba63b8SArnd Bergmann 
160735ba63b8SArnd Bergmann 	if (!bridge->lm_get) {
160835ba63b8SArnd Bergmann 		printk(KERN_ERR "vme_lm_get not supported\n");
160935ba63b8SArnd Bergmann 		return -EINVAL;
161035ba63b8SArnd Bergmann 	}
161135ba63b8SArnd Bergmann 
161235ba63b8SArnd Bergmann 	return bridge->lm_get(lm, lm_base, aspace, cycle);
161335ba63b8SArnd Bergmann }
161435ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_lm_get);
161535ba63b8SArnd Bergmann 
161635ba63b8SArnd Bergmann /**
161735ba63b8SArnd Bergmann  * vme_lm_attach - Provide callback for location monitor address
161835ba63b8SArnd Bergmann  * @resource: Pointer to VME location monitor resource.
161935ba63b8SArnd Bergmann  * @monitor: Offset to which callback should be attached.
162035ba63b8SArnd Bergmann  * @callback: Pointer to callback function called when triggered.
162135ba63b8SArnd Bergmann  * @data: Generic pointer that will be passed to the callback function.
162235ba63b8SArnd Bergmann  *
162335ba63b8SArnd Bergmann  * Attach a callback to the specificed offset into the location monitors
162435ba63b8SArnd Bergmann  * monitored addresses. A generic pointer is provided to allow data to be
162535ba63b8SArnd Bergmann  * passed to the callback when called.
162635ba63b8SArnd Bergmann  *
162735ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL when provided with an invalid location
162835ba63b8SArnd Bergmann  *	   monitor resource or function is not supported. Hardware specific
162935ba63b8SArnd Bergmann  *	   errors may also be returned.
163035ba63b8SArnd Bergmann  */
vme_lm_attach(struct vme_resource * resource,int monitor,void (* callback)(void *),void * data)163135ba63b8SArnd Bergmann int vme_lm_attach(struct vme_resource *resource, int monitor,
163235ba63b8SArnd Bergmann 		  void (*callback)(void *), void *data)
163335ba63b8SArnd Bergmann {
163435ba63b8SArnd Bergmann 	struct vme_bridge *bridge = find_bridge(resource);
163535ba63b8SArnd Bergmann 	struct vme_lm_resource *lm;
163635ba63b8SArnd Bergmann 
163735ba63b8SArnd Bergmann 	if (resource->type != VME_LM) {
163835ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a Location Monitor resource\n");
163935ba63b8SArnd Bergmann 		return -EINVAL;
164035ba63b8SArnd Bergmann 	}
164135ba63b8SArnd Bergmann 
164235ba63b8SArnd Bergmann 	lm = list_entry(resource->entry, struct vme_lm_resource, list);
164335ba63b8SArnd Bergmann 
164435ba63b8SArnd Bergmann 	if (!bridge->lm_attach) {
164535ba63b8SArnd Bergmann 		printk(KERN_ERR "vme_lm_attach not supported\n");
164635ba63b8SArnd Bergmann 		return -EINVAL;
164735ba63b8SArnd Bergmann 	}
164835ba63b8SArnd Bergmann 
164935ba63b8SArnd Bergmann 	return bridge->lm_attach(lm, monitor, callback, data);
165035ba63b8SArnd Bergmann }
165135ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_lm_attach);
165235ba63b8SArnd Bergmann 
165335ba63b8SArnd Bergmann /**
165435ba63b8SArnd Bergmann  * vme_lm_detach - Remove callback for location monitor address
165535ba63b8SArnd Bergmann  * @resource: Pointer to VME location monitor resource.
165635ba63b8SArnd Bergmann  * @monitor: Offset to which callback should be removed.
165735ba63b8SArnd Bergmann  *
165835ba63b8SArnd Bergmann  * Remove the callback associated with the specificed offset into the
165935ba63b8SArnd Bergmann  * location monitors monitored addresses.
166035ba63b8SArnd Bergmann  *
166135ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL when provided with an invalid location
166235ba63b8SArnd Bergmann  *	   monitor resource or function is not supported. Hardware specific
166335ba63b8SArnd Bergmann  *	   errors may also be returned.
166435ba63b8SArnd Bergmann  */
vme_lm_detach(struct vme_resource * resource,int monitor)166535ba63b8SArnd Bergmann int vme_lm_detach(struct vme_resource *resource, int monitor)
166635ba63b8SArnd Bergmann {
166735ba63b8SArnd Bergmann 	struct vme_bridge *bridge = find_bridge(resource);
166835ba63b8SArnd Bergmann 	struct vme_lm_resource *lm;
166935ba63b8SArnd Bergmann 
167035ba63b8SArnd Bergmann 	if (resource->type != VME_LM) {
167135ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a Location Monitor resource\n");
167235ba63b8SArnd Bergmann 		return -EINVAL;
167335ba63b8SArnd Bergmann 	}
167435ba63b8SArnd Bergmann 
167535ba63b8SArnd Bergmann 	lm = list_entry(resource->entry, struct vme_lm_resource, list);
167635ba63b8SArnd Bergmann 
167735ba63b8SArnd Bergmann 	if (!bridge->lm_detach) {
167835ba63b8SArnd Bergmann 		printk(KERN_ERR "vme_lm_detach not supported\n");
167935ba63b8SArnd Bergmann 		return -EINVAL;
168035ba63b8SArnd Bergmann 	}
168135ba63b8SArnd Bergmann 
168235ba63b8SArnd Bergmann 	return bridge->lm_detach(lm, monitor);
168335ba63b8SArnd Bergmann }
168435ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_lm_detach);
168535ba63b8SArnd Bergmann 
168635ba63b8SArnd Bergmann /**
168735ba63b8SArnd Bergmann  * vme_lm_free - Free allocated VME location monitor
168835ba63b8SArnd Bergmann  * @resource: Pointer to VME location monitor resource.
168935ba63b8SArnd Bergmann  *
169035ba63b8SArnd Bergmann  * Free allocation of a VME location monitor.
169135ba63b8SArnd Bergmann  *
169235ba63b8SArnd Bergmann  * WARNING: This function currently expects that any callbacks that have
169335ba63b8SArnd Bergmann  *          been attached to the location monitor have been removed.
169435ba63b8SArnd Bergmann  *
169535ba63b8SArnd Bergmann  * Return: Zero on success, -EINVAL when provided with an invalid location
169635ba63b8SArnd Bergmann  *	   monitor resource.
169735ba63b8SArnd Bergmann  */
vme_lm_free(struct vme_resource * resource)169835ba63b8SArnd Bergmann void vme_lm_free(struct vme_resource *resource)
169935ba63b8SArnd Bergmann {
170035ba63b8SArnd Bergmann 	struct vme_lm_resource *lm;
170135ba63b8SArnd Bergmann 
170235ba63b8SArnd Bergmann 	if (resource->type != VME_LM) {
170335ba63b8SArnd Bergmann 		printk(KERN_ERR "Not a Location Monitor resource\n");
170435ba63b8SArnd Bergmann 		return;
170535ba63b8SArnd Bergmann 	}
170635ba63b8SArnd Bergmann 
170735ba63b8SArnd Bergmann 	lm = list_entry(resource->entry, struct vme_lm_resource, list);
170835ba63b8SArnd Bergmann 
170935ba63b8SArnd Bergmann 	mutex_lock(&lm->mtx);
171035ba63b8SArnd Bergmann 
171135ba63b8SArnd Bergmann 	/* XXX
171235ba63b8SArnd Bergmann 	 * Check to see that there aren't any callbacks still attached, if
171335ba63b8SArnd Bergmann 	 * there are we should probably be detaching them!
171435ba63b8SArnd Bergmann 	 */
171535ba63b8SArnd Bergmann 
171635ba63b8SArnd Bergmann 	lm->locked = 0;
171735ba63b8SArnd Bergmann 
171835ba63b8SArnd Bergmann 	mutex_unlock(&lm->mtx);
171935ba63b8SArnd Bergmann 
172035ba63b8SArnd Bergmann 	kfree(resource);
172135ba63b8SArnd Bergmann }
172235ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_lm_free);
172335ba63b8SArnd Bergmann 
172435ba63b8SArnd Bergmann /**
172535ba63b8SArnd Bergmann  * vme_slot_num - Retrieve slot ID
172635ba63b8SArnd Bergmann  * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
172735ba63b8SArnd Bergmann  *
172835ba63b8SArnd Bergmann  * Retrieve the slot ID associated with the provided VME device.
172935ba63b8SArnd Bergmann  *
173035ba63b8SArnd Bergmann  * Return: The slot ID on success, -EINVAL if VME bridge cannot be determined
173135ba63b8SArnd Bergmann  *         or the function is not supported. Hardware specific errors may also
173235ba63b8SArnd Bergmann  *         be returned.
173335ba63b8SArnd Bergmann  */
vme_slot_num(struct vme_dev * vdev)173435ba63b8SArnd Bergmann int vme_slot_num(struct vme_dev *vdev)
173535ba63b8SArnd Bergmann {
173635ba63b8SArnd Bergmann 	struct vme_bridge *bridge;
173735ba63b8SArnd Bergmann 
173835ba63b8SArnd Bergmann 	bridge = vdev->bridge;
173935ba63b8SArnd Bergmann 	if (!bridge) {
174035ba63b8SArnd Bergmann 		printk(KERN_ERR "Can't find VME bus\n");
174135ba63b8SArnd Bergmann 		return -EINVAL;
174235ba63b8SArnd Bergmann 	}
174335ba63b8SArnd Bergmann 
174435ba63b8SArnd Bergmann 	if (!bridge->slot_get) {
174535ba63b8SArnd Bergmann 		printk(KERN_WARNING "vme_slot_num not supported\n");
174635ba63b8SArnd Bergmann 		return -EINVAL;
174735ba63b8SArnd Bergmann 	}
174835ba63b8SArnd Bergmann 
174935ba63b8SArnd Bergmann 	return bridge->slot_get(bridge);
175035ba63b8SArnd Bergmann }
175135ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_slot_num);
175235ba63b8SArnd Bergmann 
175335ba63b8SArnd Bergmann /**
175435ba63b8SArnd Bergmann  * vme_bus_num - Retrieve bus number
175535ba63b8SArnd Bergmann  * @vdev: Pointer to VME device struct vme_dev assigned to driver instance.
175635ba63b8SArnd Bergmann  *
175735ba63b8SArnd Bergmann  * Retrieve the bus enumeration associated with the provided VME device.
175835ba63b8SArnd Bergmann  *
175935ba63b8SArnd Bergmann  * Return: The bus number on success, -EINVAL if VME bridge cannot be
176035ba63b8SArnd Bergmann  *         determined.
176135ba63b8SArnd Bergmann  */
vme_bus_num(struct vme_dev * vdev)176235ba63b8SArnd Bergmann int vme_bus_num(struct vme_dev *vdev)
176335ba63b8SArnd Bergmann {
176435ba63b8SArnd Bergmann 	struct vme_bridge *bridge;
176535ba63b8SArnd Bergmann 
176635ba63b8SArnd Bergmann 	bridge = vdev->bridge;
176735ba63b8SArnd Bergmann 	if (!bridge) {
176835ba63b8SArnd Bergmann 		pr_err("Can't find VME bus\n");
176935ba63b8SArnd Bergmann 		return -EINVAL;
177035ba63b8SArnd Bergmann 	}
177135ba63b8SArnd Bergmann 
177235ba63b8SArnd Bergmann 	return bridge->num;
177335ba63b8SArnd Bergmann }
177435ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_bus_num);
177535ba63b8SArnd Bergmann 
177635ba63b8SArnd Bergmann /* - Bridge Registration --------------------------------------------------- */
177735ba63b8SArnd Bergmann 
vme_dev_release(struct device * dev)177835ba63b8SArnd Bergmann static void vme_dev_release(struct device *dev)
177935ba63b8SArnd Bergmann {
178035ba63b8SArnd Bergmann 	kfree(dev_to_vme_dev(dev));
178135ba63b8SArnd Bergmann }
178235ba63b8SArnd Bergmann 
178335ba63b8SArnd Bergmann /* Common bridge initialization */
vme_init_bridge(struct vme_bridge * bridge)178435ba63b8SArnd Bergmann struct vme_bridge *vme_init_bridge(struct vme_bridge *bridge)
178535ba63b8SArnd Bergmann {
178635ba63b8SArnd Bergmann 	INIT_LIST_HEAD(&bridge->vme_error_handlers);
178735ba63b8SArnd Bergmann 	INIT_LIST_HEAD(&bridge->master_resources);
178835ba63b8SArnd Bergmann 	INIT_LIST_HEAD(&bridge->slave_resources);
178935ba63b8SArnd Bergmann 	INIT_LIST_HEAD(&bridge->dma_resources);
179035ba63b8SArnd Bergmann 	INIT_LIST_HEAD(&bridge->lm_resources);
179135ba63b8SArnd Bergmann 	mutex_init(&bridge->irq_mtx);
179235ba63b8SArnd Bergmann 
179335ba63b8SArnd Bergmann 	return bridge;
179435ba63b8SArnd Bergmann }
179535ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_init_bridge);
179635ba63b8SArnd Bergmann 
vme_register_bridge(struct vme_bridge * bridge)179735ba63b8SArnd Bergmann int vme_register_bridge(struct vme_bridge *bridge)
179835ba63b8SArnd Bergmann {
179935ba63b8SArnd Bergmann 	int i;
180035ba63b8SArnd Bergmann 	int ret = -1;
180135ba63b8SArnd Bergmann 
180235ba63b8SArnd Bergmann 	mutex_lock(&vme_buses_lock);
180335ba63b8SArnd Bergmann 	for (i = 0; i < sizeof(vme_bus_numbers) * 8; i++) {
180435ba63b8SArnd Bergmann 		if ((vme_bus_numbers & (1 << i)) == 0) {
180535ba63b8SArnd Bergmann 			vme_bus_numbers |= (1 << i);
180635ba63b8SArnd Bergmann 			bridge->num = i;
180735ba63b8SArnd Bergmann 			INIT_LIST_HEAD(&bridge->devices);
180835ba63b8SArnd Bergmann 			list_add_tail(&bridge->bus_list, &vme_bus_list);
180935ba63b8SArnd Bergmann 			ret = 0;
181035ba63b8SArnd Bergmann 			break;
181135ba63b8SArnd Bergmann 		}
181235ba63b8SArnd Bergmann 	}
181335ba63b8SArnd Bergmann 	mutex_unlock(&vme_buses_lock);
181435ba63b8SArnd Bergmann 
181535ba63b8SArnd Bergmann 	return ret;
181635ba63b8SArnd Bergmann }
181735ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_register_bridge);
181835ba63b8SArnd Bergmann 
vme_unregister_bridge(struct vme_bridge * bridge)181935ba63b8SArnd Bergmann void vme_unregister_bridge(struct vme_bridge *bridge)
182035ba63b8SArnd Bergmann {
182135ba63b8SArnd Bergmann 	struct vme_dev *vdev;
182235ba63b8SArnd Bergmann 	struct vme_dev *tmp;
182335ba63b8SArnd Bergmann 
182435ba63b8SArnd Bergmann 	mutex_lock(&vme_buses_lock);
182535ba63b8SArnd Bergmann 	vme_bus_numbers &= ~(1 << bridge->num);
182635ba63b8SArnd Bergmann 	list_for_each_entry_safe(vdev, tmp, &bridge->devices, bridge_list) {
182735ba63b8SArnd Bergmann 		list_del(&vdev->drv_list);
182835ba63b8SArnd Bergmann 		list_del(&vdev->bridge_list);
182935ba63b8SArnd Bergmann 		device_unregister(&vdev->dev);
183035ba63b8SArnd Bergmann 	}
183135ba63b8SArnd Bergmann 	list_del(&bridge->bus_list);
183235ba63b8SArnd Bergmann 	mutex_unlock(&vme_buses_lock);
183335ba63b8SArnd Bergmann }
183435ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_unregister_bridge);
183535ba63b8SArnd Bergmann 
183635ba63b8SArnd Bergmann /* - Driver Registration --------------------------------------------------- */
183735ba63b8SArnd Bergmann 
__vme_register_driver_bus(struct vme_driver * drv,struct vme_bridge * bridge,unsigned int ndevs)183835ba63b8SArnd Bergmann static int __vme_register_driver_bus(struct vme_driver *drv,
18396a889dc7SAlexon Oliveira 				     struct vme_bridge *bridge,
18406a889dc7SAlexon Oliveira 				     unsigned int ndevs)
184135ba63b8SArnd Bergmann {
184235ba63b8SArnd Bergmann 	int err;
184335ba63b8SArnd Bergmann 	unsigned int i;
184435ba63b8SArnd Bergmann 	struct vme_dev *vdev;
184535ba63b8SArnd Bergmann 	struct vme_dev *tmp;
184635ba63b8SArnd Bergmann 
184735ba63b8SArnd Bergmann 	for (i = 0; i < ndevs; i++) {
184835ba63b8SArnd Bergmann 		vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
184935ba63b8SArnd Bergmann 		if (!vdev) {
185035ba63b8SArnd Bergmann 			err = -ENOMEM;
185135ba63b8SArnd Bergmann 			goto err_devalloc;
185235ba63b8SArnd Bergmann 		}
185335ba63b8SArnd Bergmann 		vdev->num = i;
185435ba63b8SArnd Bergmann 		vdev->bridge = bridge;
185535ba63b8SArnd Bergmann 		vdev->dev.platform_data = drv;
185635ba63b8SArnd Bergmann 		vdev->dev.release = vme_dev_release;
185735ba63b8SArnd Bergmann 		vdev->dev.parent = bridge->parent;
185835ba63b8SArnd Bergmann 		vdev->dev.bus = &vme_bus_type;
185935ba63b8SArnd Bergmann 		dev_set_name(&vdev->dev, "%s.%u-%u", drv->name, bridge->num,
186035ba63b8SArnd Bergmann 			     vdev->num);
186135ba63b8SArnd Bergmann 
186235ba63b8SArnd Bergmann 		err = device_register(&vdev->dev);
186335ba63b8SArnd Bergmann 		if (err)
186435ba63b8SArnd Bergmann 			goto err_reg;
186535ba63b8SArnd Bergmann 
186635ba63b8SArnd Bergmann 		if (vdev->dev.platform_data) {
186735ba63b8SArnd Bergmann 			list_add_tail(&vdev->drv_list, &drv->devices);
186835ba63b8SArnd Bergmann 			list_add_tail(&vdev->bridge_list, &bridge->devices);
186935ba63b8SArnd Bergmann 		} else
187035ba63b8SArnd Bergmann 			device_unregister(&vdev->dev);
187135ba63b8SArnd Bergmann 	}
187235ba63b8SArnd Bergmann 	return 0;
187335ba63b8SArnd Bergmann 
187435ba63b8SArnd Bergmann err_reg:
187535ba63b8SArnd Bergmann 	put_device(&vdev->dev);
187635ba63b8SArnd Bergmann err_devalloc:
187735ba63b8SArnd Bergmann 	list_for_each_entry_safe(vdev, tmp, &drv->devices, drv_list) {
187835ba63b8SArnd Bergmann 		list_del(&vdev->drv_list);
187935ba63b8SArnd Bergmann 		list_del(&vdev->bridge_list);
188035ba63b8SArnd Bergmann 		device_unregister(&vdev->dev);
188135ba63b8SArnd Bergmann 	}
188235ba63b8SArnd Bergmann 	return err;
188335ba63b8SArnd Bergmann }
188435ba63b8SArnd Bergmann 
__vme_register_driver(struct vme_driver * drv,unsigned int ndevs)188535ba63b8SArnd Bergmann static int __vme_register_driver(struct vme_driver *drv, unsigned int ndevs)
188635ba63b8SArnd Bergmann {
188735ba63b8SArnd Bergmann 	struct vme_bridge *bridge;
188835ba63b8SArnd Bergmann 	int err = 0;
188935ba63b8SArnd Bergmann 
189035ba63b8SArnd Bergmann 	mutex_lock(&vme_buses_lock);
189135ba63b8SArnd Bergmann 	list_for_each_entry(bridge, &vme_bus_list, bus_list) {
189235ba63b8SArnd Bergmann 		/*
189335ba63b8SArnd Bergmann 		 * This cannot cause trouble as we already have vme_buses_lock
189435ba63b8SArnd Bergmann 		 * and if the bridge is removed, it will have to go through
189535ba63b8SArnd Bergmann 		 * vme_unregister_bridge() to do it (which calls remove() on
189635ba63b8SArnd Bergmann 		 * the bridge which in turn tries to acquire vme_buses_lock and
189735ba63b8SArnd Bergmann 		 * will have to wait).
189835ba63b8SArnd Bergmann 		 */
189935ba63b8SArnd Bergmann 		err = __vme_register_driver_bus(drv, bridge, ndevs);
190035ba63b8SArnd Bergmann 		if (err)
190135ba63b8SArnd Bergmann 			break;
190235ba63b8SArnd Bergmann 	}
190335ba63b8SArnd Bergmann 	mutex_unlock(&vme_buses_lock);
190435ba63b8SArnd Bergmann 	return err;
190535ba63b8SArnd Bergmann }
190635ba63b8SArnd Bergmann 
190735ba63b8SArnd Bergmann /**
190835ba63b8SArnd Bergmann  * vme_register_driver - Register a VME driver
190935ba63b8SArnd Bergmann  * @drv: Pointer to VME driver structure to register.
191035ba63b8SArnd Bergmann  * @ndevs: Maximum number of devices to allow to be enumerated.
191135ba63b8SArnd Bergmann  *
191235ba63b8SArnd Bergmann  * Register a VME device driver with the VME subsystem.
191335ba63b8SArnd Bergmann  *
191435ba63b8SArnd Bergmann  * Return: Zero on success, error value on registration failure.
191535ba63b8SArnd Bergmann  */
vme_register_driver(struct vme_driver * drv,unsigned int ndevs)191635ba63b8SArnd Bergmann int vme_register_driver(struct vme_driver *drv, unsigned int ndevs)
191735ba63b8SArnd Bergmann {
191835ba63b8SArnd Bergmann 	int err;
191935ba63b8SArnd Bergmann 
192035ba63b8SArnd Bergmann 	drv->driver.name = drv->name;
192135ba63b8SArnd Bergmann 	drv->driver.bus = &vme_bus_type;
192235ba63b8SArnd Bergmann 	INIT_LIST_HEAD(&drv->devices);
192335ba63b8SArnd Bergmann 
192435ba63b8SArnd Bergmann 	err = driver_register(&drv->driver);
192535ba63b8SArnd Bergmann 	if (err)
192635ba63b8SArnd Bergmann 		return err;
192735ba63b8SArnd Bergmann 
192835ba63b8SArnd Bergmann 	err = __vme_register_driver(drv, ndevs);
192935ba63b8SArnd Bergmann 	if (err)
193035ba63b8SArnd Bergmann 		driver_unregister(&drv->driver);
193135ba63b8SArnd Bergmann 
193235ba63b8SArnd Bergmann 	return err;
193335ba63b8SArnd Bergmann }
193435ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_register_driver);
193535ba63b8SArnd Bergmann 
193635ba63b8SArnd Bergmann /**
193735ba63b8SArnd Bergmann  * vme_unregister_driver - Unregister a VME driver
193835ba63b8SArnd Bergmann  * @drv: Pointer to VME driver structure to unregister.
193935ba63b8SArnd Bergmann  *
194035ba63b8SArnd Bergmann  * Unregister a VME device driver from the VME subsystem.
194135ba63b8SArnd Bergmann  */
vme_unregister_driver(struct vme_driver * drv)194235ba63b8SArnd Bergmann void vme_unregister_driver(struct vme_driver *drv)
194335ba63b8SArnd Bergmann {
194435ba63b8SArnd Bergmann 	struct vme_dev *dev, *dev_tmp;
194535ba63b8SArnd Bergmann 
194635ba63b8SArnd Bergmann 	mutex_lock(&vme_buses_lock);
194735ba63b8SArnd Bergmann 	list_for_each_entry_safe(dev, dev_tmp, &drv->devices, drv_list) {
194835ba63b8SArnd Bergmann 		list_del(&dev->drv_list);
194935ba63b8SArnd Bergmann 		list_del(&dev->bridge_list);
195035ba63b8SArnd Bergmann 		device_unregister(&dev->dev);
195135ba63b8SArnd Bergmann 	}
195235ba63b8SArnd Bergmann 	mutex_unlock(&vme_buses_lock);
195335ba63b8SArnd Bergmann 
195435ba63b8SArnd Bergmann 	driver_unregister(&drv->driver);
195535ba63b8SArnd Bergmann }
195635ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_unregister_driver);
195735ba63b8SArnd Bergmann 
195835ba63b8SArnd Bergmann /* - Bus Registration ------------------------------------------------------ */
195935ba63b8SArnd Bergmann 
vme_bus_match(struct device * dev,struct device_driver * drv)196035ba63b8SArnd Bergmann static int vme_bus_match(struct device *dev, struct device_driver *drv)
196135ba63b8SArnd Bergmann {
196235ba63b8SArnd Bergmann 	struct vme_driver *vme_drv;
196335ba63b8SArnd Bergmann 
196435ba63b8SArnd Bergmann 	vme_drv = container_of(drv, struct vme_driver, driver);
196535ba63b8SArnd Bergmann 
196635ba63b8SArnd Bergmann 	if (dev->platform_data == vme_drv) {
196735ba63b8SArnd Bergmann 		struct vme_dev *vdev = dev_to_vme_dev(dev);
196835ba63b8SArnd Bergmann 
196935ba63b8SArnd Bergmann 		if (vme_drv->match && vme_drv->match(vdev))
197035ba63b8SArnd Bergmann 			return 1;
197135ba63b8SArnd Bergmann 
197235ba63b8SArnd Bergmann 		dev->platform_data = NULL;
197335ba63b8SArnd Bergmann 	}
197435ba63b8SArnd Bergmann 	return 0;
197535ba63b8SArnd Bergmann }
197635ba63b8SArnd Bergmann 
vme_bus_probe(struct device * dev)197735ba63b8SArnd Bergmann static int vme_bus_probe(struct device *dev)
197835ba63b8SArnd Bergmann {
197935ba63b8SArnd Bergmann 	struct vme_driver *driver;
198035ba63b8SArnd Bergmann 	struct vme_dev *vdev = dev_to_vme_dev(dev);
198135ba63b8SArnd Bergmann 
198235ba63b8SArnd Bergmann 	driver = dev->platform_data;
198335ba63b8SArnd Bergmann 	if (driver->probe)
198435ba63b8SArnd Bergmann 		return driver->probe(vdev);
198535ba63b8SArnd Bergmann 
198635ba63b8SArnd Bergmann 	return -ENODEV;
198735ba63b8SArnd Bergmann }
198835ba63b8SArnd Bergmann 
vme_bus_remove(struct device * dev)198935ba63b8SArnd Bergmann static void vme_bus_remove(struct device *dev)
199035ba63b8SArnd Bergmann {
199135ba63b8SArnd Bergmann 	struct vme_driver *driver;
199235ba63b8SArnd Bergmann 	struct vme_dev *vdev = dev_to_vme_dev(dev);
199335ba63b8SArnd Bergmann 
199435ba63b8SArnd Bergmann 	driver = dev->platform_data;
199535ba63b8SArnd Bergmann 	if (driver->remove)
199635ba63b8SArnd Bergmann 		driver->remove(vdev);
199735ba63b8SArnd Bergmann }
199835ba63b8SArnd Bergmann 
199935ba63b8SArnd Bergmann struct bus_type vme_bus_type = {
200035ba63b8SArnd Bergmann 	.name = "vme",
200135ba63b8SArnd Bergmann 	.match = vme_bus_match,
200235ba63b8SArnd Bergmann 	.probe = vme_bus_probe,
200335ba63b8SArnd Bergmann 	.remove = vme_bus_remove,
200435ba63b8SArnd Bergmann };
200535ba63b8SArnd Bergmann EXPORT_SYMBOL(vme_bus_type);
200635ba63b8SArnd Bergmann 
vme_init(void)200735ba63b8SArnd Bergmann static int __init vme_init(void)
200835ba63b8SArnd Bergmann {
200935ba63b8SArnd Bergmann 	return bus_register(&vme_bus_type);
201035ba63b8SArnd Bergmann }
201135ba63b8SArnd Bergmann subsys_initcall(vme_init);
2012