Location Monitor Management
---------------------------
-TODO: Provide a mechanism to request use of the location monitor. The location
- monitors can be moved and we only want one driver to be able to do that
- at a time! We also need to be able to free the location monitor for
- others to use.
+The following functions are provided to request the use of a block of location
+monitors and to free them after they are no longer required:
- struct vme_resource * vme_request_lm(struct device *dev);
+ struct vme_resource * vme_lm_request(struct device *dev);
- void vme_free_lm(struct vme_resource * res);
+ void vme_lm_free(struct vme_resource * res);
+
+Each block may provide a number of location monitors, monitoring adjacent
+locations. The following function can be used to determine how many locations
+are provided:
+
+ int vme_lm_count(struct vme_resource * res);
Location Monitor Configuration
------------------------------
-TODO: Change to struct "vme_resource *res" rather than "struct device *dev".
+Once a bank of location monitors has been allocated, the following functions
+are provided to configure the location and mode of the location monitor:
-The following functions are provided to configure the location and mode of the
-location monitor:
-
- int vme_lm_set(struct device *dev, unsigned long long base,
+ int vme_lm_set(struct vme_resource *res, unsigned long long base,
vme_address_t aspace, vme_cycle_t cycle);
- int vme_lm_get(struct device *dev, unsigned long long *base,
+ int vme_lm_get(struct vme_resource *res, unsigned long long *base,
vme_address_t *aspace, vme_cycle_t *cycle);
Location Monitor Use
--------------------
-TODO: Change to struct "vme_resource *res" rather than "struct device *dev".
-
The following functions allow a callback to be attached and detached from each
-location monitor location. The API currently supports 4 location monitors,
-monitoring 4 adjacent locations:
+location monitor location. Each location monitor can monitor a number of
+adjacent locations:
- int vme_lm_attach(struct device *dev, int num,
+ int vme_lm_attach(struct vme_resource *res, int num,
void (*callback)(int));
- int vme_lm_detach(struct device *dev, int num);
+ int vme_lm_detach(struct vme_resource *res, int num);
The callback function is declared as follows.
int tsi148_dma_list_exec(struct vme_dma_list *);
int tsi148_dma_list_empty(struct vme_dma_list *);
int tsi148_generate_irq(int, int);
-int tsi148_lm_set(unsigned long long, vme_address_t, vme_cycle_t);
-int tsi148_lm_get(unsigned long long *, vme_address_t *, vme_cycle_t *);
-int tsi148_lm_attach(int, void (*callback)(int));
-int tsi148_lm_detach(int);
int tsi148_slot_get(void);
/* Modue parameter */
* generated at a time, provide locking
*/
struct mutex vme_irq; /* Locking for VME irq callback configuration */
-struct mutex vme_lm; /* Locking for location monitor operations */
static char driver_name[] = "vme_tsi148";
* This does not enable the LM monitor - that should be done when the first
* callback is attached and disabled when the last callback is removed.
*/
-int tsi148_lm_set(unsigned long long lm_base, vme_address_t aspace,
- vme_cycle_t cycle)
+int tsi148_lm_set(struct vme_lm_resource *lm, unsigned long long lm_base,
+ vme_address_t aspace, vme_cycle_t cycle)
{
u32 lm_base_high, lm_base_low, lm_ctl = 0;
int i;
- mutex_lock(&(vme_lm));
+ mutex_lock(&(lm->mtx));
/* If we already have a callback attached, we can't move it! */
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < lm->monitors; i++) {
if(lm_callback[i] != NULL) {
- mutex_unlock(&(vme_lm));
+ mutex_unlock(&(lm->mtx));
printk("Location monitor callback attached, can't "
"reset\n");
return -EBUSY;
lm_ctl |= TSI148_LCSR_LMAT_AS_A64;
break;
default:
- mutex_unlock(&(vme_lm));
+ mutex_unlock(&(lm->mtx));
printk("Invalid address space\n");
return -EINVAL;
break;
iowrite32be(lm_base_low, tsi148_bridge->base + TSI148_LCSR_LMBAL);
iowrite32be(lm_ctl, tsi148_bridge->base + TSI148_LCSR_LMAT);
- mutex_unlock(&(vme_lm));
+ mutex_unlock(&(lm->mtx));
return 0;
}
/* Get configuration of the callback monitor and return whether it is enabled
* or disabled.
*/
-int tsi148_lm_get(unsigned long long *lm_base, vme_address_t *aspace,
- vme_cycle_t *cycle)
+int tsi148_lm_get(struct vme_lm_resource *lm, unsigned long long *lm_base,
+ vme_address_t *aspace, vme_cycle_t *cycle)
{
u32 lm_base_high, lm_base_low, lm_ctl, enabled = 0;
- mutex_lock(&(vme_lm));
+ mutex_lock(&(lm->mtx));
lm_base_high = ioread32be(tsi148_bridge->base + TSI148_LCSR_LMBAU);
lm_base_low = ioread32be(tsi148_bridge->base + TSI148_LCSR_LMBAL);
if (lm_ctl & TSI148_LCSR_LMAT_DATA)
*cycle |= VME_DATA;
- mutex_unlock(&(vme_lm));
+ mutex_unlock(&(lm->mtx));
return enabled;
}
*
* Callback will be passed the monitor triggered.
*/
-int tsi148_lm_attach(int monitor, void (*callback)(int))
+int tsi148_lm_attach(struct vme_lm_resource *lm, int monitor,
+ void (*callback)(int))
{
u32 lm_ctl, tmp;
- mutex_lock(&(vme_lm));
+ mutex_lock(&(lm->mtx));
/* Ensure that the location monitor is configured - need PGM or DATA */
lm_ctl = ioread32be(tsi148_bridge->base + TSI148_LCSR_LMAT);
if ((lm_ctl & (TSI148_LCSR_LMAT_PGM | TSI148_LCSR_LMAT_DATA)) == 0) {
- mutex_unlock(&(vme_lm));
+ mutex_unlock(&(lm->mtx));
printk("Location monitor not properly configured\n");
return -EINVAL;
}
/* Check that a callback isn't already attached */
if (lm_callback[monitor] != NULL) {
- mutex_unlock(&(vme_lm));
+ mutex_unlock(&(lm->mtx));
printk("Existing callback attached\n");
return -EBUSY;
}
iowrite32be(lm_ctl, tsi148_bridge->base + TSI148_LCSR_LMAT);
}
- mutex_unlock(&(vme_lm));
+ mutex_unlock(&(lm->mtx));
return 0;
}
/*
* Detach a callback function forn a specific location monitor.
*/
-int tsi148_lm_detach(int monitor)
+int tsi148_lm_detach(struct vme_lm_resource *lm, int monitor)
{
u32 lm_en, tmp;
- mutex_lock(&(vme_lm));
+ mutex_lock(&(lm->mtx));
/* Disable Location Monitor and ensure previous interrupts are clear */
lm_en = ioread32be(tsi148_bridge->base + TSI148_LCSR_INTEN);
iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_LMAT);
}
- mutex_unlock(&(vme_lm));
+ mutex_unlock(&(lm->mtx));
return 0;
}
struct vme_master_resource *master_image;
struct vme_slave_resource *slave_image;
struct vme_dma_resource *dma_ctrlr;
+ struct vme_lm_resource *lm;
/* If we want to support more than one of each bridge, we need to
* dynamically generate this so we get one per device
mutex_init(&(vme_int));
mutex_init(&(vme_irq));
mutex_init(&(vme_rmw));
- mutex_init(&(vme_lm));
tsi148_bridge->parent = &(pdev->dev);
strcpy(tsi148_bridge->name, driver_name);
&(tsi148_bridge->dma_resources));
}
+ /* Add location monitor to list */
+ INIT_LIST_HEAD(&(tsi148_bridge->lm_resources));
+ lm = kmalloc(sizeof(struct vme_lm_resource), GFP_KERNEL);
+ if (lm == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate memory for "
+ "location monitor resource structure\n");
+ retval = -ENOMEM;
+ goto err_lm;
+ }
+ lm->parent = tsi148_bridge;
+ mutex_init(&(lm->mtx));
+ lm->locked = 0;
+ lm->number = 1;
+ lm->monitors = 4;
+ list_add_tail(&(lm->list), &(tsi148_bridge->lm_resources));
+
tsi148_bridge->slave_get = tsi148_slave_get;
tsi148_bridge->slave_set = tsi148_slave_set;
tsi148_bridge->master_get = tsi148_master_get;
err_reg:
tsi148_crcsr_exit(pdev);
err_crcsr:
+err_lm:
+ /* resources are stored in link list */
+ list_for_each(pos, &(tsi148_bridge->lm_resources)) {
+ lm = list_entry(pos, struct vme_lm_resource, list);
+ list_del(pos);
+ kfree(lm);
+ }
err_dma:
/* resources are stored in link list */
list_for_each(pos, &(tsi148_bridge->dma_resources)) {
return list_entry(resource->entry, struct vme_dma_resource,
list)->parent;
break;
+ case VME_LM:
+ return list_entry(resource->entry, struct vme_lm_resource,
+ list)->parent;
+ break;
default:
printk(KERN_ERR "Unknown resource type\n");
return NULL;
}
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)
+/*
+ * Request the location monitor, return resource or NULL
+ */
+struct vme_resource *vme_lm_request(struct device *dev)
{
struct vme_bridge *bridge;
+ struct list_head *lm_pos = NULL;
+ struct vme_lm_resource *allocated_lm = NULL;
+ struct vme_lm_resource *lm = 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 DMA resources */
+ list_for_each(lm_pos, &(bridge->lm_resources)) {
+ lm = list_entry(lm_pos,
+ struct vme_lm_resource, list);
+
+ if (lm == NULL) {
+ printk(KERN_ERR "Registered NULL Location Monitor "
+ "resource\n");
+ continue;
+ }
+
+ /* Find an unlocked controller */
+ mutex_lock(&(lm->mtx));
+ if (lm->locked == 0) {
+ lm->locked = 1;
+ mutex_unlock(&(lm->mtx));
+ allocated_lm = lm;
+ break;
+ }
+ mutex_unlock(&(lm->mtx));
+ }
+
+ /* Check to see if we found a resource */
+ if (allocated_lm == NULL)
+ goto err_lm;
+
+ resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL);
+ if (resource == NULL) {
+ printk(KERN_ERR "Unable to allocate resource structure\n");
+ goto err_alloc;
+ }
+ resource->type = VME_LM;
+ resource->entry = &(allocated_lm->list);
+
+ return resource;
+
+err_alloc:
+ /* Unlock image */
+ mutex_lock(&(lm->mtx));
+ lm->locked = 0;
+ mutex_unlock(&(lm->mtx));
+err_lm:
+err_bus:
+ return NULL;
+}
+EXPORT_SYMBOL(vme_lm_request);
+
+int vme_lm_count(struct vme_resource *resource)
+{
+ struct vme_lm_resource *lm;
+
+ if (resource->type != VME_LM) {
+ printk(KERN_ERR "Not a Location Monitor resource\n");
+ return -EINVAL;
+ }
+
+ lm = list_entry(resource->entry, struct vme_lm_resource, list);
+
+ return lm->monitors;
+}
+EXPORT_SYMBOL(vme_lm_count);
+
+int vme_lm_set(struct vme_resource *resource, unsigned long long lm_base,
+ vme_address_t aspace, vme_cycle_t cycle)
+{
+ struct vme_bridge *bridge = find_bridge(resource);
+ struct vme_lm_resource *lm;
+
+ if (resource->type != VME_LM) {
+ printk(KERN_ERR "Not a Location Monitor resource\n");
return -EINVAL;
}
+ lm = list_entry(resource->entry, struct vme_lm_resource, list);
+
if (bridge->lm_set == NULL) {
- printk("vme_lm_set not supported\n");
+ printk(KERN_ERR "vme_lm_set not supported\n");
return -EINVAL;
}
- return bridge->lm_set(lm_base, aspace, cycle);
+ /* XXX Check parameters */
+
+ return lm->parent->lm_set(lm, 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)
+int vme_lm_get(struct vme_resource *resource, unsigned long long *lm_base,
+ vme_address_t *aspace, vme_cycle_t *cycle)
{
- struct vme_bridge *bridge;
+ struct vme_bridge *bridge = find_bridge(resource);
+ struct vme_lm_resource *lm;
- bridge = dev_to_bridge(dev);
- if (bridge == NULL) {
- printk(KERN_ERR "Can't find VME bus\n");
+ if (resource->type != VME_LM) {
+ printk(KERN_ERR "Not a Location Monitor resource\n");
return -EINVAL;
}
+ lm = list_entry(resource->entry, struct vme_lm_resource, list);
+
if (bridge->lm_get == NULL) {
- printk("vme_lm_get not supported\n");
+ printk(KERN_ERR "vme_lm_get not supported\n");
return -EINVAL;
}
- return bridge->lm_get(lm_base, aspace, cycle);
+ return bridge->lm_get(lm, lm_base, aspace, cycle);
}
EXPORT_SYMBOL(vme_lm_get);
-int vme_lm_attach(struct device *dev, int monitor, void (*callback)(int))
+int vme_lm_attach(struct vme_resource *resource, int monitor,
+ void (*callback)(int))
{
- struct vme_bridge *bridge;
+ struct vme_bridge *bridge = find_bridge(resource);
+ struct vme_lm_resource *lm;
- bridge = dev_to_bridge(dev);
- if (bridge == NULL) {
- printk(KERN_ERR "Can't find VME bus\n");
+ if (resource->type != VME_LM) {
+ printk(KERN_ERR "Not a Location Monitor resource\n");
return -EINVAL;
}
+ lm = list_entry(resource->entry, struct vme_lm_resource, list);
+
if (bridge->lm_attach == NULL) {
- printk("vme_lm_attach not supported\n");
+ printk(KERN_ERR "vme_lm_attach not supported\n");
return -EINVAL;
}
- return bridge->lm_attach(monitor, callback);
+ return bridge->lm_attach(lm, monitor, callback);
}
EXPORT_SYMBOL(vme_lm_attach);
-int vme_lm_detach(struct device *dev, int monitor)
+int vme_lm_detach(struct vme_resource *resource, int monitor)
{
- struct vme_bridge *bridge;
+ struct vme_bridge *bridge = find_bridge(resource);
+ struct vme_lm_resource *lm;
- bridge = dev_to_bridge(dev);
- if (bridge == NULL) {
- printk(KERN_ERR "Can't find VME bus\n");
+ if (resource->type != VME_LM) {
+ printk(KERN_ERR "Not a Location Monitor resource\n");
return -EINVAL;
}
+ lm = list_entry(resource->entry, struct vme_lm_resource, list);
+
if (bridge->lm_detach == NULL) {
- printk("vme_lm_detach not supported\n");
+ printk(KERN_ERR "vme_lm_detach not supported\n");
return -EINVAL;
}
- return bridge->lm_detach(monitor);
+ return bridge->lm_detach(lm, monitor);
}
EXPORT_SYMBOL(vme_lm_detach);
+void vme_lm_free(struct vme_resource *resource)
+{
+ struct vme_lm_resource *lm;
+
+ if (resource->type != VME_LM) {
+ printk(KERN_ERR "Not a Location Monitor resource\n");
+ return;
+ }
+
+ lm = list_entry(resource->entry, struct vme_lm_resource, list);
+
+ if (mutex_trylock(&(lm->mtx))) {
+ printk(KERN_ERR "Resource busy, can't free\n");
+ return;
+ }
+
+ /* XXX Check to see that there aren't any callbacks still attached */
+
+ lm->locked = 0;
+
+ mutex_unlock(&(lm->mtx));
+}
+EXPORT_SYMBOL(vme_lm_free);
+
int vme_slot_get(struct device *bus)
{
struct vme_bridge *bridge;
enum vme_resource_type {
VME_MASTER,
VME_SLAVE,
- VME_DMA
+ VME_DMA,
+ VME_LM
};
/* VME Address Spaces */
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 *,
+struct vme_resource * vme_lm_request(struct device *);
+int vme_lm_count(struct vme_resource *);
+int vme_lm_set(struct vme_resource *, unsigned long long, vme_address_t,
+ vme_cycle_t);
+int vme_lm_get(struct vme_resource *, 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_lm_attach(struct vme_resource *, int, void (*callback)(int));
+int vme_lm_detach(struct vme_resource *, int);
+void vme_lm_free(struct vme_resource *);
int vme_slot_get(struct device *);
struct list_head running;
};
+struct vme_lm_resource {
+ struct list_head list;
+ struct vme_bridge *parent;
+ struct mutex mtx;
+ int locked;
+ int number;
+ int monitors;
+};
+
struct vme_bus_error {
struct list_head list;
unsigned long long address;
struct list_head master_resources;
struct list_head slave_resources;
struct list_head dma_resources;
+ struct list_head lm_resources;
struct list_head vme_errors; /* List for errors generated on VME */
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);
+ int (*lm_set) (struct vme_lm_resource *, unsigned long long,
+ vme_address_t, vme_cycle_t);
+ int (*lm_get) (struct vme_lm_resource *, unsigned long long *,
+ vme_address_t *, vme_cycle_t *);
+ int (*lm_attach) (struct vme_lm_resource *, int, void (*callback)(int));
+ int (*lm_detach) (struct vme_lm_resource *, int);
/* CR/CSR space functions */
int (*slot_get) (void);