Re: [patch 1/5] Staging: VME Framework for the Linux Kernel

From: Emilio G. Cota
Date: Sat Aug 08 2009 - 19:02:08 EST


A few comments after a quick glance, will need to dig deeper
though:
- semaphores? isn't it screaming for mutexes?
- some function signatures (get_whatever) pass too many
pointers; it's rather ugly. Passing a pointer to an
appropriate struct seems a better solution.
- vme_alloc_consistent looks pretty fishy; why don't you pass
that responsibility to the specific master? There you obviously
know if you're bridging over PCI or whatever. Or even better;
why is this needed at all?
- Please explain me what all the DMA functions do; are they
meant to be used by master or slaves?

Have a look at the interface for slaves we've got for our tsi148
driver, available at:
http://repo.or.cz/w/tsi148vmebridge.git

/* API for new drivers */
extern int vme_request_irq(unsigned int, int (*)(void *),
void *, const char *);
extern int vme_free_irq(unsigned int );
extern int vme_generate_interrupt(int, int, signed long);
extern struct vme_mapping* find_vme_mapping_from_addr(unsigned);
extern int vme_get_window_attr(struct vme_mapping *);
extern int vme_create_window(struct vme_mapping *);
extern int vme_destroy_window(int);
extern int vme_find_mapping(struct vme_mapping *, int);
extern int vme_release_mapping(struct vme_mapping *, int);
extern int vme_do_dma(struct vme_dma *);
extern int vme_bus_error_check(int);

That's pretty thin and it covers our slaves' needs. Do you see
anything missing there?

For masters there's no interface there because it was the
master's driver who directly provided these calls to slaves.
I had it in my to-do list to split that from the tsi148, in
the same fashion as you've done with this work.

- Note above the interrupt handler; simply needs the cookie. Also,
shouldn't your vme_request_irq() just require the IRQ vector?

- I'd like to see the whole picture, or 'vertical slice', i.e.
the bus interface + a master + a slave driver. How would
the slave's driver get the addresses and sizes of the mappings,
interrupt lines, etc. for each of the devices it controls?
For the time being we quickly hacked an xml-based scheme to get
this info upon installation, but it's clearly not suitable
for mainline.

Will probably have more comments when I get some time for further
inspection.

E.

(I'll leave this here because I've Cc'ed Sebastien Dugue)
Greg K-H wrote:
> From: Martyn Welch <martyn.welch@xxxxxxxxxxx>
>
> This framework aims to colelese, extend and improve the VME Linux
> drivers found at vmelinux.org, universe2.sourceforge.net and
> openfmi.net/frs/?group_id=144. The last 2 drivers appear to be forks of
> the original code found at vmelinux.org though have extended the
> codebase.
>
> Signed-off-by: Martyn Welch <martyn.welch@xxxxxxxxxxx>
> Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxx>
>
> ---
> drivers/staging/Kconfig | 2
> drivers/staging/Makefile | 1
> drivers/staging/vme/Kconfig | 17
> drivers/staging/vme/Makefile | 7
> drivers/staging/vme/vme.c | 1371 +++++++++++++++++++++++++++++++++++++++
> drivers/staging/vme/vme.h | 153 ++++
> drivers/staging/vme/vme_bridge.h | 249 +++++++
> 7 files changed, 1800 insertions(+)
>
> --- a/drivers/staging/Kconfig
> +++ b/drivers/staging/Kconfig
> @@ -139,5 +139,7 @@ source "drivers/staging/udlfb/Kconfig"
>
> source "drivers/staging/hv/Kconfig"
>
> +source "drivers/staging/vme/Kconfig"
> +
> endif # !STAGING_EXCLUDE_BUILD
> endif # STAGING
> --- a/drivers/staging/Makefile
> +++ b/drivers/staging/Makefile
> @@ -51,3 +51,4 @@ obj-$(CONFIG_FB_UDL) += udlfb/
> obj-$(CONFIG_MSM_ADSP) += dream/qdsp5/ dream/smd/
> obj-$(CONFIG_MSM_CAMERA) += dream/camera/
> obj-$(CONFIG_HYPERV) += hv/
> +obj-$(CONFIG_VME) += vme/
> --- /dev/null
> +++ b/drivers/staging/vme/Kconfig
> @@ -0,0 +1,17 @@
> +#
> +# VME configuration.
> +#
> +
> +menuconfig VME
> + tristate "VME bridge support"
> + depends on PCI
> + ---help---
> + If you say Y here you get support for the VME bridge Framework.
> +
> +if VME
> +
> +#source "drivers/staging/vme/bridges/Kconfig"
> +#
> +#source "drivers/staging/vme/devices/Kconfig"
> +
> +endif # VME
> --- /dev/null
> +++ b/drivers/staging/vme/Makefile
> @@ -0,0 +1,7 @@
> +#
> +# Makefile for the VME bridge device drivers.
> +#
> +obj-$(CONFIG_VME) += vme.o
> +
> +#obj-y += bridges/
> +#obj-y += devices/
> --- /dev/null
> +++ b/drivers/staging/vme/vme_bridge.h
> @@ -0,0 +1,249 @@
> +#ifndef _VME_BRIDGE_H_
> +#define _VME_BRIDGE_H_
> +
> +#define VME_CRCSR_BUF_SIZE (508*1024)
> +#define VME_SLOTS_MAX 32
> +/*
> + * Resource structures
> + */
> +struct vme_master_resource {
> + struct list_head list;
> + struct vme_bridge *parent;
> + /*
> + * We are likely to need to access the VME bus in interrupt context, so
> + * protect master routines with a spinlock rather than a semaphore.
> + */
> + spinlock_t lock;
> + int locked;
> + int number;
> + vme_address_t address_attr;
> + vme_cycle_t cycle_attr;
> + vme_width_t width_attr;
> + struct resource pci_resource; /* XXX Rename to be bus agnostic */
> + void *kern_base;
> +};
> +
> +struct vme_slave_resource {
> + struct list_head list;
> + struct vme_bridge *parent;
> + struct semaphore sem;
> + int locked;
> + int number;
> + vme_address_t address_attr;
> + vme_cycle_t cycle_attr;
> +};
> +
> +struct vme_dma_pattern {
> + u32 pattern;
> + vme_pattern_t type;
> +};
> +
> +struct vme_dma_pci {
> + dma_addr_t address;
> +};
> +
> +struct vme_dma_vme {
> + unsigned long long address;
> + vme_address_t aspace;
> + vme_cycle_t cycle;
> + vme_width_t dwidth;
> +};
> +
> +struct vme_dma_list {
> + struct list_head list;
> + struct vme_dma_resource *parent;
> + struct list_head entries;
> + struct semaphore sem;
> +};
> +
> +struct vme_dma_resource {
> + struct list_head list;
> + struct vme_bridge *parent;
> + struct semaphore sem;
> + int locked;
> + int number;
> + struct list_head pending;
> + struct list_head running;
> +};
> +
> +struct vme_bus_error {
> + struct list_head list;
> + unsigned long long address;
> + u32 attributes;
> +};
> +
> +struct vme_callback {
> + void (*func)(int, int, void*);
> + void *priv_data;
> +};
> +
> +struct vme_irq {
> + int count;
> + struct vme_callback callback[255];
> +};
> +
> +/* Allow 16 characters for name (including null character) */
> +#define VMENAMSIZ 16
> +
> +/* This structure stores all the information about one bridge
> + * The structure should be dynamically allocated by the driver and one instance
> + * of the structure should be present for each VME chip present in the system.
> + *
> + * Currently we assume that all chips are PCI-based
> + */
> +struct vme_bridge {
> + char name[VMENAMSIZ];
> + int num;
> + struct list_head master_resources;
> + struct list_head slave_resources;
> + struct list_head dma_resources;
> +
> + struct list_head vme_errors; /* List for errors generated on VME */
> +
> + /* Bridge Info - XXX Move to private structure? */
> + struct device *parent; /* Generic device struct (pdev->dev for PCI) */
> + void * base; /* Base Address of device registers */
> +
> + struct device dev[VME_SLOTS_MAX]; /* Device registered with
> + * device model on VME bus
> + */
> +
> + /* Interrupt callbacks */
> + struct vme_irq irq[7];
> +
> + /* Slave Functions */
> + int (*slave_get) (struct vme_slave_resource *, int *,
> + unsigned long long *, unsigned long long *, dma_addr_t *,
> + vme_address_t *, vme_cycle_t *);
> + int (*slave_set) (struct vme_slave_resource *, int, unsigned long long,
> + unsigned long long, dma_addr_t, vme_address_t, vme_cycle_t);
> +
> + /* Master Functions */
> + int (*master_get) (struct vme_master_resource *, int *,
> + unsigned long long *, unsigned long long *, vme_address_t *,
> + vme_cycle_t *, vme_width_t *);
> + int (*master_set) (struct vme_master_resource *, int,
> + unsigned long long, unsigned long long, vme_address_t,
> + vme_cycle_t, vme_width_t);
> + ssize_t (*master_read) (struct vme_master_resource *, void *, size_t,
> + loff_t);
> + ssize_t (*master_write) (struct vme_master_resource *, void *, size_t,
> + loff_t);
> + unsigned int (*master_rmw) (struct vme_master_resource *, unsigned int,
> + unsigned int, unsigned int, loff_t);
> +
> + /* DMA Functions */
> + int (*dma_list_add) (struct vme_dma_list *, struct vme_dma_attr *,
> + struct vme_dma_attr *, size_t);
> + int (*dma_list_exec) (struct vme_dma_list *);
> + int (*dma_list_empty) (struct vme_dma_list *);
> +
> + /* Interrupt Functions */
> + int (*request_irq) (int, int, void (*cback)(int, int, void*), void *);
> + void (*free_irq) (int, int);
> + int (*generate_irq) (int, int);
> +
> + /* Location monitor functions */
> + int (*lm_set) (unsigned long long, vme_address_t, vme_cycle_t);
> + int (*lm_get) (unsigned long long *, vme_address_t *, vme_cycle_t *);
> + int (*lm_attach) (int, void (*callback)(int));
> + int (*lm_detach) (int);
> +
> + /* CR/CSR space functions */
> + int (*slot_get) (void);
> + /* Use standard master read and write functions to access CR/CSR */
> +
> +#if 0
> + int (*set_prefetch) (void);
> + int (*get_prefetch) (void);
> + int (*set_arbiter) (void);
> + int (*get_arbiter) (void);
> + int (*set_requestor) (void);
> + int (*get_requestor) (void);
> +#endif
> +};
> +
> +int vme_register_bridge (struct vme_bridge *);
> +void vme_unregister_bridge (struct vme_bridge *);
> +
> +#endif /* _VME_BRIDGE_H_ */
> +
> +#if 0
> +/*
> + * VMEbus GET INFO Arg Structure
> + */
> +struct vmeInfoCfg {
> + int vmeSlotNum; /* VME slot number of interest */
> + int boardResponded; /* Board responded */
> + char sysConFlag; /* System controller flag */
> + int vmeControllerID; /* Vendor/device ID of VME bridge */
> + int vmeControllerRev; /* Revision of VME bridge */
> + char osName[8]; /* Name of OS e.g. "Linux" */
> + int vmeSharedDataValid; /* Validity of data struct */
> + int vmeDriverRev; /* Revision of VME driver */
> + unsigned int vmeAddrHi[8]; /* Address on VME bus */
> + unsigned int vmeAddrLo[8]; /* Address on VME bus */
> + unsigned int vmeSize[8]; /* Size on VME bus */
> + unsigned int vmeAm[8]; /* Address modifier on VME bus */
> + int reserved; /* For future use */
> +};
> +typedef struct vmeInfoCfg vmeInfoCfg_t;
> +
> +/*
> + * VMEbus Requester Arg Structure
> + */
> +struct vmeRequesterCfg {
> + int requestLevel; /* Requester Bus Request Level */
> + char fairMode; /* Requester Fairness Mode Indicator */
> + int releaseMode; /* Requester Bus Release Mode */
> + int timeonTimeoutTimer; /* Master Time-on Time-out Timer */
> + int timeoffTimeoutTimer; /* Master Time-off Time-out Timer */
> + int reserved; /* For future use */
> +};
> +typedef struct vmeRequesterCfg vmeRequesterCfg_t;
> +
> +/*
> + * VMEbus Arbiter Arg Structure
> + */
> +struct vmeArbiterCfg {
> + vme_arbitration_t arbiterMode; /* Arbitration Scheduling Algorithm */
> + char arbiterTimeoutFlag; /* Arbiter Time-out Timer Indicator */
> + int globalTimeoutTimer; /* VMEbus Global Time-out Timer */
> + char noEarlyReleaseFlag; /* No Early Release on BBUSY */
> + int reserved; /* For future use */
> +};
> +typedef struct vmeArbiterCfg vmeArbiterCfg_t;
> +
> +
> +/*
> + * VMEbus RMW Configuration Data
> + */
> +struct vmeRmwCfg {
> + unsigned int targetAddrU; /* VME Address (Upper) to trigger RMW cycle */
> + unsigned int targetAddr; /* VME Address (Lower) to trigger RMW cycle */
> + vme_address_t addrSpace; /* VME Address Space */
> + int enableMask; /* Bit mask defining the bits of interest */
> + int compareData; /* Data to be compared with the data read */
> + int swapData; /* Data written to the VMEbus on success */
> + int maxAttempts; /* Maximum times to try */
> + int numAttempts; /* Number of attempts before success */
> + int reserved; /* For future use */
> +
> +};
> +typedef struct vmeRmwCfg vmeRmwCfg_t;
> +
> +/*
> + * VMEbus Location Monitor Arg Structure
> + */
> +struct vmeLmCfg {
> + unsigned int addrU; /* Location Monitor Address upper */
> + unsigned int addr; /* Location Monitor Address lower */
> + vme_address_t addrSpace; /* Address Space */
> + int userAccessType; /* User/Supervisor Access Type */
> + int dataAccessType; /* Data/Program Access Type */
> + int lmWait; /* Time to wait for access */
> + int lmEvents; /* Lm event mask */
> + int reserved; /* For future use */
> +};
> +typedef struct vmeLmCfg vmeLmCfg_t;
> +#endif
> --- /dev/null
> +++ b/drivers/staging/vme/vme.c
> @@ -0,0 +1,1371 @@
> +/*
> + * VME Bridge Framework
> + *
> + * Author: Martyn Welch <martyn.welch@xxxxxxxxxxx>
> + * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc.
> + *
> + * Based on work by Tom Armistead and Ajit Prem
> + * Copyright 2004 Motorola Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + */
> +
> +#include <linux/version.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/mm.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/pci.h>
> +#include <linux/poll.h>
> +#include <linux/highmem.h>
> +#include <linux/interrupt.h>
> +#include <linux/pagemap.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/syscalls.h>
> +#include <linux/semaphore.h>
> +#include <linux/spinlock.h>
> +
> +#include "vme.h"
> +#include "vme_bridge.h"
> +
> +/* Bitmask and semaphore to keep track of bridge numbers */
> +static unsigned int vme_bus_numbers;
> +DECLARE_MUTEX(vme_bus_num_sem);
> +
> +static void __exit vme_exit (void);
> +static int __init vme_init (void);
> +
> +
> +/*
> + * Find the bridge resource associated with a specific device resource
> + */
> +static struct vme_bridge *dev_to_bridge(struct device *dev)
> +{
> + return dev->platform_data;
> +}
> +
> +/*
> + * Find the bridge that the resource is associated with.
> + */
> +static struct vme_bridge *find_bridge(struct vme_resource *resource)
> +{
> + /* Get list to search */
> + switch (resource->type) {
> + case VME_MASTER:
> + return list_entry(resource->entry, struct vme_master_resource,
> + list)->parent;
> + break;
> + case VME_SLAVE:
> + return list_entry(resource->entry, struct vme_slave_resource,
> + list)->parent;
> + break;
> + case VME_DMA:
> + return list_entry(resource->entry, struct vme_dma_resource,
> + list)->parent;
> + break;
> + default:
> + printk(KERN_ERR "Unknown resource type\n");
> + return NULL;
> + break;
> + }
> +}
> +
> +/*
> + * Allocate a contiguous block of memory for use by the driver. This is used to
> + * create the buffers for the slave windows.
> + *
> + * XXX VME bridges could be available on buses other than PCI. At the momment
> + * this framework only supports PCI devices.
> + */
> +void * vme_alloc_consistent(struct vme_resource *resource, size_t size,
> + dma_addr_t *dma)
> +{
> + struct vme_bridge *bridge;
> + struct pci_dev *pdev;
> +
> + if(resource == NULL) {
> + printk("No resource\n");
> + return NULL;
> + }
> +
> + bridge = find_bridge(resource);
> + if(bridge == NULL) {
> + printk("Can't find bridge\n");
> + return NULL;
> + }
> +
> + /* Find pci_dev container of dev */
> + if (bridge->parent == NULL) {
> + printk("Dev entry NULL\n");
> + return NULL;
> + }
> + pdev = container_of(bridge->parent, struct pci_dev, dev);
> +
> + return pci_alloc_consistent(pdev, size, dma);
> +}
> +EXPORT_SYMBOL(vme_alloc_consistent);
> +
> +/*
> + * Free previously allocated contiguous block of memory.
> + *
> + * XXX VME bridges could be available on buses other than PCI. At the momment
> + * this framework only supports PCI devices.
> + */
> +void vme_free_consistent(struct vme_resource *resource, size_t size,
> + void *vaddr, dma_addr_t dma)
> +{
> + struct vme_bridge *bridge;
> + struct pci_dev *pdev;
> +
> + if(resource == NULL) {
> + printk("No resource\n");
> + return;
> + }
> +
> + bridge = find_bridge(resource);
> + if(bridge == NULL) {
> + printk("Can't find bridge\n");
> + return;
> + }
> +
> + /* Find pci_dev container of dev */
> + pdev = container_of(bridge->parent, struct pci_dev, dev);
> +
> + pci_free_consistent(pdev, size, vaddr, dma);
> +}
> +EXPORT_SYMBOL(vme_free_consistent);
> +
> +size_t vme_get_size(struct vme_resource *resource)
> +{
> + int enabled, retval;
> + unsigned long long base, size;
> + dma_addr_t buf_base;
> + vme_address_t aspace;
> + vme_cycle_t cycle;
> + vme_width_t dwidth;
> +
> + switch (resource->type) {
> + case VME_MASTER:
> + retval = vme_master_get(resource, &enabled, &base, &size,
> + &aspace, &cycle, &dwidth);
> +
> + return size;
> + break;
> + case VME_SLAVE:
> + retval = vme_slave_get(resource, &enabled, &base, &size,
> + &buf_base, &aspace, &cycle);
> +
> + return size;
> + break;
> + case VME_DMA:
> + return 0;
> + break;
> + default:
> + printk(KERN_ERR "Unknown resource type\n");
> + return 0;
> + break;
> + }
> +}
> +EXPORT_SYMBOL(vme_get_size);
> +
> +static int vme_check_window(vme_address_t aspace, unsigned long long vme_base,
> + unsigned long long size)
> +{
> + int retval = 0;
> +
> + switch (aspace) {
> + case VME_A16:
> + if (((vme_base + size) > VME_A16_MAX) ||
> + (vme_base > VME_A16_MAX))
> + retval = -EFAULT;
> + break;
> + case VME_A24:
> + if (((vme_base + size) > VME_A24_MAX) ||
> + (vme_base > VME_A24_MAX))
> + retval = -EFAULT;
> + break;
> + case VME_A32:
> + if (((vme_base + size) > VME_A32_MAX) ||
> + (vme_base > VME_A32_MAX))
> + retval = -EFAULT;
> + break;
> + case VME_A64:
> + /*
> + * Any value held in an unsigned long long can be used as the
> + * base
> + */
> + break;
> + case VME_CRCSR:
> + if (((vme_base + size) > VME_CRCSR_MAX) ||
> + (vme_base > VME_CRCSR_MAX))
> + retval = -EFAULT;
> + break;
> + case VME_USER1:
> + case VME_USER2:
> + case VME_USER3:
> + case VME_USER4:
> + /* User Defined */
> + break;
> + default:
> + printk("Invalid address space\n");
> + retval = -EINVAL;
> + break;
> + }
> +
> + return retval;
> +}
> +
> +/*
> + * Request a slave image with specific attributes, return some unique
> + * identifier.
> + */
> +struct vme_resource * vme_slave_request(struct device *dev,
> + vme_address_t address, vme_cycle_t cycle)
> +{
> + struct vme_bridge *bridge;
> + struct list_head *slave_pos = NULL;
> + struct vme_slave_resource *allocated_image = NULL;
> + struct vme_slave_resource *slave_image = NULL;
> + struct vme_resource *resource = NULL;
> +
> + bridge = dev_to_bridge(dev);
> + if (bridge == NULL) {
> + printk(KERN_ERR "Can't find VME bus\n");
> + goto err_bus;
> + }
> +
> + /* Loop through slave resources */
> + list_for_each(slave_pos, &(bridge->slave_resources)) {
> + slave_image = list_entry(slave_pos,
> + struct vme_slave_resource, list);
> +
> + if (slave_image == NULL) {
> + printk("Registered NULL Slave resource\n");
> + continue;
> + }
> +
> + /* Find an unlocked and compatible image */
> + down(&(slave_image->sem));
> + if(((slave_image->address_attr & address) == address) &&
> + ((slave_image->cycle_attr & cycle) == cycle) &&
> + (slave_image->locked == 0)) {
> +
> + slave_image->locked = 1;
> + up(&(slave_image->sem));
> + allocated_image = slave_image;
> + break;
> + }
> + up(&(slave_image->sem));
> + }
> +
> + /* No free image */
> + if (allocated_image == NULL)
> + goto err_image;
> +
> + resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL);
> + if (resource == NULL) {
> + printk(KERN_WARNING "Unable to allocate resource structure\n");
> + goto err_alloc;
> + }
> + resource->type = VME_SLAVE;
> + resource->entry = &(allocated_image->list);
> +
> + return resource;
> +
> +err_alloc:
> + /* Unlock image */
> + down(&(slave_image->sem));
> + slave_image->locked = 0;
> + up(&(slave_image->sem));
> +err_image:
> +err_bus:
> + return NULL;
> +}
> +EXPORT_SYMBOL(vme_slave_request);
> +
> +int vme_slave_set (struct vme_resource *resource, int enabled,
> + unsigned long long vme_base, unsigned long long size,
> + dma_addr_t buf_base, vme_address_t aspace, vme_cycle_t cycle)
> +{
> + struct vme_bridge *bridge = find_bridge(resource);
> + struct vme_slave_resource *image;
> + int retval;
> +
> + if (resource->type != VME_SLAVE) {
> + printk("Not a slave resource\n");
> + return -EINVAL;
> + }
> +
> + image = list_entry(resource->entry, struct vme_slave_resource, list);
> +
> + if (bridge->slave_set == NULL) {
> + printk("Function not supported\n");
> + return -ENOSYS;
> + }
> +
> + if(!(((image->address_attr & aspace) == aspace) &&
> + ((image->cycle_attr & cycle) == cycle))) {
> + printk("Invalid attributes\n");
> + return -EINVAL;
> + }
> +
> + retval = vme_check_window(aspace, vme_base, size);
> + if(retval)
> + return retval;
> +
> + return bridge->slave_set(image, enabled, vme_base, size, buf_base,
> + aspace, cycle);
> +}
> +EXPORT_SYMBOL(vme_slave_set);
> +
> +int vme_slave_get (struct vme_resource *resource, int *enabled,
> + unsigned long long *vme_base, unsigned long long *size,
> + dma_addr_t *buf_base, vme_address_t *aspace, vme_cycle_t *cycle)
> +{
> + struct vme_bridge *bridge = find_bridge(resource);
> + struct vme_slave_resource *image;
> +
> + if (resource->type != VME_SLAVE) {
> + printk("Not a slave resource\n");
> + return -EINVAL;
> + }
> +
> + image = list_entry(resource->entry, struct vme_slave_resource, list);
> +
> + if (bridge->slave_set == NULL) {
> + printk("vme_slave_get not supported\n");
> + return -EINVAL;
> + }
> +
> + return bridge->slave_get(image, enabled, vme_base, size, buf_base,
> + aspace, cycle);
> +}
> +EXPORT_SYMBOL(vme_slave_get);
> +
> +void vme_slave_free(struct vme_resource *resource)
> +{
> + struct vme_slave_resource *slave_image;
> +
> + if (resource->type != VME_SLAVE) {
> + printk("Not a slave resource\n");
> + return;
> + }
> +
> + slave_image = list_entry(resource->entry, struct vme_slave_resource,
> + list);
> + if (slave_image == NULL) {
> + printk("Can't find slave resource\n");
> + return;
> + }
> +
> + /* Unlock image */
> + down(&(slave_image->sem));
> + if (slave_image->locked == 0)
> + printk(KERN_ERR "Image is already free\n");
> +
> + slave_image->locked = 0;
> + up(&(slave_image->sem));
> +
> + /* Free up resource memory */
> + kfree(resource);
> +}
> +EXPORT_SYMBOL(vme_slave_free);
> +
> +/*
> + * Request a master image with specific attributes, return some unique
> + * identifier.
> + */
> +struct vme_resource * vme_master_request(struct device *dev,
> + vme_address_t address, vme_cycle_t cycle, vme_width_t dwidth)
> +{
> + struct vme_bridge *bridge;
> + struct list_head *master_pos = NULL;
> + struct vme_master_resource *allocated_image = NULL;
> + struct vme_master_resource *master_image = NULL;
> + struct vme_resource *resource = NULL;
> +
> + bridge = dev_to_bridge(dev);
> + if (bridge == NULL) {
> + printk(KERN_ERR "Can't find VME bus\n");
> + goto err_bus;
> + }
> +
> + /* Loop through master resources */
> + list_for_each(master_pos, &(bridge->master_resources)) {
> + master_image = list_entry(master_pos,
> + struct vme_master_resource, list);
> +
> + if (master_image == NULL) {
> + printk(KERN_WARNING "Registered NULL master resource\n");
> + continue;
> + }
> +
> + /* Find an unlocked and compatible image */
> + spin_lock(&(master_image->lock));
> + if(((master_image->address_attr & address) == address) &&
> + ((master_image->cycle_attr & cycle) == cycle) &&
> + ((master_image->width_attr & dwidth) == dwidth) &&
> + (master_image->locked == 0)) {
> +
> + master_image->locked = 1;
> + spin_unlock(&(master_image->lock));
> + allocated_image = master_image;
> + break;
> + }
> + spin_unlock(&(master_image->lock));
> + }
> +
> + /* Check to see if we found a resource */
> + if (allocated_image == NULL) {
> + printk(KERN_ERR "Can't find a suitable resource\n");
> + goto err_image;
> + }
> +
> + resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL);
> + if (resource == NULL) {
> + printk(KERN_ERR "Unable to allocate resource structure\n");
> + goto err_alloc;
> + }
> + resource->type = VME_MASTER;
> + resource->entry = &(allocated_image->list);
> +
> + return resource;
> +
> + kfree(resource);
> +err_alloc:
> + /* Unlock image */
> + spin_lock(&(master_image->lock));
> + master_image->locked = 0;
> + spin_unlock(&(master_image->lock));
> +err_image:
> +err_bus:
> + return NULL;
> +}
> +EXPORT_SYMBOL(vme_master_request);
> +
> +int vme_master_set (struct vme_resource *resource, int enabled,
> + unsigned long long vme_base, unsigned long long size,
> + vme_address_t aspace, vme_cycle_t cycle, vme_width_t dwidth)
> +{
> + struct vme_bridge *bridge = find_bridge(resource);
> + struct vme_master_resource *image;
> + int retval;
> +
> + if (resource->type != VME_MASTER) {
> + printk("Not a master resource\n");
> + return -EINVAL;
> + }
> +
> + image = list_entry(resource->entry, struct vme_master_resource, list);
> +
> + if (bridge->master_set == NULL) {
> + printk("vme_master_set not supported\n");
> + return -EINVAL;
> + }
> +
> + if(!(((image->address_attr & aspace) == aspace) &&
> + ((image->cycle_attr & cycle) == cycle) &&
> + ((image->width_attr & dwidth) == dwidth))) {
> + printk("Invalid attributes\n");
> + return -EINVAL;
> + }
> +
> + retval = vme_check_window(aspace, vme_base, size);
> + if(retval)
> + return retval;
> +
> + return bridge->master_set(image, enabled, vme_base, size, aspace,
> + cycle, dwidth);
> +}
> +EXPORT_SYMBOL(vme_master_set);
> +
> +int vme_master_get (struct vme_resource *resource, int *enabled,
> + unsigned long long *vme_base, unsigned long long *size,
> + vme_address_t *aspace, vme_cycle_t *cycle, vme_width_t *dwidth)
> +{
> + struct vme_bridge *bridge = find_bridge(resource);
> + struct vme_master_resource *image;
> +
> + if (resource->type != VME_MASTER) {
> + printk("Not a master resource\n");
> + return -EINVAL;
> + }
> +
> + image = list_entry(resource->entry, struct vme_master_resource, list);
> +
> + if (bridge->master_set == NULL) {
> + printk("vme_master_set not supported\n");
> + return -EINVAL;
> + }
> +
> + return bridge->master_get(image, enabled, vme_base, size, aspace,
> + cycle, dwidth);
> +}
> +EXPORT_SYMBOL(vme_master_get);
> +
> +/*
> + * Read data out of VME space into a buffer.
> + */
> +ssize_t vme_master_read (struct vme_resource *resource, void *buf, size_t count,
> + loff_t offset)
> +{
> + struct vme_bridge *bridge = find_bridge(resource);
> + struct vme_master_resource *image;
> + size_t length;
> +
> + if (bridge->master_read == NULL) {
> + printk("Reading from resource not supported\n");
> + return -EINVAL;
> + }
> +
> + if (resource->type != VME_MASTER) {
> + printk("Not a master resource\n");
> + return -EINVAL;
> + }
> +
> + image = list_entry(resource->entry, struct vme_master_resource, list);
> +
> + length = vme_get_size(resource);
> +
> + if (offset > length) {
> + printk("Invalid Offset\n");
> + return -EFAULT;
> + }
> +
> + if ((offset + count) > length)
> + count = length - offset;
> +
> + return bridge->master_read(image, buf, count, offset);
> +
> +}
> +EXPORT_SYMBOL(vme_master_read);
> +
> +/*
> + * Write data out to VME space from a buffer.
> + */
> +ssize_t vme_master_write (struct vme_resource *resource, void *buf,
> + size_t count, loff_t offset)
> +{
> + struct vme_bridge *bridge = find_bridge(resource);
> + struct vme_master_resource *image;
> + size_t length;
> +
> + if (bridge->master_write == NULL) {
> + printk("Writing to resource not supported\n");
> + return -EINVAL;
> + }
> +
> + if (resource->type != VME_MASTER) {
> + printk("Not a master resource\n");
> + return -EINVAL;
> + }
> +
> + image = list_entry(resource->entry, struct vme_master_resource, list);
> +
> + length = vme_get_size(resource);
> +
> + if (offset > length) {
> + printk("Invalid Offset\n");
> + return -EFAULT;
> + }
> +
> + if ((offset + count) > length)
> + count = length - offset;
> +
> + return bridge->master_write(image, buf, count, offset);
> +}
> +EXPORT_SYMBOL(vme_master_write);
> +
> +/*
> + * Perform RMW cycle to provided location.
> + */
> +unsigned int vme_master_rmw (struct vme_resource *resource, unsigned int mask,
> + unsigned int compare, unsigned int swap, loff_t offset)
> +{
> + struct vme_bridge *bridge = find_bridge(resource);
> + struct vme_master_resource *image;
> +
> + if (bridge->master_rmw == NULL) {
> + printk("Writing to resource not supported\n");
> + return -EINVAL;
> + }
> +
> + if (resource->type != VME_MASTER) {
> + printk("Not a master resource\n");
> + return -EINVAL;
> + }
> +
> + image = list_entry(resource->entry, struct vme_master_resource, list);
> +
> + return bridge->master_rmw(image, mask, compare, swap, offset);
> +}
> +EXPORT_SYMBOL(vme_master_rmw);
> +
> +void vme_master_free(struct vme_resource *resource)
> +{
> + struct vme_master_resource *master_image;
> +
> + if (resource->type != VME_MASTER) {
> + printk("Not a master resource\n");
> + return;
> + }
> +
> + master_image = list_entry(resource->entry, struct vme_master_resource,
> + list);
> + if (master_image == NULL) {
> + printk("Can't find master resource\n");
> + return;
> + }
> +
> + /* Unlock image */
> + spin_lock(&(master_image->lock));
> + if (master_image->locked == 0)
> + printk(KERN_ERR "Image is already free\n");
> +
> + master_image->locked = 0;
> + spin_unlock(&(master_image->lock));
> +
> + /* Free up resource memory */
> + kfree(resource);
> +}
> +EXPORT_SYMBOL(vme_master_free);
> +
> +/*
> + * Request a DMA controller with specific attributes, return some unique
> + * identifier.
> + */
> +struct vme_resource *vme_request_dma(struct device *dev)
> +{
> + struct vme_bridge *bridge;
> + struct list_head *dma_pos = NULL;
> + struct vme_dma_resource *allocated_ctrlr = NULL;
> + struct vme_dma_resource *dma_ctrlr = NULL;
> + struct vme_resource *resource = NULL;
> +
> + /* XXX Not checking resource attributes */
> + printk(KERN_ERR "No VME resource Attribute tests done\n");
> +
> + bridge = dev_to_bridge(dev);
> + if (bridge == NULL) {
> + printk(KERN_ERR "Can't find VME bus\n");
> + goto err_bus;
> + }
> +
> + /* Loop through DMA resources */
> + list_for_each(dma_pos, &(bridge->dma_resources)) {
> + dma_ctrlr = list_entry(dma_pos,
> + struct vme_dma_resource, list);
> +
> + if (dma_ctrlr == NULL) {
> + printk("Registered NULL DMA resource\n");
> + continue;
> + }
> +
> + /* Find an unlocked controller */
> + down(&(dma_ctrlr->sem));
> + if(dma_ctrlr->locked == 0) {
> + dma_ctrlr->locked = 1;
> + up(&(dma_ctrlr->sem));
> + allocated_ctrlr = dma_ctrlr;
> + break;
> + }
> + up(&(dma_ctrlr->sem));
> + }
> +
> + /* Check to see if we found a resource */
> + if (allocated_ctrlr == NULL)
> + goto err_ctrlr;
> +
> + resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL);
> + if (resource == NULL) {
> + printk(KERN_WARNING "Unable to allocate resource structure\n");
> + goto err_alloc;
> + }
> + resource->type = VME_DMA;
> + resource->entry = &(allocated_ctrlr->list);
> +
> + return resource;
> +
> +err_alloc:
> + /* Unlock image */
> + down(&(dma_ctrlr->sem));
> + dma_ctrlr->locked = 0;
> + up(&(dma_ctrlr->sem));
> +err_ctrlr:
> +err_bus:
> + return NULL;
> +}
> +EXPORT_SYMBOL(vme_request_dma);
> +
> +/*
> + * Start new list
> + */
> +struct vme_dma_list *vme_new_dma_list(struct vme_resource *resource)
> +{
> + struct vme_dma_resource *ctrlr;
> + struct vme_dma_list *dma_list;
> +
> + if (resource->type != VME_DMA) {
> + printk("Not a DMA resource\n");
> + return NULL;
> + }
> +
> + ctrlr = list_entry(resource->entry, struct vme_dma_resource, list);
> +
> + dma_list = (struct vme_dma_list *)kmalloc(
> + sizeof(struct vme_dma_list), GFP_KERNEL);
> + if(dma_list == NULL) {
> + printk("Unable to allocate memory for new dma list\n");
> + return NULL;
> + }
> + INIT_LIST_HEAD(&(dma_list->entries));
> + dma_list->parent = ctrlr;
> + init_MUTEX(&(dma_list->sem));
> +
> + return dma_list;
> +}
> +EXPORT_SYMBOL(vme_new_dma_list);
> +
> +/*
> + * Create "Pattern" type attributes
> + */
> +struct vme_dma_attr *vme_dma_pattern_attribute(u32 pattern,
> + vme_pattern_t type)
> +{
> + struct vme_dma_attr *attributes;
> + struct vme_dma_pattern *pattern_attr;
> +
> + attributes = (struct vme_dma_attr *)kmalloc(
> + sizeof(struct vme_dma_attr), GFP_KERNEL);
> + if(attributes == NULL) {
> + printk("Unable to allocate memory for attributes structure\n");
> + goto err_attr;
> + }
> +
> + pattern_attr = (struct vme_dma_pattern *)kmalloc(
> + sizeof(struct vme_dma_pattern), GFP_KERNEL);
> + if(pattern_attr == NULL) {
> + printk("Unable to allocate memory for pattern attributes\n");
> + goto err_pat;
> + }
> +
> + attributes->type = VME_DMA_PATTERN;
> + attributes->private = (void *)pattern_attr;
> +
> + pattern_attr->pattern = pattern;
> + pattern_attr->type = type;
> +
> + return attributes;
> +
> + kfree(pattern_attr);
> +err_pat:
> + kfree(attributes);
> +err_attr:
> + return NULL;
> +}
> +EXPORT_SYMBOL(vme_dma_pattern_attribute);
> +
> +/*
> + * Create "PCI" type attributes
> + */
> +struct vme_dma_attr *vme_dma_pci_attribute(dma_addr_t address)
> +{
> + struct vme_dma_attr *attributes;
> + struct vme_dma_pci *pci_attr;
> +
> + /* XXX Run some sanity checks here */
> +
> + attributes = (struct vme_dma_attr *)kmalloc(
> + sizeof(struct vme_dma_attr), GFP_KERNEL);
> + if(attributes == NULL) {
> + printk("Unable to allocate memory for attributes structure\n");
> + goto err_attr;
> + }
> +
> + pci_attr = (struct vme_dma_pci *)kmalloc(sizeof(struct vme_dma_pci),
> + GFP_KERNEL);
> + if(pci_attr == NULL) {
> + printk("Unable to allocate memory for pci attributes\n");
> + goto err_pci;
> + }
> +
> +
> +
> + attributes->type = VME_DMA_PCI;
> + attributes->private = (void *)pci_attr;
> +
> + pci_attr->address = address;
> +
> + return attributes;
> +
> + kfree(pci_attr);
> +err_pci:
> + kfree(attributes);
> +err_attr:
> + return NULL;
> +}
> +EXPORT_SYMBOL(vme_dma_pci_attribute);
> +
> +/*
> + * Create "VME" type attributes
> + */
> +struct vme_dma_attr *vme_dma_vme_attribute(unsigned long long address,
> + vme_address_t aspace, vme_cycle_t cycle, vme_width_t dwidth)
> +{
> + struct vme_dma_attr *attributes;
> + struct vme_dma_vme *vme_attr;
> +
> + /* XXX Run some sanity checks here */
> +
> + attributes = (struct vme_dma_attr *)kmalloc(
> + sizeof(struct vme_dma_attr), GFP_KERNEL);
> + if(attributes == NULL) {
> + printk("Unable to allocate memory for attributes structure\n");
> + goto err_attr;
> + }
> +
> + vme_attr = (struct vme_dma_vme *)kmalloc(sizeof(struct vme_dma_vme),
> + GFP_KERNEL);
> + if(vme_attr == NULL) {
> + printk("Unable to allocate memory for vme attributes\n");
> + goto err_vme;
> + }
> +
> + attributes->type = VME_DMA_VME;
> + attributes->private = (void *)vme_attr;
> +
> + vme_attr->address = address;
> + vme_attr->aspace = aspace;
> + vme_attr->cycle = cycle;
> + vme_attr->dwidth = dwidth;
> +
> + return attributes;
> +
> + kfree(vme_attr);
> +err_vme:
> + kfree(attributes);
> +err_attr:
> + return NULL;
> +}
> +EXPORT_SYMBOL(vme_dma_vme_attribute);
> +
> +/*
> + * Free attribute
> + */
> +void vme_dma_free_attribute(struct vme_dma_attr *attributes)
> +{
> + kfree(attributes->private);
> + kfree(attributes);
> +}
> +EXPORT_SYMBOL(vme_dma_free_attribute);
> +
> +int vme_dma_list_add(struct vme_dma_list *list, struct vme_dma_attr *src,
> + struct vme_dma_attr *dest, size_t count)
> +{
> + struct vme_bridge *bridge = list->parent->parent;
> + int retval;
> +
> + if (bridge->dma_list_add == NULL) {
> + printk("Link List DMA generation not supported\n");
> + return -EINVAL;
> + }
> +
> + if (down_trylock(&(list->sem))) {
> + printk("Link List already submitted\n");
> + return -EINVAL;
> + }
> +
> + retval = bridge->dma_list_add(list, src, dest, count);
> +
> + up(&(list->sem));
> +
> + return retval;
> +}
> +EXPORT_SYMBOL(vme_dma_list_add);
> +
> +int vme_dma_list_exec(struct vme_dma_list *list)
> +{
> + struct vme_bridge *bridge = list->parent->parent;
> + int retval;
> +
> + if (bridge->dma_list_exec == NULL) {
> + printk("Link List DMA execution not supported\n");
> + return -EINVAL;
> + }
> +
> + down(&(list->sem));
> +
> + retval = bridge->dma_list_exec(list);
> +
> + up(&(list->sem));
> +
> + return retval;
> +}
> +EXPORT_SYMBOL(vme_dma_list_exec);
> +
> +int vme_dma_list_free(struct vme_dma_list *list)
> +{
> + struct vme_bridge *bridge = list->parent->parent;
> + int retval;
> +
> + if (bridge->dma_list_empty == NULL) {
> + printk("Emptying of Link Lists not supported\n");
> + return -EINVAL;
> + }
> +
> + if (down_trylock(&(list->sem))) {
> + printk("Link List in use\n");
> + return -EINVAL;
> + }
> +
> + /*
> + * Empty out all of the entries from the dma list. We need to go to the
> + * low level driver as dma entries are driver specific.
> + */
> + retval = bridge->dma_list_empty(list);
> + if (retval) {
> + printk("Unable to empty link-list entries\n");
> + up(&(list->sem));
> + return retval;
> + }
> + up(&(list->sem));
> + kfree(list);
> +
> + return retval;
> +}
> +EXPORT_SYMBOL(vme_dma_list_free);
> +
> +int vme_dma_free(struct vme_resource *resource)
> +{
> + struct vme_dma_resource *ctrlr;
> +
> + if (resource->type != VME_DMA) {
> + printk("Not a DMA resource\n");
> + return -EINVAL;
> + }
> +
> + ctrlr = list_entry(resource->entry, struct vme_dma_resource, list);
> +
> + if (down_trylock(&(ctrlr->sem))) {
> + printk("Resource busy, can't free\n");
> + return -EBUSY;
> + }
> +
> + if (!(list_empty(&(ctrlr->pending)) && list_empty(&(ctrlr->running)))) {
> + printk("Resource still processing transfers\n");
> + up(&(ctrlr->sem));
> + return -EBUSY;
> + }
> +
> + ctrlr->locked = 0;
> +
> + up(&(ctrlr->sem));
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(vme_dma_free);
> +
> +int vme_request_irq(struct device *dev, int level, int statid,
> + void (*callback)(int level, int vector, void *priv_data),
> + void *priv_data)
> +{
> + struct vme_bridge *bridge;
> +
> + bridge = dev_to_bridge(dev);
> + if (bridge == NULL) {
> + printk(KERN_ERR "Can't find VME bus\n");
> + return -EINVAL;
> + }
> +
> + if((level < 1) || (level > 7)) {
> + printk(KERN_WARNING "Invalid interrupt level\n");
> + return -EINVAL;
> + }
> +
> + if (bridge->request_irq == NULL) {
> + printk("Registering interrupts not supported\n");
> + return -EINVAL;
> + }
> +
> + return bridge->request_irq(level, statid, callback, priv_data);
> +}
> +EXPORT_SYMBOL(vme_request_irq);
> +
> +void vme_free_irq(struct device *dev, int level, int statid)
> +{
> + struct vme_bridge *bridge;
> +
> + bridge = dev_to_bridge(dev);
> + if (bridge == NULL) {
> + printk(KERN_ERR "Can't find VME bus\n");
> + return;
> + }
> +
> + if((level < 1) || (level > 7)) {
> + printk(KERN_WARNING "Invalid interrupt level\n");
> + return;
> + }
> +
> + if (bridge->free_irq == NULL) {
> + printk("Freeing interrupts not supported\n");
> + return;
> + }
> +
> + bridge->free_irq(level, statid);
> +}
> +EXPORT_SYMBOL(vme_free_irq);
> +
> +int vme_generate_irq(struct device *dev, int level, int statid)
> +{
> + struct vme_bridge *bridge;
> +
> + bridge = dev_to_bridge(dev);
> + if (bridge == NULL) {
> + printk(KERN_ERR "Can't find VME bus\n");
> + return -EINVAL;
> + }
> +
> + if((level < 1) || (level > 7)) {
> + printk(KERN_WARNING "Invalid interrupt level\n");
> + return -EINVAL;
> + }
> +
> + if (bridge->generate_irq == NULL) {
> + printk("Interrupt generation not supported\n");
> + return -EINVAL;
> + }
> +
> + return bridge->generate_irq(level, statid);
> +}
> +EXPORT_SYMBOL(vme_generate_irq);
> +
> +int vme_lm_set(struct device *dev, unsigned long long lm_base, vme_address_t aspace,
> + vme_cycle_t cycle)
> +{
> + struct vme_bridge *bridge;
> +
> + bridge = dev_to_bridge(dev);
> + if (bridge == NULL) {
> + printk(KERN_ERR "Can't find VME bus\n");
> + return -EINVAL;
> + }
> +
> + if (bridge->lm_set == NULL) {
> + printk("vme_lm_set not supported\n");
> + return -EINVAL;
> + }
> +
> + return bridge->lm_set(lm_base, aspace, cycle);
> +}
> +EXPORT_SYMBOL(vme_lm_set);
> +
> +int vme_lm_get(struct device *dev, unsigned long long *lm_base, vme_address_t *aspace,
> + vme_cycle_t *cycle)
> +{
> + struct vme_bridge *bridge;
> +
> + bridge = dev_to_bridge(dev);
> + if (bridge == NULL) {
> + printk(KERN_ERR "Can't find VME bus\n");
> + return -EINVAL;
> + }
> +
> + if (bridge->lm_get == NULL) {
> + printk("vme_lm_get not supported\n");
> + return -EINVAL;
> + }
> +
> + return bridge->lm_get(lm_base, aspace, cycle);
> +}
> +EXPORT_SYMBOL(vme_lm_get);
> +
> +int vme_lm_attach(struct device *dev, int monitor, void (*callback)(int))
> +{
> + struct vme_bridge *bridge;
> +
> + bridge = dev_to_bridge(dev);
> + if (bridge == NULL) {
> + printk(KERN_ERR "Can't find VME bus\n");
> + return -EINVAL;
> + }
> +
> + if (bridge->lm_attach == NULL) {
> + printk("vme_lm_attach not supported\n");
> + return -EINVAL;
> + }
> +
> + return bridge->lm_attach(monitor, callback);
> +}
> +EXPORT_SYMBOL(vme_lm_attach);
> +
> +int vme_lm_detach(struct device *dev, int monitor)
> +{
> + struct vme_bridge *bridge;
> +
> + bridge = dev_to_bridge(dev);
> + if (bridge == NULL) {
> + printk(KERN_ERR "Can't find VME bus\n");
> + return -EINVAL;
> + }
> +
> + if (bridge->lm_detach == NULL) {
> + printk("vme_lm_detach not supported\n");
> + return -EINVAL;
> + }
> +
> + return bridge->lm_detach(monitor);
> +}
> +EXPORT_SYMBOL(vme_lm_detach);
> +
> +int vme_slot_get(struct device *bus)
> +{
> + struct vme_bridge *bridge;
> +
> + bridge = dev_to_bridge(bus);
> + if (bridge == NULL) {
> + printk(KERN_ERR "Can't find VME bus\n");
> + return -EINVAL;
> + }
> +
> + if (bridge->slot_get == NULL) {
> + printk("vme_slot_get not supported\n");
> + return -EINVAL;
> + }
> +
> + return bridge->slot_get();
> +}
> +EXPORT_SYMBOL(vme_slot_get);
> +
> +
> +/* - Bridge Registration --------------------------------------------------- */
> +
> +static int vme_alloc_bus_num(void)
> +{
> + int i;
> +
> + down(&vme_bus_num_sem);
> + for (i = 0; i < sizeof(vme_bus_numbers) * 8; i++) {
> + if (((vme_bus_numbers >> i) & 0x1) == 0) {
> + vme_bus_numbers |= (0x1 << i);
> + break;
> + }
> + }
> + up(&vme_bus_num_sem);
> +
> + return i;
> +}
> +
> +static void vme_free_bus_num(int bus)
> +{
> + down(&vme_bus_num_sem);
> + vme_bus_numbers |= ~(0x1 << bus);
> + up(&vme_bus_num_sem);
> +}
> +
> +int vme_register_bridge (struct vme_bridge *bridge)
> +{
> + struct device *dev;
> + int retval;
> + int i;
> +
> + bridge->num = vme_alloc_bus_num();
> +
> + /* This creates 32 vme "slot" devices. This equates to a slot for each
> + * ID available in a system conforming to the ANSI/VITA 1-1994
> + * specification.
> + */
> + for (i = 0; i < VME_SLOTS_MAX; i++) {
> + dev = &(bridge->dev[i]);
> + memset(dev, 0, sizeof(struct device));
> +
> + dev->parent = bridge->parent;
> + dev->bus = &(vme_bus_type);
> + /*
> + * We save a pointer to the bridge in platform_data so that we
> + * can get to it later. We keep driver_data for use by the
> + * driver that binds against the slot
> + */
> + dev->platform_data = bridge;
> + dev_set_name(dev, "vme-%x.%x", bridge->num, i + 1);
> +
> + retval = device_register(dev);
> + if(retval)
> + goto err_reg;
> + }
> +
> + return retval;
> +
> + i = VME_SLOTS_MAX;
> +err_reg:
> + while (i > -1) {
> + dev = &(bridge->dev[i]);
> + device_unregister(dev);
> + }
> + vme_free_bus_num(bridge->num);
> + return retval;
> +}
> +EXPORT_SYMBOL(vme_register_bridge);
> +
> +void vme_unregister_bridge (struct vme_bridge *bridge)
> +{
> + int i;
> + struct device *dev;
> +
> +
> + for (i = 0; i < VME_SLOTS_MAX; i++) {
> + dev = &(bridge->dev[i]);
> + device_unregister(dev);
> + }
> + vme_free_bus_num(bridge->num);
> +}
> +EXPORT_SYMBOL(vme_unregister_bridge);
> +
> +
> +/* - Driver Registration --------------------------------------------------- */
> +
> +int vme_register_driver (struct vme_driver *drv)
> +{
> + drv->driver.name = drv->name;
> + drv->driver.bus = &vme_bus_type;
> +
> + return driver_register(&drv->driver);
> +}
> +EXPORT_SYMBOL(vme_register_driver);
> +
> +void vme_unregister_driver (struct vme_driver *drv)
> +{
> + driver_unregister(&drv->driver);
> +}
> +EXPORT_SYMBOL(vme_unregister_driver);
> +
> +/* - Bus Registration ------------------------------------------------------ */
> +
> +int vme_calc_slot(struct device *dev)
> +{
> + struct vme_bridge *bridge;
> + int num;
> +
> + bridge = dev_to_bridge(dev);
> +
> + /* Determine slot number */
> + num = 0;
> + while(num < VME_SLOTS_MAX) {
> + if(&(bridge->dev[num]) == dev) {
> + break;
> + }
> + num++;
> + }
> + if (num == VME_SLOTS_MAX) {
> + dev_err(dev, "Failed to identify slot\n");
> + num = 0;
> + goto err_dev;
> + }
> + num++;
> +
> +err_dev:
> + return num;
> +}
> +
> +static struct vme_driver *dev_to_vme_driver(struct device *dev)
> +{
> + if(dev->driver == NULL)
> + printk("Bugger dev->driver is NULL\n");
> +
> + return container_of(dev->driver, struct vme_driver, driver);
> +}
> +
> +static int vme_bus_match(struct device *dev, struct device_driver *drv)
> +{
> + struct vme_bridge *bridge;
> + struct vme_driver *driver;
> + int i, num;
> +
> + bridge = dev_to_bridge(dev);
> + driver = container_of(drv, struct vme_driver, driver);
> +
> + num = vme_calc_slot(dev);
> + if (!num)
> + goto err_dev;
> +
> + if (driver->bind_table == NULL) {
> + dev_err(dev, "Bind table NULL\n");
> + goto err_table;
> + }
> +
> + i = 0;
> + while((driver->bind_table[i].bus != 0) ||
> + (driver->bind_table[i].slot != 0)) {
> +
> + if ((bridge->num == driver->bind_table[i].bus) &&
> + (num == driver->bind_table[i].slot))
> + return 1;
> + i++;
> + }
> +
> +err_dev:
> +err_table:
> + return 0;
> +}
> +
> +static int vme_bus_probe(struct device *dev)
> +{
> + struct vme_bridge *bridge;
> + struct vme_driver *driver;
> + int retval = -ENODEV;
> +
> + driver = dev_to_vme_driver(dev);
> + bridge = dev_to_bridge(dev);
> +
> + if(driver->probe != NULL) {
> + retval = driver->probe(dev, bridge->num, vme_calc_slot(dev));
> + }
> +
> + return retval;
> +}
> +
> +static int vme_bus_remove(struct device *dev)
> +{
> + struct vme_bridge *bridge;
> + struct vme_driver *driver;
> + int retval = -ENODEV;
> +
> + driver = dev_to_vme_driver(dev);
> + bridge = dev_to_bridge(dev);
> +
> + if(driver->remove != NULL) {
> + retval = driver->remove(dev, bridge->num, vme_calc_slot(dev));
> + }
> +
> + return retval;
> +}
> +
> +struct bus_type vme_bus_type = {
> + .name = "vme",
> + .match = vme_bus_match,
> + .probe = vme_bus_probe,
> + .remove = vme_bus_remove,
> +};
> +EXPORT_SYMBOL(vme_bus_type);
> +
> +static int __init vme_init (void)
> +{
> + return bus_register(&vme_bus_type);
> +}
> +
> +static void __exit vme_exit (void)
> +{
> + bus_unregister(&vme_bus_type);
> +}
> +
> +MODULE_DESCRIPTION("VME bridge driver framework");
> +MODULE_AUTHOR("Martyn Welch <martyn.welch@xxxxxxxxxxx");
> +MODULE_LICENSE("GPL");
> +
> +module_init(vme_init);
> +module_exit(vme_exit);
> --- /dev/null
> +++ b/drivers/staging/vme/vme.h
> @@ -0,0 +1,153 @@
> +#ifndef _VME_H_
> +#define _VME_H_
> +
> +/* Resource Type */
> +enum vme_resource_type {
> + VME_MASTER,
> + VME_SLAVE,
> + VME_DMA
> +};
> +
> +/* VME Address Spaces */
> +typedef u32 vme_address_t;
> +#define VME_A16 0x1
> +#define VME_A24 0x2
> +#define VME_A32 0x4
> +#define VME_A64 0x8
> +#define VME_CRCSR 0x10
> +#define VME_USER1 0x20
> +#define VME_USER2 0x40
> +#define VME_USER3 0x80
> +#define VME_USER4 0x100
> +
> +#define VME_A16_MAX 0x10000ULL
> +#define VME_A24_MAX 0x1000000ULL
> +#define VME_A32_MAX 0x100000000ULL
> +#define VME_A64_MAX 0x10000000000000000ULL
> +#define VME_CRCSR_MAX 0x1000000ULL
> +
> +
> +/* VME Cycle Types */
> +typedef u32 vme_cycle_t;
> +#define VME_SCT 0x1
> +#define VME_BLT 0x2
> +#define VME_MBLT 0x4
> +#define VME_2eVME 0x8
> +#define VME_2eSST 0x10
> +#define VME_2eSSTB 0x20
> +
> +#define VME_2eSST160 0x100
> +#define VME_2eSST267 0x200
> +#define VME_2eSST320 0x400
> +
> +#define VME_SUPER 0x1000
> +#define VME_USER 0x2000
> +#define VME_PROG 0x4000
> +#define VME_DATA 0x8000
> +
> +/* VME Data Widths */
> +typedef u32 vme_width_t;
> +#define VME_D8 0x1
> +#define VME_D16 0x2
> +#define VME_D32 0x4
> +#define VME_D64 0x8
> +
> +/* Arbitration Scheduling Modes */
> +typedef u32 vme_arbitration_t;
> +#define VME_R_ROBIN_MODE 0x1
> +#define VME_PRIORITY_MODE 0x2
> +
> +typedef u32 vme_dma_t;
> +#define VME_DMA_PATTERN (1<<0)
> +#define VME_DMA_PCI (1<<1)
> +#define VME_DMA_VME (1<<2)
> +
> +typedef u32 vme_pattern_t;
> +#define VME_DMA_PATTERN_BYTE (1<<0)
> +#define VME_DMA_PATTERN_WORD (1<<1)
> +#define VME_DMA_PATTERN_INCREMENT (1<<2)
> +
> +struct vme_dma_attr {
> + vme_dma_t type;
> + void *private;
> +};
> +
> +struct vme_resource {
> + enum vme_resource_type type;
> + struct list_head *entry;
> +};
> +
> +extern struct bus_type vme_bus_type;
> +
> +struct vme_device_id {
> + int bus;
> + int slot;
> +};
> +
> +struct vme_driver {
> + struct list_head node;
> + char *name;
> + const struct vme_device_id *bind_table;
> + int (*probe) (struct device *, int, int);
> + int (*remove) (struct device *, int, int);
> + void (*shutdown) (void);
> + struct device_driver driver;
> +};
> +
> +void * vme_alloc_consistent(struct vme_resource *, size_t, dma_addr_t *);
> +void vme_free_consistent(struct vme_resource *, size_t, void *,
> + dma_addr_t);
> +
> +size_t vme_get_size(struct vme_resource *);
> +
> +struct vme_resource * vme_slave_request(struct device *, vme_address_t, vme_cycle_t);
> +int vme_slave_set (struct vme_resource *, int, unsigned long long,
> + unsigned long long, dma_addr_t, vme_address_t, vme_cycle_t);
> +int vme_slave_get (struct vme_resource *, int *, unsigned long long *,
> + unsigned long long *, dma_addr_t *, vme_address_t *, vme_cycle_t *);
> +void vme_slave_free(struct vme_resource *);
> +
> +struct vme_resource * vme_master_request(struct device *, vme_address_t, vme_cycle_t,
> + vme_width_t);
> +int vme_master_set (struct vme_resource *, int, unsigned long long,
> + unsigned long long, vme_address_t, vme_cycle_t, vme_width_t);
> +int vme_master_get (struct vme_resource *, int *, unsigned long long *,
> + unsigned long long *, vme_address_t *, vme_cycle_t *, vme_width_t *);
> +ssize_t vme_master_read(struct vme_resource *, void *, size_t, loff_t);
> +ssize_t vme_master_write(struct vme_resource *, void *, size_t, loff_t);
> +unsigned int vme_master_rmw (struct vme_resource *, unsigned int, unsigned int,
> + unsigned int, loff_t);
> +void vme_master_free(struct vme_resource *);
> +
> +struct vme_resource *vme_request_dma(struct device *);
> +struct vme_dma_list *vme_new_dma_list(struct vme_resource *);
> +struct vme_dma_attr *vme_dma_pattern_attribute(u32, vme_pattern_t);
> +struct vme_dma_attr *vme_dma_pci_attribute(dma_addr_t);
> +struct vme_dma_attr *vme_dma_vme_attribute(unsigned long long, vme_address_t,
> + vme_cycle_t, vme_width_t);
> +void vme_dma_free_attribute(struct vme_dma_attr *);
> +int vme_dma_list_add(struct vme_dma_list *, struct vme_dma_attr *,
> + struct vme_dma_attr *, size_t);
> +int vme_dma_list_exec(struct vme_dma_list *);
> +int vme_dma_list_free(struct vme_dma_list *);
> +int vme_dma_free(struct vme_resource *);
> +
> +int vme_request_irq(struct device *, int, int,
> + void (*callback)(int, int, void *), void *);
> +void vme_free_irq(struct device *, int, int);
> +int vme_generate_irq(struct device *, int, int);
> +
> +int vme_lm_set(struct device *, unsigned long long, vme_address_t, vme_cycle_t);
> +int vme_lm_get(struct device *, unsigned long long *, vme_address_t *,
> + vme_cycle_t *);
> +int vme_lm_attach(struct device *, int, void (*callback)(int));
> +int vme_lm_detach(struct device *, int);
> +
> +int vme_slot_get(struct device *);
> +
> +int vme_register_driver (struct vme_driver *);
> +void vme_unregister_driver (struct vme_driver *);
> +
> +
> +#endif /* _VME_H_ */
> +
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/