diff options
Diffstat (limited to 'drivers/vme/bridges')
-rw-r--r-- | drivers/vme/bridges/Kconfig | 15 | ||||
-rw-r--r-- | drivers/vme/bridges/Makefile | 2 | ||||
-rw-r--r-- | drivers/vme/bridges/vme_ca91cx42.c | 1959 | ||||
-rw-r--r-- | drivers/vme/bridges/vme_ca91cx42.h | 583 | ||||
-rw-r--r-- | drivers/vme/bridges/vme_tsi148.c | 2691 | ||||
-rw-r--r-- | drivers/vme/bridges/vme_tsi148.h | 1410 |
6 files changed, 6660 insertions, 0 deletions
diff --git a/drivers/vme/bridges/Kconfig b/drivers/vme/bridges/Kconfig new file mode 100644 index 00000000000..9331064e047 --- /dev/null +++ b/drivers/vme/bridges/Kconfig @@ -0,0 +1,15 @@ +comment "VME Bridge Drivers" + +config VME_CA91CX42 + tristate "Universe II" + depends on VIRT_TO_BUS + help + If you say Y here you get support for the Tundra CA91C142 + (Universe II) VME bridge chip. + +config VME_TSI148 + tristate "Tempe" + depends on VIRT_TO_BUS + help + If you say Y here you get support for the Tundra TSI148 VME bridge + chip. diff --git a/drivers/vme/bridges/Makefile b/drivers/vme/bridges/Makefile new file mode 100644 index 00000000000..59638afcd50 --- /dev/null +++ b/drivers/vme/bridges/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_VME_CA91CX42) += vme_ca91cx42.o +obj-$(CONFIG_VME_TSI148) += vme_tsi148.o diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c new file mode 100644 index 00000000000..a3c0f84e2fa --- /dev/null +++ b/drivers/vme/bridges/vme_ca91cx42.c @@ -0,0 +1,1959 @@ +/* + * Support for the Tundra Universe I/II VME-PCI Bridge Chips + * + * Author: Martyn Welch <martyn.welch@ge.com> + * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. + * + * Based on work by Tom Armistead and Ajit Prem + * Copyright 2004 Motorola Inc. + * + * Derived from ca91c042.c by Michael Wyrick + * + * 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/module.h> +#include <linux/mm.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/poll.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/io.h> +#include <linux/uaccess.h> +#include <linux/vme.h> + +#include "../vme_bridge.h" +#include "vme_ca91cx42.h" + +static int __init ca91cx42_init(void); +static int ca91cx42_probe(struct pci_dev *, const struct pci_device_id *); +static void ca91cx42_remove(struct pci_dev *); +static void __exit ca91cx42_exit(void); + +/* Module parameters */ +static int geoid; + +static const char driver_name[] = "vme_ca91cx42"; + +static DEFINE_PCI_DEVICE_TABLE(ca91cx42_ids) = { + { PCI_DEVICE(PCI_VENDOR_ID_TUNDRA, PCI_DEVICE_ID_TUNDRA_CA91C142) }, + { }, +}; + +static struct pci_driver ca91cx42_driver = { + .name = driver_name, + .id_table = ca91cx42_ids, + .probe = ca91cx42_probe, + .remove = ca91cx42_remove, +}; + +static u32 ca91cx42_DMA_irqhandler(struct ca91cx42_driver *bridge) +{ + wake_up(&bridge->dma_queue); + + return CA91CX42_LINT_DMA; +} + +static u32 ca91cx42_LM_irqhandler(struct ca91cx42_driver *bridge, u32 stat) +{ + int i; + u32 serviced = 0; + + for (i = 0; i < 4; i++) { + if (stat & CA91CX42_LINT_LM[i]) { + /* We only enable interrupts if the callback is set */ + bridge->lm_callback[i](i); + serviced |= CA91CX42_LINT_LM[i]; + } + } + + return serviced; +} + +/* XXX This needs to be split into 4 queues */ +static u32 ca91cx42_MB_irqhandler(struct ca91cx42_driver *bridge, int mbox_mask) +{ + wake_up(&bridge->mbox_queue); + + return CA91CX42_LINT_MBOX; +} + +static u32 ca91cx42_IACK_irqhandler(struct ca91cx42_driver *bridge) +{ + wake_up(&bridge->iack_queue); + + return CA91CX42_LINT_SW_IACK; +} + +static u32 ca91cx42_VERR_irqhandler(struct vme_bridge *ca91cx42_bridge) +{ + int val; + struct ca91cx42_driver *bridge; + + bridge = ca91cx42_bridge->driver_priv; + + val = ioread32(bridge->base + DGCS); + + if (!(val & 0x00000800)) { + dev_err(ca91cx42_bridge->parent, "ca91cx42_VERR_irqhandler DMA " + "Read Error DGCS=%08X\n", val); + } + + return CA91CX42_LINT_VERR; +} + +static u32 ca91cx42_LERR_irqhandler(struct vme_bridge *ca91cx42_bridge) +{ + int val; + struct ca91cx42_driver *bridge; + + bridge = ca91cx42_bridge->driver_priv; + + val = ioread32(bridge->base + DGCS); + + if (!(val & 0x00000800)) + dev_err(ca91cx42_bridge->parent, "ca91cx42_LERR_irqhandler DMA " + "Read Error DGCS=%08X\n", val); + + return CA91CX42_LINT_LERR; +} + + +static u32 ca91cx42_VIRQ_irqhandler(struct vme_bridge *ca91cx42_bridge, + int stat) +{ + int vec, i, serviced = 0; + struct ca91cx42_driver *bridge; + + bridge = ca91cx42_bridge->driver_priv; + + + for (i = 7; i > 0; i--) { + if (stat & (1 << i)) { + vec = ioread32(bridge->base + + CA91CX42_V_STATID[i]) & 0xff; + + vme_irq_handler(ca91cx42_bridge, i, vec); + + serviced |= (1 << i); + } + } + + return serviced; +} + +static irqreturn_t ca91cx42_irqhandler(int irq, void *ptr) +{ + u32 stat, enable, serviced = 0; + struct vme_bridge *ca91cx42_bridge; + struct ca91cx42_driver *bridge; + + ca91cx42_bridge = ptr; + + bridge = ca91cx42_bridge->driver_priv; + + enable = ioread32(bridge->base + LINT_EN); + stat = ioread32(bridge->base + LINT_STAT); + + /* Only look at unmasked interrupts */ + stat &= enable; + + if (unlikely(!stat)) + return IRQ_NONE; + + if (stat & CA91CX42_LINT_DMA) + serviced |= ca91cx42_DMA_irqhandler(bridge); + if (stat & (CA91CX42_LINT_LM0 | CA91CX42_LINT_LM1 | CA91CX42_LINT_LM2 | + CA91CX42_LINT_LM3)) + serviced |= ca91cx42_LM_irqhandler(bridge, stat); + if (stat & CA91CX42_LINT_MBOX) + serviced |= ca91cx42_MB_irqhandler(bridge, stat); + if (stat & CA91CX42_LINT_SW_IACK) + serviced |= ca91cx42_IACK_irqhandler(bridge); + if (stat & CA91CX42_LINT_VERR) + serviced |= ca91cx42_VERR_irqhandler(ca91cx42_bridge); + if (stat & CA91CX42_LINT_LERR) + serviced |= ca91cx42_LERR_irqhandler(ca91cx42_bridge); + if (stat & (CA91CX42_LINT_VIRQ1 | CA91CX42_LINT_VIRQ2 | + CA91CX42_LINT_VIRQ3 | CA91CX42_LINT_VIRQ4 | + CA91CX42_LINT_VIRQ5 | CA91CX42_LINT_VIRQ6 | + CA91CX42_LINT_VIRQ7)) + serviced |= ca91cx42_VIRQ_irqhandler(ca91cx42_bridge, stat); + + /* Clear serviced interrupts */ + iowrite32(serviced, bridge->base + LINT_STAT); + + return IRQ_HANDLED; +} + +static int ca91cx42_irq_init(struct vme_bridge *ca91cx42_bridge) +{ + int result, tmp; + struct pci_dev *pdev; + struct ca91cx42_driver *bridge; + + bridge = ca91cx42_bridge->driver_priv; + + /* Need pdev */ + pdev = container_of(ca91cx42_bridge->parent, struct pci_dev, dev); + + /* Initialise list for VME bus errors */ + INIT_LIST_HEAD(&ca91cx42_bridge->vme_errors); + + mutex_init(&ca91cx42_bridge->irq_mtx); + + /* Disable interrupts from PCI to VME */ + iowrite32(0, bridge->base + VINT_EN); + + /* Disable PCI interrupts */ + iowrite32(0, bridge->base + LINT_EN); + /* Clear Any Pending PCI Interrupts */ + iowrite32(0x00FFFFFF, bridge->base + LINT_STAT); + + result = request_irq(pdev->irq, ca91cx42_irqhandler, IRQF_SHARED, + driver_name, ca91cx42_bridge); + if (result) { + dev_err(&pdev->dev, "Can't get assigned pci irq vector %02X\n", + pdev->irq); + return result; + } + + /* Ensure all interrupts are mapped to PCI Interrupt 0 */ + iowrite32(0, bridge->base + LINT_MAP0); + iowrite32(0, bridge->base + LINT_MAP1); + iowrite32(0, bridge->base + LINT_MAP2); + + /* Enable DMA, mailbox & LM Interrupts */ + tmp = CA91CX42_LINT_MBOX3 | CA91CX42_LINT_MBOX2 | CA91CX42_LINT_MBOX1 | + CA91CX42_LINT_MBOX0 | CA91CX42_LINT_SW_IACK | + CA91CX42_LINT_VERR | CA91CX42_LINT_LERR | CA91CX42_LINT_DMA; + + iowrite32(tmp, bridge->base + LINT_EN); + + return 0; +} + +static void ca91cx42_irq_exit(struct ca91cx42_driver *bridge, + struct pci_dev *pdev) +{ + /* Disable interrupts from PCI to VME */ + iowrite32(0, bridge->base + VINT_EN); + + /* Disable PCI interrupts */ + iowrite32(0, bridge->base + LINT_EN); + /* Clear Any Pending PCI Interrupts */ + iowrite32(0x00FFFFFF, bridge->base + LINT_STAT); + + free_irq(pdev->irq, pdev); +} + +static int ca91cx42_iack_received(struct ca91cx42_driver *bridge, int level) +{ + u32 tmp; + + tmp = ioread32(bridge->base + LINT_STAT); + + if (tmp & (1 << level)) + return 0; + else + return 1; +} + +/* + * Set up an VME interrupt + */ +static void ca91cx42_irq_set(struct vme_bridge *ca91cx42_bridge, int level, + int state, int sync) + +{ + struct pci_dev *pdev; + u32 tmp; + struct ca91cx42_driver *bridge; + + bridge = ca91cx42_bridge->driver_priv; + + /* Enable IRQ level */ + tmp = ioread32(bridge->base + LINT_EN); + + if (state == 0) + tmp &= ~CA91CX42_LINT_VIRQ[level]; + else + tmp |= CA91CX42_LINT_VIRQ[level]; + + iowrite32(tmp, bridge->base + LINT_EN); + + if ((state == 0) && (sync != 0)) { + pdev = container_of(ca91cx42_bridge->parent, struct pci_dev, + dev); + + synchronize_irq(pdev->irq); + } +} + +static int ca91cx42_irq_generate(struct vme_bridge *ca91cx42_bridge, int level, + int statid) +{ + u32 tmp; + struct ca91cx42_driver *bridge; + + bridge = ca91cx42_bridge->driver_priv; + + /* Universe can only generate even vectors */ + if (statid & 1) + return -EINVAL; + + mutex_lock(&bridge->vme_int); + + tmp = ioread32(bridge->base + VINT_EN); + + /* Set Status/ID */ + iowrite32(statid << 24, bridge->base + STATID); + + /* Assert VMEbus IRQ */ + tmp = tmp | (1 << (level + 24)); + iowrite32(tmp, bridge->base + VINT_EN); + + /* Wait for IACK */ + wait_event_interruptible(bridge->iack_queue, + ca91cx42_iack_received(bridge, level)); + + /* Return interrupt to low state */ + tmp = ioread32(bridge->base + VINT_EN); + tmp = tmp & ~(1 << (level + 24)); + iowrite32(tmp, bridge->base + VINT_EN); + + mutex_unlock(&bridge->vme_int); + + return 0; +} + +static int ca91cx42_slave_set(struct vme_slave_resource *image, int enabled, + unsigned long long vme_base, unsigned long long size, + dma_addr_t pci_base, u32 aspace, u32 cycle) +{ + unsigned int i, addr = 0, granularity; + unsigned int temp_ctl = 0; + unsigned int vme_bound, pci_offset; + struct vme_bridge *ca91cx42_bridge; + struct ca91cx42_driver *bridge; + + ca91cx42_bridge = image->parent; + + bridge = ca91cx42_bridge->driver_priv; + + i = image->number; + + switch (aspace) { + case VME_A16: + addr |= CA91CX42_VSI_CTL_VAS_A16; + break; + case VME_A24: + addr |= CA91CX42_VSI_CTL_VAS_A24; + break; + case VME_A32: + addr |= CA91CX42_VSI_CTL_VAS_A32; + break; + case VME_USER1: + addr |= CA91CX42_VSI_CTL_VAS_USER1; + break; + case VME_USER2: + addr |= CA91CX42_VSI_CTL_VAS_USER2; + break; + case VME_A64: + case VME_CRCSR: + case VME_USER3: + case VME_USER4: + default: + dev_err(ca91cx42_bridge->parent, "Invalid address space\n"); + return -EINVAL; + break; + } + + /* + * Bound address is a valid address for the window, adjust + * accordingly + */ + vme_bound = vme_base + size; + pci_offset = pci_base - vme_base; + + if ((i == 0) || (i == 4)) + granularity = 0x1000; + else + granularity = 0x10000; + + if (vme_base & (granularity - 1)) { + dev_err(ca91cx42_bridge->parent, "Invalid VME base " + "alignment\n"); + return -EINVAL; + } + if (vme_bound & (granularity - 1)) { + dev_err(ca91cx42_bridge->parent, "Invalid VME bound " + "alignment\n"); + return -EINVAL; + } + if (pci_offset & (granularity - 1)) { + dev_err(ca91cx42_bridge->parent, "Invalid PCI Offset " + "alignment\n"); + return -EINVAL; + } + + /* Disable while we are mucking around */ + temp_ctl = ioread32(bridge->base + CA91CX42_VSI_CTL[i]); + temp_ctl &= ~CA91CX42_VSI_CTL_EN; + iowrite32(temp_ctl, bridge->base + CA91CX42_VSI_CTL[i]); + + /* Setup mapping */ + iowrite32(vme_base, bridge->base + CA91CX42_VSI_BS[i]); + iowrite32(vme_bound, bridge->base + CA91CX42_VSI_BD[i]); + iowrite32(pci_offset, bridge->base + CA91CX42_VSI_TO[i]); + + /* Setup address space */ + temp_ctl &= ~CA91CX42_VSI_CTL_VAS_M; + temp_ctl |= addr; + + /* Setup cycle types */ + temp_ctl &= ~(CA91CX42_VSI_CTL_PGM_M | CA91CX42_VSI_CTL_SUPER_M); + if (cycle & VME_SUPER) + temp_ctl |= CA91CX42_VSI_CTL_SUPER_SUPR; + if (cycle & VME_USER) + temp_ctl |= CA91CX42_VSI_CTL_SUPER_NPRIV; + if (cycle & VME_PROG) + temp_ctl |= CA91CX42_VSI_CTL_PGM_PGM; + if (cycle & VME_DATA) + temp_ctl |= CA91CX42_VSI_CTL_PGM_DATA; + + /* Write ctl reg without enable */ + iowrite32(temp_ctl, bridge->base + CA91CX42_VSI_CTL[i]); + + if (enabled) + temp_ctl |= CA91CX42_VSI_CTL_EN; + + iowrite32(temp_ctl, bridge->base + CA91CX42_VSI_CTL[i]); + + return 0; +} + +static int ca91cx42_slave_get(struct vme_slave_resource *image, int *enabled, + unsigned long long *vme_base, unsigned long long *size, + dma_addr_t *pci_base, u32 *aspace, u32 *cycle) +{ + unsigned int i, granularity = 0, ctl = 0; + unsigned long long vme_bound, pci_offset; + struct ca91cx42_driver *bridge; + + bridge = image->parent->driver_priv; + + i = image->number; + + if ((i == 0) || (i == 4)) + granularity = 0x1000; + else + granularity = 0x10000; + + /* Read Registers */ + ctl = ioread32(bridge->base + CA91CX42_VSI_CTL[i]); + + *vme_base = ioread32(bridge->base + CA91CX42_VSI_BS[i]); + vme_bound = ioread32(bridge->base + CA91CX42_VSI_BD[i]); + pci_offset = ioread32(bridge->base + CA91CX42_VSI_TO[i]); + + *pci_base = (dma_addr_t)vme_base + pci_offset; + *size = (unsigned long long)((vme_bound - *vme_base) + granularity); + + *enabled = 0; + *aspace = 0; + *cycle = 0; + + if (ctl & CA91CX42_VSI_CTL_EN) + *enabled = 1; + + if ((ctl & CA91CX42_VSI_CTL_VAS_M) == CA91CX42_VSI_CTL_VAS_A16) + *aspace = VME_A16; + if ((ctl & CA91CX42_VSI_CTL_VAS_M) == CA91CX42_VSI_CTL_VAS_A24) + *aspace = VME_A24; + if ((ctl & CA91CX42_VSI_CTL_VAS_M) == CA91CX42_VSI_CTL_VAS_A32) + *aspace = VME_A32; + if ((ctl & CA91CX42_VSI_CTL_VAS_M) == CA91CX42_VSI_CTL_VAS_USER1) + *aspace = VME_USER1; + if ((ctl & CA91CX42_VSI_CTL_VAS_M) == CA91CX42_VSI_CTL_VAS_USER2) + *aspace = VME_USER2; + + if (ctl & CA91CX42_VSI_CTL_SUPER_SUPR) + *cycle |= VME_SUPER; + if (ctl & CA91CX42_VSI_CTL_SUPER_NPRIV) + *cycle |= VME_USER; + if (ctl & CA91CX42_VSI_CTL_PGM_PGM) + *cycle |= VME_PROG; + if (ctl & CA91CX42_VSI_CTL_PGM_DATA) + *cycle |= VME_DATA; + + return 0; +} + +/* + * Allocate and map PCI Resource + */ +static int ca91cx42_alloc_resource(struct vme_master_resource *image, + unsigned long long size) +{ + unsigned long long existing_size; + int retval = 0; + struct pci_dev *pdev; + struct vme_bridge *ca91cx42_bridge; + + ca91cx42_bridge = image->parent; + + /* Find pci_dev container of dev */ + if (ca91cx42_bridge->parent == NULL) { + dev_err(ca91cx42_bridge->parent, "Dev entry NULL\n"); + return -EINVAL; + } + pdev = container_of(ca91cx42_bridge->parent, struct pci_dev, dev); + + existing_size = (unsigned long long)(image->bus_resource.end - + image->bus_resource.start); + + /* If the existing size is OK, return */ + if (existing_size == (size - 1)) + return 0; + + if (existing_size != 0) { + iounmap(image->kern_base); + image->kern_base = NULL; + kfree(image->bus_resource.name); + release_resource(&image->bus_resource); + memset(&image->bus_resource, 0, sizeof(struct resource)); + } + + if (image->bus_resource.name == NULL) { + image->bus_resource.name = kmalloc(VMENAMSIZ+3, GFP_ATOMIC); + if (image->bus_resource.name == NULL) { + dev_err(ca91cx42_bridge->parent, "Unable to allocate " + "memory for resource name\n"); + retval = -ENOMEM; + goto err_name; + } + } + + sprintf((char *)image->bus_resource.name, "%s.%d", + ca91cx42_bridge->name, image->number); + + image->bus_resource.start = 0; + image->bus_resource.end = (unsigned long)size; + image->bus_resource.flags = IORESOURCE_MEM; + + retval = pci_bus_alloc_resource(pdev->bus, + &image->bus_resource, size, size, PCIBIOS_MIN_MEM, + 0, NULL, NULL); + if (retval) { + dev_err(ca91cx42_bridge->parent, "Failed to allocate mem " + "resource for window %d size 0x%lx start 0x%lx\n", + image->number, (unsigned long)size, + (unsigned long)image->bus_resource.start); + goto err_resource; + } + + image->kern_base = ioremap_nocache( + image->bus_resource.start, size); + if (image->kern_base == NULL) { + dev_err(ca91cx42_bridge->parent, "Failed to remap resource\n"); + retval = -ENOMEM; + goto err_remap; + } + + return 0; + +err_remap: + release_resource(&image->bus_resource); +err_resource: + kfree(image->bus_resource.name); + memset(&image->bus_resource, 0, sizeof(struct resource)); +err_name: + return retval; +} + +/* + * Free and unmap PCI Resource + */ +static void ca91cx42_free_resource(struct vme_master_resource *image) +{ + iounmap(image->kern_base); + image->kern_base = NULL; + release_resource(&image->bus_resource); + kfree(image->bus_resource.name); + memset(&image->bus_resource, 0, sizeof(struct resource)); +} + + +static int ca91cx42_master_set(struct vme_master_resource *image, int enabled, + unsigned long long vme_base, unsigned long long size, u32 aspace, + u32 cycle, u32 dwidth) +{ + int retval = 0; + unsigned int i, granularity = 0; + unsigned int temp_ctl = 0; + unsigned long long pci_bound, vme_offset, pci_base; + struct vme_bridge *ca91cx42_bridge; + struct ca91cx42_driver *bridge; + + ca91cx42_bridge = image->parent; + + bridge = ca91cx42_bridge->driver_priv; + + i = image->number; + + if ((i == 0) || (i == 4)) + granularity = 0x1000; + else + granularity = 0x10000; + + /* Verify input data */ + if (vme_base & (granularity - 1)) { + dev_err(ca91cx42_bridge->parent, "Invalid VME Window " + "alignment\n"); + retval = -EINVAL; + goto err_window; + } + if (size & (granularity - 1)) { + dev_err(ca91cx42_bridge->parent, "Invalid VME Window " + "alignment\n"); + retval = -EINVAL; + goto err_window; + } + + spin_lock(&image->lock); + + /* + * Let's allocate the resource here rather than further up the stack as + * it avoids pushing loads of bus dependent stuff up the stack + */ + retval = ca91cx42_alloc_resource(image, size); + if (retval) { + spin_unlock(&image->lock); + dev_err(ca91cx42_bridge->parent, "Unable to allocate memory " + "for resource name\n"); + retval = -ENOMEM; + goto err_res; + } + + pci_base = (unsigned long long)image->bus_resource.start; + + /* + * Bound address is a valid address for the window, adjust + * according to window granularity. + */ + pci_bound = pci_base + size; + vme_offset = vme_base - pci_base; + + /* Disable while we are mucking around */ + temp_ctl = ioread32(bridge->base + CA91CX42_LSI_CTL[i]); + temp_ctl &= ~CA91CX42_LSI_CTL_EN; + iowrite32(temp_ctl, bridge->base + CA91CX42_LSI_CTL[i]); + + /* Setup cycle types */ + temp_ctl &= ~CA91CX42_LSI_CTL_VCT_M; + if (cycle & VME_BLT) + temp_ctl |= CA91CX42_LSI_CTL_VCT_BLT; + if (cycle & VME_MBLT) + temp_ctl |= CA91CX42_LSI_CTL_VCT_MBLT; + + /* Setup data width */ + temp_ctl &= ~CA91CX42_LSI_CTL_VDW_M; + switch (dwidth) { + case VME_D8: + temp_ctl |= CA91CX42_LSI_CTL_VDW_D8; + break; + case VME_D16: + temp_ctl |= CA91CX42_LSI_CTL_VDW_D16; + break; + case VME_D32: + temp_ctl |= CA91CX42_LSI_CTL_VDW_D32; + break; + case VME_D64: + temp_ctl |= CA91CX42_LSI_CTL_VDW_D64; + break; + default: + spin_unlock(&image->lock); + dev_err(ca91cx42_bridge->parent, "Invalid data width\n"); + retval = -EINVAL; + goto err_dwidth; + break; + } + + /* Setup address space */ + temp_ctl &= ~CA91CX42_LSI_CTL_VAS_M; + switch (aspace) { + case VME_A16: + temp_ctl |= CA91CX42_LSI_CTL_VAS_A16; + break; + case VME_A24: + temp_ctl |= CA91CX42_LSI_CTL_VAS_A24; + break; + case VME_A32: + temp_ctl |= CA91CX42_LSI_CTL_VAS_A32; + break; + case VME_CRCSR: + temp_ctl |= CA91CX42_LSI_CTL_VAS_CRCSR; + break; + case VME_USER1: + temp_ctl |= CA91CX42_LSI_CTL_VAS_USER1; + break; + case VME_USER2: + temp_ctl |= CA91CX42_LSI_CTL_VAS_USER2; + break; + case VME_A64: + case VME_USER3: + case VME_USER4: + default: + spin_unlock(&image->lock); + dev_err(ca91cx42_bridge->parent, "Invalid address space\n"); + retval = -EINVAL; + goto err_aspace; + break; + } + + temp_ctl &= ~(CA91CX42_LSI_CTL_PGM_M | CA91CX42_LSI_CTL_SUPER_M); + if (cycle & VME_SUPER) + temp_ctl |= CA91CX42_LSI_CTL_SUPER_SUPR; + if (cycle & VME_PROG) + temp_ctl |= CA91CX42_LSI_CTL_PGM_PGM; + + /* Setup mapping */ + iowrite32(pci_base, bridge->base + CA91CX42_LSI_BS[i]); + iowrite32(pci_bound, bridge->base + CA91CX42_LSI_BD[i]); + iowrite32(vme_offset, bridge->base + CA91CX42_LSI_TO[i]); + + /* Write ctl reg without enable */ + iowrite32(temp_ctl, bridge->base + CA91CX42_LSI_CTL[i]); + + if (enabled) + temp_ctl |= CA91CX42_LSI_CTL_EN; + + iowrite32(temp_ctl, bridge->base + CA91CX42_LSI_CTL[i]); + + spin_unlock(&image->lock); + return 0; + +err_aspace: +err_dwidth: + ca91cx42_free_resource(image); +err_res: +err_window: + return retval; +} + +static int __ca91cx42_master_get(struct vme_master_resource *image, + int *enabled, unsigned long long *vme_base, unsigned long long *size, + u32 *aspace, u32 *cycle, u32 *dwidth) +{ + unsigned int i, ctl; + unsigned long long pci_base, pci_bound, vme_offset; + struct ca91cx42_driver *bridge; + + bridge = image->parent->driver_priv; + + i = image->number; + + ctl = ioread32(bridge->base + CA91CX42_LSI_CTL[i]); + + pci_base = ioread32(bridge->base + CA91CX42_LSI_BS[i]); + vme_offset = ioread32(bridge->base + CA91CX42_LSI_TO[i]); + pci_bound = ioread32(bridge->base + CA91CX42_LSI_BD[i]); + + *vme_base = pci_base + vme_offset; + *size = (unsigned long long)(pci_bound - pci_base); + + *enabled = 0; + *aspace = 0; + *cycle = 0; + *dwidth = 0; + + if (ctl & CA91CX42_LSI_CTL_EN) + *enabled = 1; + + /* Setup address space */ + switch (ctl & CA91CX42_LSI_CTL_VAS_M) { + case CA91CX42_LSI_CTL_VAS_A16: + *aspace = VME_A16; + break; + case CA91CX42_LSI_CTL_VAS_A24: + *aspace = VME_A24; + break; + case CA91CX42_LSI_CTL_VAS_A32: + *aspace = VME_A32; + break; + case CA91CX42_LSI_CTL_VAS_CRCSR: + *aspace = VME_CRCSR; + break; + case CA91CX42_LSI_CTL_VAS_USER1: + *aspace = VME_USER1; + break; + case CA91CX42_LSI_CTL_VAS_USER2: + *aspace = VME_USER2; + break; + } + + /* XXX Not sure howto check for MBLT */ + /* Setup cycle types */ + if (ctl & CA91CX42_LSI_CTL_VCT_BLT) + *cycle |= VME_BLT; + else + *cycle |= VME_SCT; + + if (ctl & CA91CX42_LSI_CTL_SUPER_SUPR) + *cycle |= VME_SUPER; + else + *cycle |= VME_USER; + + if (ctl & CA91CX42_LSI_CTL_PGM_PGM) + *cycle = VME_PROG; + else + *cycle = VME_DATA; + + /* Setup data width */ + switch (ctl & CA91CX42_LSI_CTL_VDW_M) { + case CA91CX42_LSI_CTL_VDW_D8: + *dwidth = VME_D8; + break; + case CA91CX42_LSI_CTL_VDW_D16: + *dwidth = VME_D16; + break; + case CA91CX42_LSI_CTL_VDW_D32: + *dwidth = VME_D32; + break; + case CA91CX42_LSI_CTL_VDW_D64: + *dwidth = VME_D64; + break; + } + + return 0; +} + +static int ca91cx42_master_get(struct vme_master_resource *image, int *enabled, + unsigned long long *vme_base, unsigned long long *size, u32 *aspace, + u32 *cycle, u32 *dwidth) +{ + int retval; + + spin_lock(&image->lock); + + retval = __ca91cx42_master_get(image, enabled, vme_base, size, aspace, + cycle, dwidth); + + spin_unlock(&image->lock); + + return retval; +} + +static ssize_t ca91cx42_master_read(struct vme_master_resource *image, + void *buf, size_t count, loff_t offset) +{ + ssize_t retval; + void *addr = image->kern_base + offset; + unsigned int done = 0; + unsigned int count32; + + if (count == 0) + return 0; + + spin_lock(&image->lock); + + /* The following code handles VME address alignment problem + * in order to assure the maximal data width cycle. + * We cannot use memcpy_xxx directly here because it + * may cut data transfer in 8-bits cycles, thus making + * D16 cycle impossible. + * From the other hand, the bridge itself assures that + * maximal configured data cycle is used and splits it + * automatically for non-aligned addresses. + */ + if ((uintptr_t)addr & 0x1) { + *(u8 *)buf = ioread8(addr); + done += 1; + if (done == count) + goto out; + } + if ((uintptr_t)addr & 0x2) { + if ((count - done) < 2) { + *(u8 *)(buf + done) = ioread8(addr + done); + done += 1; + goto out; + } else { + *(u16 *)(buf + done) = ioread16(addr + done); + done += 2; + } + } + + count32 = (count - done) & ~0x3; + if (count32 > 0) { + memcpy_fromio(buf + done, addr + done, (unsigned int)count); + done += count32; + } + + if ((count - done) & 0x2) { + *(u16 *)(buf + done) = ioread16(addr + done); + done += 2; + } + if ((count - done) & 0x1) { + *(u8 *)(buf + done) = ioread8(addr + done); + done += 1; + } +out: + retval = count; + spin_unlock(&image->lock); + + return retval; +} + +static ssize_t ca91cx42_master_write(struct vme_master_resource *image, + void *buf, size_t count, loff_t offset) +{ + ssize_t retval; + void *addr = image->kern_base + offset; + unsigned int done = 0; + unsigned int count32; + + if (count == 0) + return 0; + + spin_lock(&image->lock); + + /* Here we apply for the same strategy we do in master_read + * function in order to assure D16 cycle when required. + */ + if ((uintptr_t)addr & 0x1) { + iowrite8(*(u8 *)buf, addr); + done += 1; + if (done == count) + goto out; + } + if ((uintptr_t)addr & 0x2) { + if ((count - done) < 2) { + iowrite8(*(u8 *)(buf + done), addr + done); + done += 1; + goto out; + } else { + iowrite16(*(u16 *)(buf + done), addr + done); + done += 2; + } + } + + count32 = (count - done) & ~0x3; + if (count32 > 0) { + memcpy_toio(addr + done, buf + done, count32); + done += count32; + } + + if ((count - done) & 0x2) { + iowrite16(*(u16 *)(buf + done), addr + done); + done += 2; + } + if ((count - done) & 0x1) { + iowrite8(*(u8 *)(buf + done), addr + done); + done += 1; + } +out: + retval = count; + + spin_unlock(&image->lock); + + return retval; +} + +static unsigned int ca91cx42_master_rmw(struct vme_master_resource *image, + unsigned int mask, unsigned int compare, unsigned int swap, + loff_t offset) +{ + u32 result; + uintptr_t pci_addr; + int i; + struct ca91cx42_driver *bridge; + struct device *dev; + + bridge = image->parent->driver_priv; + dev = image->parent->parent; + + /* Find the PCI address that maps to the desired VME address */ + i = image->number; + + /* Locking as we can only do one of these at a time */ + mutex_lock(&bridge->vme_rmw); + + /* Lock image */ + spin_lock(&image->lock); + + pci_addr = (uintptr_t)image->kern_base + offset; + + /* Address must be 4-byte aligned */ + if (pci_addr & 0x3) { + dev_err(dev, "RMW Address not 4-byte aligned\n"); + result = -EINVAL; + goto out; + } + + /* Ensure RMW Disabled whilst configuring */ + iowrite32(0, bridge->base + SCYC_CTL); + + /* Configure registers */ + iowrite32(mask, bridge->base + SCYC_EN); + iowrite32(compare, bridge->base + SCYC_CMP); + iowrite32(swap, bridge->base + SCYC_SWP); + iowrite32(pci_addr, bridge->base + SCYC_ADDR); + + /* Enable RMW */ + iowrite32(CA91CX42_SCYC_CTL_CYC_RMW, bridge->base + SCYC_CTL); + + /* Kick process off with a read to the required address. */ + result = ioread32(image->kern_base + offset); + + /* Disable RMW */ + iowrite32(0, bridge->base + SCYC_CTL); + +out: + spin_unlock(&image->lock); + + mutex_unlock(&bridge->vme_rmw); + + return result; +} + +static int ca91cx42_dma_list_add(struct vme_dma_list *list, + struct vme_dma_attr *src, struct vme_dma_attr *dest, size_t count) +{ + struct ca91cx42_dma_entry *entry, *prev; + struct vme_dma_pci *pci_attr; + struct vme_dma_vme *vme_attr; + dma_addr_t desc_ptr; + int retval = 0; + struct device *dev; + + dev = list->parent->parent->parent; + + /* XXX descriptor must be aligned on 64-bit boundaries */ + entry = kmalloc(sizeof(struct ca91cx42_dma_entry), GFP_KERNEL); + if (entry == NULL) { + dev_err(dev, "Failed to allocate memory for dma resource " + "structure\n"); + retval = -ENOMEM; + goto err_mem; + } + + /* Test descriptor alignment */ + if ((unsigned long)&entry->descriptor & CA91CX42_DCPP_M) { + dev_err(dev, "Descriptor not aligned to 16 byte boundary as " + "required: %p\n", &entry->descriptor); + retval = -EINVAL; + goto err_align; + } + + memset(&entry->descriptor, 0, sizeof(struct ca91cx42_dma_descriptor)); + + if (dest->type == VME_DMA_VME) { + entry->descriptor.dctl |= CA91CX42_DCTL_L2V; + vme_attr = dest->private; + pci_attr = src->private; + } else { + vme_attr = src->private; + pci_attr = dest->private; + } + + /* Check we can do fulfill required attributes */ + if ((vme_attr->aspace & ~(VME_A16 | VME_A24 | VME_A32 | VME_USER1 | + VME_USER2)) != 0) { + + dev_err(dev, "Unsupported cycle type\n"); + retval = -EINVAL; + goto err_aspace; + } + + if ((vme_attr->cycle & ~(VME_SCT | VME_BLT | VME_SUPER | VME_USER | + VME_PROG | VME_DATA)) != 0) { + + dev_err(dev, "Unsupported cycle type\n"); + retval = -EINVAL; + goto err_cycle; + } + + /* Check to see if we can fulfill source and destination */ + if (!(((src->type == VME_DMA_PCI) && (dest->type == VME_DMA_VME)) || + ((src->type == VME_DMA_VME) && (dest->type == VME_DMA_PCI)))) { + + dev_err(dev, "Cannot perform transfer with this " + "source-destination combination\n"); + retval = -EINVAL; + goto err_direct; + } + + /* Setup cycle types */ + if (vme_attr->cycle & VME_BLT) + entry->descriptor.dctl |= CA91CX42_DCTL_VCT_BLT; + + /* Setup data width */ + switch (vme_attr->dwidth) { + case VME_D8: + entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D8; + break; + case VME_D16: + entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D16; + break; + case VME_D32: + entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D32; + break; + case VME_D64: + entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D64; + break; + default: + dev_err(dev, "Invalid data width\n"); + return -EINVAL; + } + + /* Setup address space */ + switch (vme_attr->aspace) { + case VME_A16: + entry->descriptor.dctl |= CA91CX42_DCTL_VAS_A16; + break; + case VME_A24: + entry->descriptor.dctl |= CA91CX42_DCTL_VAS_A24; + break; + case VME_A32: + entry->descriptor.dctl |= CA91CX42_DCTL_VAS_A32; + break; + case VME_USER1: + entry->descriptor.dctl |= CA91CX42_DCTL_VAS_USER1; + break; + case VME_USER2: + entry->descriptor.dctl |= CA91CX42_DCTL_VAS_USER2; + break; + default: + dev_err(dev, "Invalid address space\n"); + return -EINVAL; + break; + } + + if (vme_attr->cycle & VME_SUPER) + entry->descriptor.dctl |= CA91CX42_DCTL_SUPER_SUPR; + if (vme_attr->cycle & VME_PROG) + entry->descriptor.dctl |= CA91CX42_DCTL_PGM_PGM; + + entry->descriptor.dtbc = count; + entry->descriptor.dla = pci_attr->address; + entry->descriptor.dva = vme_attr->address; + entry->descriptor.dcpp = CA91CX42_DCPP_NULL; + + /* Add to list */ + list_add_tail(&entry->list, &list->entries); + + /* Fill out previous descriptors "Next Address" */ + if (entry->list.prev != &list->entries) { + prev = list_entry(entry->list.prev, struct ca91cx42_dma_entry, + list); + /* We need the bus address for the pointer */ + desc_ptr = virt_to_bus(&entry->descriptor); + prev->descriptor.dcpp = desc_ptr & ~CA91CX42_DCPP_M; + } + + return 0; + +err_cycle: +err_aspace: +err_direct: |