From: Martyn Welch Date: Tue, 11 Aug 2009 16:44:56 +0000 (+0100) Subject: Staging: vme: add VME Location Monitor management mechanism X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=42fb503122d8cd428b5b1078bd473847ca2b206c;p=linux-beck.git Staging: vme: add VME Location Monitor management mechanism Extend the image and DMA channel resource management methods to control the location monitor resource. The location monitor should be controlled as it can only be used at a single location at a time. Signed-off-by: Martyn Welch Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/staging/vme/TODO b/drivers/staging/vme/TODO index 046cf5815998..1e1f4d65976c 100644 --- a/drivers/staging/vme/TODO +++ b/drivers/staging/vme/TODO @@ -331,44 +331,44 @@ monitor. 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. diff --git a/drivers/staging/vme/bridges/vme_tsi148.c b/drivers/staging/vme/bridges/vme_tsi148.c index cc4955ac1256..8960fa9ee7aa 100644 --- a/drivers/staging/vme/bridges/vme_tsi148.c +++ b/drivers/staging/vme/bridges/vme_tsi148.c @@ -59,10 +59,6 @@ int tsi148_dma_list_add (struct vme_dma_list *, struct vme_dma_attr *, 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 */ @@ -82,7 +78,6 @@ struct mutex vme_int; /* * 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"; @@ -1985,18 +1980,18 @@ int tsi148_dma_list_empty(struct vme_dma_list *list) * 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; @@ -2017,7 +2012,7 @@ int tsi148_lm_set(unsigned long long lm_base, vme_address_t aspace, 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; @@ -2038,7 +2033,7 @@ int tsi148_lm_set(unsigned long long lm_base, vme_address_t aspace, 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; } @@ -2046,12 +2041,12 @@ int tsi148_lm_set(unsigned long long lm_base, vme_address_t aspace, /* 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); @@ -2084,7 +2079,7 @@ int tsi148_lm_get(unsigned long long *lm_base, vme_address_t *aspace, if (lm_ctl & TSI148_LCSR_LMAT_DATA) *cycle |= VME_DATA; - mutex_unlock(&(vme_lm)); + mutex_unlock(&(lm->mtx)); return enabled; } @@ -2094,23 +2089,24 @@ int tsi148_lm_get(unsigned long long *lm_base, vme_address_t *aspace, * * 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; } @@ -2133,7 +2129,7 @@ int tsi148_lm_attach(int monitor, void (*callback)(int)) iowrite32be(lm_ctl, tsi148_bridge->base + TSI148_LCSR_LMAT); } - mutex_unlock(&(vme_lm)); + mutex_unlock(&(lm->mtx)); return 0; } @@ -2141,11 +2137,11 @@ int tsi148_lm_attach(int monitor, void (*callback)(int)) /* * 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); @@ -2170,7 +2166,7 @@ int tsi148_lm_detach(int monitor) iowrite32be(tmp, tsi148_bridge->base + TSI148_LCSR_LMAT); } - mutex_unlock(&(vme_lm)); + mutex_unlock(&(lm->mtx)); return 0; } @@ -2285,6 +2281,7 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id) 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 @@ -2338,7 +2335,6 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id) 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); @@ -2459,6 +2455,22 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id) &(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; @@ -2513,6 +2525,13 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id) 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)) { diff --git a/drivers/staging/vme/vme.c b/drivers/staging/vme/vme.c index 07b254a65353..477a1adfd0e9 100644 --- a/drivers/staging/vme/vme.c +++ b/drivers/staging/vme/vme.c @@ -69,6 +69,10 @@ static struct vme_bridge *find_bridge(struct vme_resource *resource) 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; @@ -1045,84 +1049,198 @@ int vme_generate_irq(struct device *dev, int level, int 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) +/* + * 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; diff --git a/drivers/staging/vme/vme.h b/drivers/staging/vme/vme.h index a092138e3a73..6206e91d1992 100644 --- a/drivers/staging/vme/vme.h +++ b/drivers/staging/vme/vme.h @@ -5,7 +5,8 @@ enum vme_resource_type { VME_MASTER, VME_SLAVE, - VME_DMA + VME_DMA, + VME_LM }; /* VME Address Spaces */ @@ -140,11 +141,15 @@ int vme_request_irq(struct device *, int, int, 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 *); diff --git a/drivers/staging/vme/vme_bridge.h b/drivers/staging/vme/vme_bridge.h index b6f694adff5d..e43cc19103b3 100644 --- a/drivers/staging/vme/vme_bridge.h +++ b/drivers/staging/vme/vme_bridge.h @@ -66,6 +66,15 @@ struct vme_dma_resource { 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; @@ -97,6 +106,7 @@ struct vme_bridge { 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 */ @@ -144,10 +154,12 @@ struct vme_bridge { 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);