18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Support for the Tundra Universe I/II VME-PCI Bridge Chips 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Martyn Welch <martyn.welch@ge.com> 68c2ecf20Sopenharmony_ci * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on work by Tom Armistead and Ajit Prem 98c2ecf20Sopenharmony_ci * Copyright 2004 Motorola Inc. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Derived from ca91c042.c by Michael Wyrick 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/mm.h> 168c2ecf20Sopenharmony_ci#include <linux/types.h> 178c2ecf20Sopenharmony_ci#include <linux/errno.h> 188c2ecf20Sopenharmony_ci#include <linux/pci.h> 198c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 208c2ecf20Sopenharmony_ci#include <linux/poll.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 238c2ecf20Sopenharmony_ci#include <linux/sched.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/time.h> 268c2ecf20Sopenharmony_ci#include <linux/io.h> 278c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 288c2ecf20Sopenharmony_ci#include <linux/vme.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "../vme_bridge.h" 318c2ecf20Sopenharmony_ci#include "vme_ca91cx42.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic int ca91cx42_probe(struct pci_dev *, const struct pci_device_id *); 348c2ecf20Sopenharmony_cistatic void ca91cx42_remove(struct pci_dev *); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* Module parameters */ 378c2ecf20Sopenharmony_cistatic int geoid; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic const char driver_name[] = "vme_ca91cx42"; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic const struct pci_device_id ca91cx42_ids[] = { 428c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_TUNDRA, PCI_DEVICE_ID_TUNDRA_CA91C142) }, 438c2ecf20Sopenharmony_ci { }, 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ca91cx42_ids); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic struct pci_driver ca91cx42_driver = { 498c2ecf20Sopenharmony_ci .name = driver_name, 508c2ecf20Sopenharmony_ci .id_table = ca91cx42_ids, 518c2ecf20Sopenharmony_ci .probe = ca91cx42_probe, 528c2ecf20Sopenharmony_ci .remove = ca91cx42_remove, 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic u32 ca91cx42_DMA_irqhandler(struct ca91cx42_driver *bridge) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci wake_up(&bridge->dma_queue); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return CA91CX42_LINT_DMA; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic u32 ca91cx42_LM_irqhandler(struct ca91cx42_driver *bridge, u32 stat) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci int i; 658c2ecf20Sopenharmony_ci u32 serviced = 0; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 688c2ecf20Sopenharmony_ci if (stat & CA91CX42_LINT_LM[i]) { 698c2ecf20Sopenharmony_ci /* We only enable interrupts if the callback is set */ 708c2ecf20Sopenharmony_ci bridge->lm_callback[i](bridge->lm_data[i]); 718c2ecf20Sopenharmony_ci serviced |= CA91CX42_LINT_LM[i]; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return serviced; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* XXX This needs to be split into 4 queues */ 798c2ecf20Sopenharmony_cistatic u32 ca91cx42_MB_irqhandler(struct ca91cx42_driver *bridge, int mbox_mask) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci wake_up(&bridge->mbox_queue); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return CA91CX42_LINT_MBOX; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic u32 ca91cx42_IACK_irqhandler(struct ca91cx42_driver *bridge) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci wake_up(&bridge->iack_queue); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return CA91CX42_LINT_SW_IACK; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic u32 ca91cx42_VERR_irqhandler(struct vme_bridge *ca91cx42_bridge) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci int val; 968c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci bridge = ca91cx42_bridge->driver_priv; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci val = ioread32(bridge->base + DGCS); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (!(val & 0x00000800)) { 1038c2ecf20Sopenharmony_ci dev_err(ca91cx42_bridge->parent, "ca91cx42_VERR_irqhandler DMA " 1048c2ecf20Sopenharmony_ci "Read Error DGCS=%08X\n", val); 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return CA91CX42_LINT_VERR; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic u32 ca91cx42_LERR_irqhandler(struct vme_bridge *ca91cx42_bridge) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci int val; 1138c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci bridge = ca91cx42_bridge->driver_priv; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci val = ioread32(bridge->base + DGCS); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (!(val & 0x00000800)) 1208c2ecf20Sopenharmony_ci dev_err(ca91cx42_bridge->parent, "ca91cx42_LERR_irqhandler DMA " 1218c2ecf20Sopenharmony_ci "Read Error DGCS=%08X\n", val); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return CA91CX42_LINT_LERR; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic u32 ca91cx42_VIRQ_irqhandler(struct vme_bridge *ca91cx42_bridge, 1288c2ecf20Sopenharmony_ci int stat) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci int vec, i, serviced = 0; 1318c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci bridge = ca91cx42_bridge->driver_priv; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci for (i = 7; i > 0; i--) { 1378c2ecf20Sopenharmony_ci if (stat & (1 << i)) { 1388c2ecf20Sopenharmony_ci vec = ioread32(bridge->base + 1398c2ecf20Sopenharmony_ci CA91CX42_V_STATID[i]) & 0xff; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci vme_irq_handler(ca91cx42_bridge, i, vec); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci serviced |= (1 << i); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return serviced; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic irqreturn_t ca91cx42_irqhandler(int irq, void *ptr) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci u32 stat, enable, serviced = 0; 1538c2ecf20Sopenharmony_ci struct vme_bridge *ca91cx42_bridge; 1548c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ca91cx42_bridge = ptr; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci bridge = ca91cx42_bridge->driver_priv; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci enable = ioread32(bridge->base + LINT_EN); 1618c2ecf20Sopenharmony_ci stat = ioread32(bridge->base + LINT_STAT); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* Only look at unmasked interrupts */ 1648c2ecf20Sopenharmony_ci stat &= enable; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (unlikely(!stat)) 1678c2ecf20Sopenharmony_ci return IRQ_NONE; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (stat & CA91CX42_LINT_DMA) 1708c2ecf20Sopenharmony_ci serviced |= ca91cx42_DMA_irqhandler(bridge); 1718c2ecf20Sopenharmony_ci if (stat & (CA91CX42_LINT_LM0 | CA91CX42_LINT_LM1 | CA91CX42_LINT_LM2 | 1728c2ecf20Sopenharmony_ci CA91CX42_LINT_LM3)) 1738c2ecf20Sopenharmony_ci serviced |= ca91cx42_LM_irqhandler(bridge, stat); 1748c2ecf20Sopenharmony_ci if (stat & CA91CX42_LINT_MBOX) 1758c2ecf20Sopenharmony_ci serviced |= ca91cx42_MB_irqhandler(bridge, stat); 1768c2ecf20Sopenharmony_ci if (stat & CA91CX42_LINT_SW_IACK) 1778c2ecf20Sopenharmony_ci serviced |= ca91cx42_IACK_irqhandler(bridge); 1788c2ecf20Sopenharmony_ci if (stat & CA91CX42_LINT_VERR) 1798c2ecf20Sopenharmony_ci serviced |= ca91cx42_VERR_irqhandler(ca91cx42_bridge); 1808c2ecf20Sopenharmony_ci if (stat & CA91CX42_LINT_LERR) 1818c2ecf20Sopenharmony_ci serviced |= ca91cx42_LERR_irqhandler(ca91cx42_bridge); 1828c2ecf20Sopenharmony_ci if (stat & (CA91CX42_LINT_VIRQ1 | CA91CX42_LINT_VIRQ2 | 1838c2ecf20Sopenharmony_ci CA91CX42_LINT_VIRQ3 | CA91CX42_LINT_VIRQ4 | 1848c2ecf20Sopenharmony_ci CA91CX42_LINT_VIRQ5 | CA91CX42_LINT_VIRQ6 | 1858c2ecf20Sopenharmony_ci CA91CX42_LINT_VIRQ7)) 1868c2ecf20Sopenharmony_ci serviced |= ca91cx42_VIRQ_irqhandler(ca91cx42_bridge, stat); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* Clear serviced interrupts */ 1898c2ecf20Sopenharmony_ci iowrite32(serviced, bridge->base + LINT_STAT); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int ca91cx42_irq_init(struct vme_bridge *ca91cx42_bridge) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci int result, tmp; 1978c2ecf20Sopenharmony_ci struct pci_dev *pdev; 1988c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci bridge = ca91cx42_bridge->driver_priv; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Need pdev */ 2038c2ecf20Sopenharmony_ci pdev = to_pci_dev(ca91cx42_bridge->parent); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* Disable interrupts from PCI to VME */ 2068c2ecf20Sopenharmony_ci iowrite32(0, bridge->base + VINT_EN); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* Disable PCI interrupts */ 2098c2ecf20Sopenharmony_ci iowrite32(0, bridge->base + LINT_EN); 2108c2ecf20Sopenharmony_ci /* Clear Any Pending PCI Interrupts */ 2118c2ecf20Sopenharmony_ci iowrite32(0x00FFFFFF, bridge->base + LINT_STAT); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci result = request_irq(pdev->irq, ca91cx42_irqhandler, IRQF_SHARED, 2148c2ecf20Sopenharmony_ci driver_name, ca91cx42_bridge); 2158c2ecf20Sopenharmony_ci if (result) { 2168c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't get assigned pci irq vector %02X\n", 2178c2ecf20Sopenharmony_ci pdev->irq); 2188c2ecf20Sopenharmony_ci return result; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* Ensure all interrupts are mapped to PCI Interrupt 0 */ 2228c2ecf20Sopenharmony_ci iowrite32(0, bridge->base + LINT_MAP0); 2238c2ecf20Sopenharmony_ci iowrite32(0, bridge->base + LINT_MAP1); 2248c2ecf20Sopenharmony_ci iowrite32(0, bridge->base + LINT_MAP2); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* Enable DMA, mailbox & LM Interrupts */ 2278c2ecf20Sopenharmony_ci tmp = CA91CX42_LINT_MBOX3 | CA91CX42_LINT_MBOX2 | CA91CX42_LINT_MBOX1 | 2288c2ecf20Sopenharmony_ci CA91CX42_LINT_MBOX0 | CA91CX42_LINT_SW_IACK | 2298c2ecf20Sopenharmony_ci CA91CX42_LINT_VERR | CA91CX42_LINT_LERR | CA91CX42_LINT_DMA; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci iowrite32(tmp, bridge->base + LINT_EN); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void ca91cx42_irq_exit(struct ca91cx42_driver *bridge, 2378c2ecf20Sopenharmony_ci struct pci_dev *pdev) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct vme_bridge *ca91cx42_bridge; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* Disable interrupts from PCI to VME */ 2428c2ecf20Sopenharmony_ci iowrite32(0, bridge->base + VINT_EN); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* Disable PCI interrupts */ 2458c2ecf20Sopenharmony_ci iowrite32(0, bridge->base + LINT_EN); 2468c2ecf20Sopenharmony_ci /* Clear Any Pending PCI Interrupts */ 2478c2ecf20Sopenharmony_ci iowrite32(0x00FFFFFF, bridge->base + LINT_STAT); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci ca91cx42_bridge = container_of((void *)bridge, struct vme_bridge, 2508c2ecf20Sopenharmony_ci driver_priv); 2518c2ecf20Sopenharmony_ci free_irq(pdev->irq, ca91cx42_bridge); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int ca91cx42_iack_received(struct ca91cx42_driver *bridge, int level) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci u32 tmp; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci tmp = ioread32(bridge->base + LINT_STAT); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (tmp & (1 << level)) 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci else 2638c2ecf20Sopenharmony_ci return 1; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* 2678c2ecf20Sopenharmony_ci * Set up an VME interrupt 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_cistatic void ca91cx42_irq_set(struct vme_bridge *ca91cx42_bridge, int level, 2708c2ecf20Sopenharmony_ci int state, int sync) 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct pci_dev *pdev; 2748c2ecf20Sopenharmony_ci u32 tmp; 2758c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci bridge = ca91cx42_bridge->driver_priv; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* Enable IRQ level */ 2808c2ecf20Sopenharmony_ci tmp = ioread32(bridge->base + LINT_EN); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (state == 0) 2838c2ecf20Sopenharmony_ci tmp &= ~CA91CX42_LINT_VIRQ[level]; 2848c2ecf20Sopenharmony_ci else 2858c2ecf20Sopenharmony_ci tmp |= CA91CX42_LINT_VIRQ[level]; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci iowrite32(tmp, bridge->base + LINT_EN); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if ((state == 0) && (sync != 0)) { 2908c2ecf20Sopenharmony_ci pdev = to_pci_dev(ca91cx42_bridge->parent); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci synchronize_irq(pdev->irq); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic int ca91cx42_irq_generate(struct vme_bridge *ca91cx42_bridge, int level, 2978c2ecf20Sopenharmony_ci int statid) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci u32 tmp; 3008c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci bridge = ca91cx42_bridge->driver_priv; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Universe can only generate even vectors */ 3058c2ecf20Sopenharmony_ci if (statid & 1) 3068c2ecf20Sopenharmony_ci return -EINVAL; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci mutex_lock(&bridge->vme_int); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci tmp = ioread32(bridge->base + VINT_EN); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* Set Status/ID */ 3138c2ecf20Sopenharmony_ci iowrite32(statid << 24, bridge->base + STATID); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* Assert VMEbus IRQ */ 3168c2ecf20Sopenharmony_ci tmp = tmp | (1 << (level + 24)); 3178c2ecf20Sopenharmony_ci iowrite32(tmp, bridge->base + VINT_EN); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* Wait for IACK */ 3208c2ecf20Sopenharmony_ci wait_event_interruptible(bridge->iack_queue, 3218c2ecf20Sopenharmony_ci ca91cx42_iack_received(bridge, level)); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* Return interrupt to low state */ 3248c2ecf20Sopenharmony_ci tmp = ioread32(bridge->base + VINT_EN); 3258c2ecf20Sopenharmony_ci tmp = tmp & ~(1 << (level + 24)); 3268c2ecf20Sopenharmony_ci iowrite32(tmp, bridge->base + VINT_EN); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci mutex_unlock(&bridge->vme_int); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic int ca91cx42_slave_set(struct vme_slave_resource *image, int enabled, 3348c2ecf20Sopenharmony_ci unsigned long long vme_base, unsigned long long size, 3358c2ecf20Sopenharmony_ci dma_addr_t pci_base, u32 aspace, u32 cycle) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci unsigned int i, addr = 0, granularity; 3388c2ecf20Sopenharmony_ci unsigned int temp_ctl = 0; 3398c2ecf20Sopenharmony_ci unsigned int vme_bound, pci_offset; 3408c2ecf20Sopenharmony_ci struct vme_bridge *ca91cx42_bridge; 3418c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci ca91cx42_bridge = image->parent; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci bridge = ca91cx42_bridge->driver_priv; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci i = image->number; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci switch (aspace) { 3508c2ecf20Sopenharmony_ci case VME_A16: 3518c2ecf20Sopenharmony_ci addr |= CA91CX42_VSI_CTL_VAS_A16; 3528c2ecf20Sopenharmony_ci break; 3538c2ecf20Sopenharmony_ci case VME_A24: 3548c2ecf20Sopenharmony_ci addr |= CA91CX42_VSI_CTL_VAS_A24; 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci case VME_A32: 3578c2ecf20Sopenharmony_ci addr |= CA91CX42_VSI_CTL_VAS_A32; 3588c2ecf20Sopenharmony_ci break; 3598c2ecf20Sopenharmony_ci case VME_USER1: 3608c2ecf20Sopenharmony_ci addr |= CA91CX42_VSI_CTL_VAS_USER1; 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci case VME_USER2: 3638c2ecf20Sopenharmony_ci addr |= CA91CX42_VSI_CTL_VAS_USER2; 3648c2ecf20Sopenharmony_ci break; 3658c2ecf20Sopenharmony_ci case VME_A64: 3668c2ecf20Sopenharmony_ci case VME_CRCSR: 3678c2ecf20Sopenharmony_ci case VME_USER3: 3688c2ecf20Sopenharmony_ci case VME_USER4: 3698c2ecf20Sopenharmony_ci default: 3708c2ecf20Sopenharmony_ci dev_err(ca91cx42_bridge->parent, "Invalid address space\n"); 3718c2ecf20Sopenharmony_ci return -EINVAL; 3728c2ecf20Sopenharmony_ci break; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* 3768c2ecf20Sopenharmony_ci * Bound address is a valid address for the window, adjust 3778c2ecf20Sopenharmony_ci * accordingly 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_ci vme_bound = vme_base + size; 3808c2ecf20Sopenharmony_ci pci_offset = pci_base - vme_base; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if ((i == 0) || (i == 4)) 3838c2ecf20Sopenharmony_ci granularity = 0x1000; 3848c2ecf20Sopenharmony_ci else 3858c2ecf20Sopenharmony_ci granularity = 0x10000; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (vme_base & (granularity - 1)) { 3888c2ecf20Sopenharmony_ci dev_err(ca91cx42_bridge->parent, "Invalid VME base " 3898c2ecf20Sopenharmony_ci "alignment\n"); 3908c2ecf20Sopenharmony_ci return -EINVAL; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci if (vme_bound & (granularity - 1)) { 3938c2ecf20Sopenharmony_ci dev_err(ca91cx42_bridge->parent, "Invalid VME bound " 3948c2ecf20Sopenharmony_ci "alignment\n"); 3958c2ecf20Sopenharmony_ci return -EINVAL; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci if (pci_offset & (granularity - 1)) { 3988c2ecf20Sopenharmony_ci dev_err(ca91cx42_bridge->parent, "Invalid PCI Offset " 3998c2ecf20Sopenharmony_ci "alignment\n"); 4008c2ecf20Sopenharmony_ci return -EINVAL; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* Disable while we are mucking around */ 4048c2ecf20Sopenharmony_ci temp_ctl = ioread32(bridge->base + CA91CX42_VSI_CTL[i]); 4058c2ecf20Sopenharmony_ci temp_ctl &= ~CA91CX42_VSI_CTL_EN; 4068c2ecf20Sopenharmony_ci iowrite32(temp_ctl, bridge->base + CA91CX42_VSI_CTL[i]); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* Setup mapping */ 4098c2ecf20Sopenharmony_ci iowrite32(vme_base, bridge->base + CA91CX42_VSI_BS[i]); 4108c2ecf20Sopenharmony_ci iowrite32(vme_bound, bridge->base + CA91CX42_VSI_BD[i]); 4118c2ecf20Sopenharmony_ci iowrite32(pci_offset, bridge->base + CA91CX42_VSI_TO[i]); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* Setup address space */ 4148c2ecf20Sopenharmony_ci temp_ctl &= ~CA91CX42_VSI_CTL_VAS_M; 4158c2ecf20Sopenharmony_ci temp_ctl |= addr; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* Setup cycle types */ 4188c2ecf20Sopenharmony_ci temp_ctl &= ~(CA91CX42_VSI_CTL_PGM_M | CA91CX42_VSI_CTL_SUPER_M); 4198c2ecf20Sopenharmony_ci if (cycle & VME_SUPER) 4208c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_VSI_CTL_SUPER_SUPR; 4218c2ecf20Sopenharmony_ci if (cycle & VME_USER) 4228c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_VSI_CTL_SUPER_NPRIV; 4238c2ecf20Sopenharmony_ci if (cycle & VME_PROG) 4248c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_VSI_CTL_PGM_PGM; 4258c2ecf20Sopenharmony_ci if (cycle & VME_DATA) 4268c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_VSI_CTL_PGM_DATA; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* Write ctl reg without enable */ 4298c2ecf20Sopenharmony_ci iowrite32(temp_ctl, bridge->base + CA91CX42_VSI_CTL[i]); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (enabled) 4328c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_VSI_CTL_EN; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci iowrite32(temp_ctl, bridge->base + CA91CX42_VSI_CTL[i]); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci return 0; 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic int ca91cx42_slave_get(struct vme_slave_resource *image, int *enabled, 4408c2ecf20Sopenharmony_ci unsigned long long *vme_base, unsigned long long *size, 4418c2ecf20Sopenharmony_ci dma_addr_t *pci_base, u32 *aspace, u32 *cycle) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci unsigned int i, granularity = 0, ctl = 0; 4448c2ecf20Sopenharmony_ci unsigned long long vme_bound, pci_offset; 4458c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci bridge = image->parent->driver_priv; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci i = image->number; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if ((i == 0) || (i == 4)) 4528c2ecf20Sopenharmony_ci granularity = 0x1000; 4538c2ecf20Sopenharmony_ci else 4548c2ecf20Sopenharmony_ci granularity = 0x10000; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* Read Registers */ 4578c2ecf20Sopenharmony_ci ctl = ioread32(bridge->base + CA91CX42_VSI_CTL[i]); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci *vme_base = ioread32(bridge->base + CA91CX42_VSI_BS[i]); 4608c2ecf20Sopenharmony_ci vme_bound = ioread32(bridge->base + CA91CX42_VSI_BD[i]); 4618c2ecf20Sopenharmony_ci pci_offset = ioread32(bridge->base + CA91CX42_VSI_TO[i]); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci *pci_base = (dma_addr_t)*vme_base + pci_offset; 4648c2ecf20Sopenharmony_ci *size = (unsigned long long)((vme_bound - *vme_base) + granularity); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci *enabled = 0; 4678c2ecf20Sopenharmony_ci *aspace = 0; 4688c2ecf20Sopenharmony_ci *cycle = 0; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (ctl & CA91CX42_VSI_CTL_EN) 4718c2ecf20Sopenharmony_ci *enabled = 1; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if ((ctl & CA91CX42_VSI_CTL_VAS_M) == CA91CX42_VSI_CTL_VAS_A16) 4748c2ecf20Sopenharmony_ci *aspace = VME_A16; 4758c2ecf20Sopenharmony_ci if ((ctl & CA91CX42_VSI_CTL_VAS_M) == CA91CX42_VSI_CTL_VAS_A24) 4768c2ecf20Sopenharmony_ci *aspace = VME_A24; 4778c2ecf20Sopenharmony_ci if ((ctl & CA91CX42_VSI_CTL_VAS_M) == CA91CX42_VSI_CTL_VAS_A32) 4788c2ecf20Sopenharmony_ci *aspace = VME_A32; 4798c2ecf20Sopenharmony_ci if ((ctl & CA91CX42_VSI_CTL_VAS_M) == CA91CX42_VSI_CTL_VAS_USER1) 4808c2ecf20Sopenharmony_ci *aspace = VME_USER1; 4818c2ecf20Sopenharmony_ci if ((ctl & CA91CX42_VSI_CTL_VAS_M) == CA91CX42_VSI_CTL_VAS_USER2) 4828c2ecf20Sopenharmony_ci *aspace = VME_USER2; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (ctl & CA91CX42_VSI_CTL_SUPER_SUPR) 4858c2ecf20Sopenharmony_ci *cycle |= VME_SUPER; 4868c2ecf20Sopenharmony_ci if (ctl & CA91CX42_VSI_CTL_SUPER_NPRIV) 4878c2ecf20Sopenharmony_ci *cycle |= VME_USER; 4888c2ecf20Sopenharmony_ci if (ctl & CA91CX42_VSI_CTL_PGM_PGM) 4898c2ecf20Sopenharmony_ci *cycle |= VME_PROG; 4908c2ecf20Sopenharmony_ci if (ctl & CA91CX42_VSI_CTL_PGM_DATA) 4918c2ecf20Sopenharmony_ci *cycle |= VME_DATA; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci/* 4978c2ecf20Sopenharmony_ci * Allocate and map PCI Resource 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_cistatic int ca91cx42_alloc_resource(struct vme_master_resource *image, 5008c2ecf20Sopenharmony_ci unsigned long long size) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci unsigned long long existing_size; 5038c2ecf20Sopenharmony_ci int retval = 0; 5048c2ecf20Sopenharmony_ci struct pci_dev *pdev; 5058c2ecf20Sopenharmony_ci struct vme_bridge *ca91cx42_bridge; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci ca91cx42_bridge = image->parent; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* Find pci_dev container of dev */ 5108c2ecf20Sopenharmony_ci if (!ca91cx42_bridge->parent) { 5118c2ecf20Sopenharmony_ci dev_err(ca91cx42_bridge->parent, "Dev entry NULL\n"); 5128c2ecf20Sopenharmony_ci return -EINVAL; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci pdev = to_pci_dev(ca91cx42_bridge->parent); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci existing_size = (unsigned long long)(image->bus_resource.end - 5178c2ecf20Sopenharmony_ci image->bus_resource.start); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* If the existing size is OK, return */ 5208c2ecf20Sopenharmony_ci if (existing_size == (size - 1)) 5218c2ecf20Sopenharmony_ci return 0; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (existing_size != 0) { 5248c2ecf20Sopenharmony_ci iounmap(image->kern_base); 5258c2ecf20Sopenharmony_ci image->kern_base = NULL; 5268c2ecf20Sopenharmony_ci kfree(image->bus_resource.name); 5278c2ecf20Sopenharmony_ci release_resource(&image->bus_resource); 5288c2ecf20Sopenharmony_ci memset(&image->bus_resource, 0, sizeof(image->bus_resource)); 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (!image->bus_resource.name) { 5328c2ecf20Sopenharmony_ci image->bus_resource.name = kmalloc(VMENAMSIZ+3, GFP_ATOMIC); 5338c2ecf20Sopenharmony_ci if (!image->bus_resource.name) { 5348c2ecf20Sopenharmony_ci retval = -ENOMEM; 5358c2ecf20Sopenharmony_ci goto err_name; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci sprintf((char *)image->bus_resource.name, "%s.%d", 5408c2ecf20Sopenharmony_ci ca91cx42_bridge->name, image->number); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci image->bus_resource.start = 0; 5438c2ecf20Sopenharmony_ci image->bus_resource.end = (unsigned long)size; 5448c2ecf20Sopenharmony_ci image->bus_resource.flags = IORESOURCE_MEM; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci retval = pci_bus_alloc_resource(pdev->bus, 5478c2ecf20Sopenharmony_ci &image->bus_resource, size, 0x10000, PCIBIOS_MIN_MEM, 5488c2ecf20Sopenharmony_ci 0, NULL, NULL); 5498c2ecf20Sopenharmony_ci if (retval) { 5508c2ecf20Sopenharmony_ci dev_err(ca91cx42_bridge->parent, "Failed to allocate mem " 5518c2ecf20Sopenharmony_ci "resource for window %d size 0x%lx start 0x%lx\n", 5528c2ecf20Sopenharmony_ci image->number, (unsigned long)size, 5538c2ecf20Sopenharmony_ci (unsigned long)image->bus_resource.start); 5548c2ecf20Sopenharmony_ci goto err_resource; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci image->kern_base = ioremap( 5588c2ecf20Sopenharmony_ci image->bus_resource.start, size); 5598c2ecf20Sopenharmony_ci if (!image->kern_base) { 5608c2ecf20Sopenharmony_ci dev_err(ca91cx42_bridge->parent, "Failed to remap resource\n"); 5618c2ecf20Sopenharmony_ci retval = -ENOMEM; 5628c2ecf20Sopenharmony_ci goto err_remap; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci return 0; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cierr_remap: 5688c2ecf20Sopenharmony_ci release_resource(&image->bus_resource); 5698c2ecf20Sopenharmony_cierr_resource: 5708c2ecf20Sopenharmony_ci kfree(image->bus_resource.name); 5718c2ecf20Sopenharmony_ci memset(&image->bus_resource, 0, sizeof(image->bus_resource)); 5728c2ecf20Sopenharmony_cierr_name: 5738c2ecf20Sopenharmony_ci return retval; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci/* 5778c2ecf20Sopenharmony_ci * Free and unmap PCI Resource 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_cistatic void ca91cx42_free_resource(struct vme_master_resource *image) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci iounmap(image->kern_base); 5828c2ecf20Sopenharmony_ci image->kern_base = NULL; 5838c2ecf20Sopenharmony_ci release_resource(&image->bus_resource); 5848c2ecf20Sopenharmony_ci kfree(image->bus_resource.name); 5858c2ecf20Sopenharmony_ci memset(&image->bus_resource, 0, sizeof(image->bus_resource)); 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic int ca91cx42_master_set(struct vme_master_resource *image, int enabled, 5908c2ecf20Sopenharmony_ci unsigned long long vme_base, unsigned long long size, u32 aspace, 5918c2ecf20Sopenharmony_ci u32 cycle, u32 dwidth) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci int retval = 0; 5948c2ecf20Sopenharmony_ci unsigned int i, granularity = 0; 5958c2ecf20Sopenharmony_ci unsigned int temp_ctl = 0; 5968c2ecf20Sopenharmony_ci unsigned long long pci_bound, vme_offset, pci_base; 5978c2ecf20Sopenharmony_ci struct vme_bridge *ca91cx42_bridge; 5988c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci ca91cx42_bridge = image->parent; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci bridge = ca91cx42_bridge->driver_priv; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci i = image->number; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci if ((i == 0) || (i == 4)) 6078c2ecf20Sopenharmony_ci granularity = 0x1000; 6088c2ecf20Sopenharmony_ci else 6098c2ecf20Sopenharmony_ci granularity = 0x10000; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* Verify input data */ 6128c2ecf20Sopenharmony_ci if (vme_base & (granularity - 1)) { 6138c2ecf20Sopenharmony_ci dev_err(ca91cx42_bridge->parent, "Invalid VME Window " 6148c2ecf20Sopenharmony_ci "alignment\n"); 6158c2ecf20Sopenharmony_ci retval = -EINVAL; 6168c2ecf20Sopenharmony_ci goto err_window; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci if (size & (granularity - 1)) { 6198c2ecf20Sopenharmony_ci dev_err(ca91cx42_bridge->parent, "Invalid VME Window " 6208c2ecf20Sopenharmony_ci "alignment\n"); 6218c2ecf20Sopenharmony_ci retval = -EINVAL; 6228c2ecf20Sopenharmony_ci goto err_window; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci spin_lock(&image->lock); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* 6288c2ecf20Sopenharmony_ci * Let's allocate the resource here rather than further up the stack as 6298c2ecf20Sopenharmony_ci * it avoids pushing loads of bus dependent stuff up the stack 6308c2ecf20Sopenharmony_ci */ 6318c2ecf20Sopenharmony_ci retval = ca91cx42_alloc_resource(image, size); 6328c2ecf20Sopenharmony_ci if (retval) { 6338c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 6348c2ecf20Sopenharmony_ci dev_err(ca91cx42_bridge->parent, "Unable to allocate memory " 6358c2ecf20Sopenharmony_ci "for resource name\n"); 6368c2ecf20Sopenharmony_ci retval = -ENOMEM; 6378c2ecf20Sopenharmony_ci goto err_res; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci pci_base = (unsigned long long)image->bus_resource.start; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* 6438c2ecf20Sopenharmony_ci * Bound address is a valid address for the window, adjust 6448c2ecf20Sopenharmony_ci * according to window granularity. 6458c2ecf20Sopenharmony_ci */ 6468c2ecf20Sopenharmony_ci pci_bound = pci_base + size; 6478c2ecf20Sopenharmony_ci vme_offset = vme_base - pci_base; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* Disable while we are mucking around */ 6508c2ecf20Sopenharmony_ci temp_ctl = ioread32(bridge->base + CA91CX42_LSI_CTL[i]); 6518c2ecf20Sopenharmony_ci temp_ctl &= ~CA91CX42_LSI_CTL_EN; 6528c2ecf20Sopenharmony_ci iowrite32(temp_ctl, bridge->base + CA91CX42_LSI_CTL[i]); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* Setup cycle types */ 6558c2ecf20Sopenharmony_ci temp_ctl &= ~CA91CX42_LSI_CTL_VCT_M; 6568c2ecf20Sopenharmony_ci if (cycle & VME_BLT) 6578c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_LSI_CTL_VCT_BLT; 6588c2ecf20Sopenharmony_ci if (cycle & VME_MBLT) 6598c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_LSI_CTL_VCT_MBLT; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci /* Setup data width */ 6628c2ecf20Sopenharmony_ci temp_ctl &= ~CA91CX42_LSI_CTL_VDW_M; 6638c2ecf20Sopenharmony_ci switch (dwidth) { 6648c2ecf20Sopenharmony_ci case VME_D8: 6658c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_LSI_CTL_VDW_D8; 6668c2ecf20Sopenharmony_ci break; 6678c2ecf20Sopenharmony_ci case VME_D16: 6688c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_LSI_CTL_VDW_D16; 6698c2ecf20Sopenharmony_ci break; 6708c2ecf20Sopenharmony_ci case VME_D32: 6718c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_LSI_CTL_VDW_D32; 6728c2ecf20Sopenharmony_ci break; 6738c2ecf20Sopenharmony_ci case VME_D64: 6748c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_LSI_CTL_VDW_D64; 6758c2ecf20Sopenharmony_ci break; 6768c2ecf20Sopenharmony_ci default: 6778c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 6788c2ecf20Sopenharmony_ci dev_err(ca91cx42_bridge->parent, "Invalid data width\n"); 6798c2ecf20Sopenharmony_ci retval = -EINVAL; 6808c2ecf20Sopenharmony_ci goto err_dwidth; 6818c2ecf20Sopenharmony_ci break; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* Setup address space */ 6858c2ecf20Sopenharmony_ci temp_ctl &= ~CA91CX42_LSI_CTL_VAS_M; 6868c2ecf20Sopenharmony_ci switch (aspace) { 6878c2ecf20Sopenharmony_ci case VME_A16: 6888c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_LSI_CTL_VAS_A16; 6898c2ecf20Sopenharmony_ci break; 6908c2ecf20Sopenharmony_ci case VME_A24: 6918c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_LSI_CTL_VAS_A24; 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci case VME_A32: 6948c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_LSI_CTL_VAS_A32; 6958c2ecf20Sopenharmony_ci break; 6968c2ecf20Sopenharmony_ci case VME_CRCSR: 6978c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_LSI_CTL_VAS_CRCSR; 6988c2ecf20Sopenharmony_ci break; 6998c2ecf20Sopenharmony_ci case VME_USER1: 7008c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_LSI_CTL_VAS_USER1; 7018c2ecf20Sopenharmony_ci break; 7028c2ecf20Sopenharmony_ci case VME_USER2: 7038c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_LSI_CTL_VAS_USER2; 7048c2ecf20Sopenharmony_ci break; 7058c2ecf20Sopenharmony_ci case VME_A64: 7068c2ecf20Sopenharmony_ci case VME_USER3: 7078c2ecf20Sopenharmony_ci case VME_USER4: 7088c2ecf20Sopenharmony_ci default: 7098c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 7108c2ecf20Sopenharmony_ci dev_err(ca91cx42_bridge->parent, "Invalid address space\n"); 7118c2ecf20Sopenharmony_ci retval = -EINVAL; 7128c2ecf20Sopenharmony_ci goto err_aspace; 7138c2ecf20Sopenharmony_ci break; 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci temp_ctl &= ~(CA91CX42_LSI_CTL_PGM_M | CA91CX42_LSI_CTL_SUPER_M); 7178c2ecf20Sopenharmony_ci if (cycle & VME_SUPER) 7188c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_LSI_CTL_SUPER_SUPR; 7198c2ecf20Sopenharmony_ci if (cycle & VME_PROG) 7208c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_LSI_CTL_PGM_PGM; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci /* Setup mapping */ 7238c2ecf20Sopenharmony_ci iowrite32(pci_base, bridge->base + CA91CX42_LSI_BS[i]); 7248c2ecf20Sopenharmony_ci iowrite32(pci_bound, bridge->base + CA91CX42_LSI_BD[i]); 7258c2ecf20Sopenharmony_ci iowrite32(vme_offset, bridge->base + CA91CX42_LSI_TO[i]); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* Write ctl reg without enable */ 7288c2ecf20Sopenharmony_ci iowrite32(temp_ctl, bridge->base + CA91CX42_LSI_CTL[i]); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (enabled) 7318c2ecf20Sopenharmony_ci temp_ctl |= CA91CX42_LSI_CTL_EN; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci iowrite32(temp_ctl, bridge->base + CA91CX42_LSI_CTL[i]); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 7368c2ecf20Sopenharmony_ci return 0; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cierr_aspace: 7398c2ecf20Sopenharmony_cierr_dwidth: 7408c2ecf20Sopenharmony_ci ca91cx42_free_resource(image); 7418c2ecf20Sopenharmony_cierr_res: 7428c2ecf20Sopenharmony_cierr_window: 7438c2ecf20Sopenharmony_ci return retval; 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic int __ca91cx42_master_get(struct vme_master_resource *image, 7478c2ecf20Sopenharmony_ci int *enabled, unsigned long long *vme_base, unsigned long long *size, 7488c2ecf20Sopenharmony_ci u32 *aspace, u32 *cycle, u32 *dwidth) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci unsigned int i, ctl; 7518c2ecf20Sopenharmony_ci unsigned long long pci_base, pci_bound, vme_offset; 7528c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci bridge = image->parent->driver_priv; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci i = image->number; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci ctl = ioread32(bridge->base + CA91CX42_LSI_CTL[i]); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci pci_base = ioread32(bridge->base + CA91CX42_LSI_BS[i]); 7618c2ecf20Sopenharmony_ci vme_offset = ioread32(bridge->base + CA91CX42_LSI_TO[i]); 7628c2ecf20Sopenharmony_ci pci_bound = ioread32(bridge->base + CA91CX42_LSI_BD[i]); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci *vme_base = pci_base + vme_offset; 7658c2ecf20Sopenharmony_ci *size = (unsigned long long)(pci_bound - pci_base); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci *enabled = 0; 7688c2ecf20Sopenharmony_ci *aspace = 0; 7698c2ecf20Sopenharmony_ci *cycle = 0; 7708c2ecf20Sopenharmony_ci *dwidth = 0; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (ctl & CA91CX42_LSI_CTL_EN) 7738c2ecf20Sopenharmony_ci *enabled = 1; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* Setup address space */ 7768c2ecf20Sopenharmony_ci switch (ctl & CA91CX42_LSI_CTL_VAS_M) { 7778c2ecf20Sopenharmony_ci case CA91CX42_LSI_CTL_VAS_A16: 7788c2ecf20Sopenharmony_ci *aspace = VME_A16; 7798c2ecf20Sopenharmony_ci break; 7808c2ecf20Sopenharmony_ci case CA91CX42_LSI_CTL_VAS_A24: 7818c2ecf20Sopenharmony_ci *aspace = VME_A24; 7828c2ecf20Sopenharmony_ci break; 7838c2ecf20Sopenharmony_ci case CA91CX42_LSI_CTL_VAS_A32: 7848c2ecf20Sopenharmony_ci *aspace = VME_A32; 7858c2ecf20Sopenharmony_ci break; 7868c2ecf20Sopenharmony_ci case CA91CX42_LSI_CTL_VAS_CRCSR: 7878c2ecf20Sopenharmony_ci *aspace = VME_CRCSR; 7888c2ecf20Sopenharmony_ci break; 7898c2ecf20Sopenharmony_ci case CA91CX42_LSI_CTL_VAS_USER1: 7908c2ecf20Sopenharmony_ci *aspace = VME_USER1; 7918c2ecf20Sopenharmony_ci break; 7928c2ecf20Sopenharmony_ci case CA91CX42_LSI_CTL_VAS_USER2: 7938c2ecf20Sopenharmony_ci *aspace = VME_USER2; 7948c2ecf20Sopenharmony_ci break; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci /* XXX Not sure howto check for MBLT */ 7988c2ecf20Sopenharmony_ci /* Setup cycle types */ 7998c2ecf20Sopenharmony_ci if (ctl & CA91CX42_LSI_CTL_VCT_BLT) 8008c2ecf20Sopenharmony_ci *cycle |= VME_BLT; 8018c2ecf20Sopenharmony_ci else 8028c2ecf20Sopenharmony_ci *cycle |= VME_SCT; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci if (ctl & CA91CX42_LSI_CTL_SUPER_SUPR) 8058c2ecf20Sopenharmony_ci *cycle |= VME_SUPER; 8068c2ecf20Sopenharmony_ci else 8078c2ecf20Sopenharmony_ci *cycle |= VME_USER; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci if (ctl & CA91CX42_LSI_CTL_PGM_PGM) 8108c2ecf20Sopenharmony_ci *cycle = VME_PROG; 8118c2ecf20Sopenharmony_ci else 8128c2ecf20Sopenharmony_ci *cycle = VME_DATA; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* Setup data width */ 8158c2ecf20Sopenharmony_ci switch (ctl & CA91CX42_LSI_CTL_VDW_M) { 8168c2ecf20Sopenharmony_ci case CA91CX42_LSI_CTL_VDW_D8: 8178c2ecf20Sopenharmony_ci *dwidth = VME_D8; 8188c2ecf20Sopenharmony_ci break; 8198c2ecf20Sopenharmony_ci case CA91CX42_LSI_CTL_VDW_D16: 8208c2ecf20Sopenharmony_ci *dwidth = VME_D16; 8218c2ecf20Sopenharmony_ci break; 8228c2ecf20Sopenharmony_ci case CA91CX42_LSI_CTL_VDW_D32: 8238c2ecf20Sopenharmony_ci *dwidth = VME_D32; 8248c2ecf20Sopenharmony_ci break; 8258c2ecf20Sopenharmony_ci case CA91CX42_LSI_CTL_VDW_D64: 8268c2ecf20Sopenharmony_ci *dwidth = VME_D64; 8278c2ecf20Sopenharmony_ci break; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci return 0; 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic int ca91cx42_master_get(struct vme_master_resource *image, int *enabled, 8348c2ecf20Sopenharmony_ci unsigned long long *vme_base, unsigned long long *size, u32 *aspace, 8358c2ecf20Sopenharmony_ci u32 *cycle, u32 *dwidth) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci int retval; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci spin_lock(&image->lock); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci retval = __ca91cx42_master_get(image, enabled, vme_base, size, aspace, 8428c2ecf20Sopenharmony_ci cycle, dwidth); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci return retval; 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic ssize_t ca91cx42_master_read(struct vme_master_resource *image, 8508c2ecf20Sopenharmony_ci void *buf, size_t count, loff_t offset) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci ssize_t retval; 8538c2ecf20Sopenharmony_ci void __iomem *addr = image->kern_base + offset; 8548c2ecf20Sopenharmony_ci unsigned int done = 0; 8558c2ecf20Sopenharmony_ci unsigned int count32; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (count == 0) 8588c2ecf20Sopenharmony_ci return 0; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci spin_lock(&image->lock); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* The following code handles VME address alignment. We cannot use 8638c2ecf20Sopenharmony_ci * memcpy_xxx here because it may cut data transfers in to 8-bit 8648c2ecf20Sopenharmony_ci * cycles when D16 or D32 cycles are required on the VME bus. 8658c2ecf20Sopenharmony_ci * On the other hand, the bridge itself assures that the maximum data 8668c2ecf20Sopenharmony_ci * cycle configured for the transfer is used and splits it 8678c2ecf20Sopenharmony_ci * automatically for non-aligned addresses, so we don't want the 8688c2ecf20Sopenharmony_ci * overhead of needlessly forcing small transfers for the entire cycle. 8698c2ecf20Sopenharmony_ci */ 8708c2ecf20Sopenharmony_ci if ((uintptr_t)addr & 0x1) { 8718c2ecf20Sopenharmony_ci *(u8 *)buf = ioread8(addr); 8728c2ecf20Sopenharmony_ci done += 1; 8738c2ecf20Sopenharmony_ci if (done == count) 8748c2ecf20Sopenharmony_ci goto out; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci if ((uintptr_t)(addr + done) & 0x2) { 8778c2ecf20Sopenharmony_ci if ((count - done) < 2) { 8788c2ecf20Sopenharmony_ci *(u8 *)(buf + done) = ioread8(addr + done); 8798c2ecf20Sopenharmony_ci done += 1; 8808c2ecf20Sopenharmony_ci goto out; 8818c2ecf20Sopenharmony_ci } else { 8828c2ecf20Sopenharmony_ci *(u16 *)(buf + done) = ioread16(addr + done); 8838c2ecf20Sopenharmony_ci done += 2; 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci count32 = (count - done) & ~0x3; 8888c2ecf20Sopenharmony_ci while (done < count32) { 8898c2ecf20Sopenharmony_ci *(u32 *)(buf + done) = ioread32(addr + done); 8908c2ecf20Sopenharmony_ci done += 4; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci if ((count - done) & 0x2) { 8948c2ecf20Sopenharmony_ci *(u16 *)(buf + done) = ioread16(addr + done); 8958c2ecf20Sopenharmony_ci done += 2; 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci if ((count - done) & 0x1) { 8988c2ecf20Sopenharmony_ci *(u8 *)(buf + done) = ioread8(addr + done); 8998c2ecf20Sopenharmony_ci done += 1; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ciout: 9028c2ecf20Sopenharmony_ci retval = count; 9038c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci return retval; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic ssize_t ca91cx42_master_write(struct vme_master_resource *image, 9098c2ecf20Sopenharmony_ci void *buf, size_t count, loff_t offset) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci ssize_t retval; 9128c2ecf20Sopenharmony_ci void __iomem *addr = image->kern_base + offset; 9138c2ecf20Sopenharmony_ci unsigned int done = 0; 9148c2ecf20Sopenharmony_ci unsigned int count32; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci if (count == 0) 9178c2ecf20Sopenharmony_ci return 0; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci spin_lock(&image->lock); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci /* Here we apply for the same strategy we do in master_read 9228c2ecf20Sopenharmony_ci * function in order to assure the correct cycles. 9238c2ecf20Sopenharmony_ci */ 9248c2ecf20Sopenharmony_ci if ((uintptr_t)addr & 0x1) { 9258c2ecf20Sopenharmony_ci iowrite8(*(u8 *)buf, addr); 9268c2ecf20Sopenharmony_ci done += 1; 9278c2ecf20Sopenharmony_ci if (done == count) 9288c2ecf20Sopenharmony_ci goto out; 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci if ((uintptr_t)(addr + done) & 0x2) { 9318c2ecf20Sopenharmony_ci if ((count - done) < 2) { 9328c2ecf20Sopenharmony_ci iowrite8(*(u8 *)(buf + done), addr + done); 9338c2ecf20Sopenharmony_ci done += 1; 9348c2ecf20Sopenharmony_ci goto out; 9358c2ecf20Sopenharmony_ci } else { 9368c2ecf20Sopenharmony_ci iowrite16(*(u16 *)(buf + done), addr + done); 9378c2ecf20Sopenharmony_ci done += 2; 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci count32 = (count - done) & ~0x3; 9428c2ecf20Sopenharmony_ci while (done < count32) { 9438c2ecf20Sopenharmony_ci iowrite32(*(u32 *)(buf + done), addr + done); 9448c2ecf20Sopenharmony_ci done += 4; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci if ((count - done) & 0x2) { 9488c2ecf20Sopenharmony_ci iowrite16(*(u16 *)(buf + done), addr + done); 9498c2ecf20Sopenharmony_ci done += 2; 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci if ((count - done) & 0x1) { 9528c2ecf20Sopenharmony_ci iowrite8(*(u8 *)(buf + done), addr + done); 9538c2ecf20Sopenharmony_ci done += 1; 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ciout: 9568c2ecf20Sopenharmony_ci retval = count; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci return retval; 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic unsigned int ca91cx42_master_rmw(struct vme_master_resource *image, 9648c2ecf20Sopenharmony_ci unsigned int mask, unsigned int compare, unsigned int swap, 9658c2ecf20Sopenharmony_ci loff_t offset) 9668c2ecf20Sopenharmony_ci{ 9678c2ecf20Sopenharmony_ci u32 result; 9688c2ecf20Sopenharmony_ci uintptr_t pci_addr; 9698c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 9708c2ecf20Sopenharmony_ci struct device *dev; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci bridge = image->parent->driver_priv; 9738c2ecf20Sopenharmony_ci dev = image->parent->parent; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci /* Find the PCI address that maps to the desired VME address */ 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci /* Locking as we can only do one of these at a time */ 9788c2ecf20Sopenharmony_ci mutex_lock(&bridge->vme_rmw); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* Lock image */ 9818c2ecf20Sopenharmony_ci spin_lock(&image->lock); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci pci_addr = (uintptr_t)image->kern_base + offset; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci /* Address must be 4-byte aligned */ 9868c2ecf20Sopenharmony_ci if (pci_addr & 0x3) { 9878c2ecf20Sopenharmony_ci dev_err(dev, "RMW Address not 4-byte aligned\n"); 9888c2ecf20Sopenharmony_ci result = -EINVAL; 9898c2ecf20Sopenharmony_ci goto out; 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci /* Ensure RMW Disabled whilst configuring */ 9938c2ecf20Sopenharmony_ci iowrite32(0, bridge->base + SCYC_CTL); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci /* Configure registers */ 9968c2ecf20Sopenharmony_ci iowrite32(mask, bridge->base + SCYC_EN); 9978c2ecf20Sopenharmony_ci iowrite32(compare, bridge->base + SCYC_CMP); 9988c2ecf20Sopenharmony_ci iowrite32(swap, bridge->base + SCYC_SWP); 9998c2ecf20Sopenharmony_ci iowrite32(pci_addr, bridge->base + SCYC_ADDR); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci /* Enable RMW */ 10028c2ecf20Sopenharmony_ci iowrite32(CA91CX42_SCYC_CTL_CYC_RMW, bridge->base + SCYC_CTL); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci /* Kick process off with a read to the required address. */ 10058c2ecf20Sopenharmony_ci result = ioread32(image->kern_base + offset); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci /* Disable RMW */ 10088c2ecf20Sopenharmony_ci iowrite32(0, bridge->base + SCYC_CTL); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ciout: 10118c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci mutex_unlock(&bridge->vme_rmw); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci return result; 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_cistatic int ca91cx42_dma_list_add(struct vme_dma_list *list, 10198c2ecf20Sopenharmony_ci struct vme_dma_attr *src, struct vme_dma_attr *dest, size_t count) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci struct ca91cx42_dma_entry *entry, *prev; 10228c2ecf20Sopenharmony_ci struct vme_dma_pci *pci_attr; 10238c2ecf20Sopenharmony_ci struct vme_dma_vme *vme_attr; 10248c2ecf20Sopenharmony_ci dma_addr_t desc_ptr; 10258c2ecf20Sopenharmony_ci int retval = 0; 10268c2ecf20Sopenharmony_ci struct device *dev; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci dev = list->parent->parent->parent; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* XXX descriptor must be aligned on 64-bit boundaries */ 10318c2ecf20Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 10328c2ecf20Sopenharmony_ci if (!entry) { 10338c2ecf20Sopenharmony_ci retval = -ENOMEM; 10348c2ecf20Sopenharmony_ci goto err_mem; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci /* Test descriptor alignment */ 10388c2ecf20Sopenharmony_ci if ((unsigned long)&entry->descriptor & CA91CX42_DCPP_M) { 10398c2ecf20Sopenharmony_ci dev_err(dev, "Descriptor not aligned to 16 byte boundary as " 10408c2ecf20Sopenharmony_ci "required: %p\n", &entry->descriptor); 10418c2ecf20Sopenharmony_ci retval = -EINVAL; 10428c2ecf20Sopenharmony_ci goto err_align; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci memset(&entry->descriptor, 0, sizeof(entry->descriptor)); 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci if (dest->type == VME_DMA_VME) { 10488c2ecf20Sopenharmony_ci entry->descriptor.dctl |= CA91CX42_DCTL_L2V; 10498c2ecf20Sopenharmony_ci vme_attr = dest->private; 10508c2ecf20Sopenharmony_ci pci_attr = src->private; 10518c2ecf20Sopenharmony_ci } else { 10528c2ecf20Sopenharmony_ci vme_attr = src->private; 10538c2ecf20Sopenharmony_ci pci_attr = dest->private; 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci /* Check we can do fulfill required attributes */ 10578c2ecf20Sopenharmony_ci if ((vme_attr->aspace & ~(VME_A16 | VME_A24 | VME_A32 | VME_USER1 | 10588c2ecf20Sopenharmony_ci VME_USER2)) != 0) { 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci dev_err(dev, "Unsupported cycle type\n"); 10618c2ecf20Sopenharmony_ci retval = -EINVAL; 10628c2ecf20Sopenharmony_ci goto err_aspace; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci if ((vme_attr->cycle & ~(VME_SCT | VME_BLT | VME_SUPER | VME_USER | 10668c2ecf20Sopenharmony_ci VME_PROG | VME_DATA)) != 0) { 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci dev_err(dev, "Unsupported cycle type\n"); 10698c2ecf20Sopenharmony_ci retval = -EINVAL; 10708c2ecf20Sopenharmony_ci goto err_cycle; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci /* Check to see if we can fulfill source and destination */ 10748c2ecf20Sopenharmony_ci if (!(((src->type == VME_DMA_PCI) && (dest->type == VME_DMA_VME)) || 10758c2ecf20Sopenharmony_ci ((src->type == VME_DMA_VME) && (dest->type == VME_DMA_PCI)))) { 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci dev_err(dev, "Cannot perform transfer with this " 10788c2ecf20Sopenharmony_ci "source-destination combination\n"); 10798c2ecf20Sopenharmony_ci retval = -EINVAL; 10808c2ecf20Sopenharmony_ci goto err_direct; 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci /* Setup cycle types */ 10848c2ecf20Sopenharmony_ci if (vme_attr->cycle & VME_BLT) 10858c2ecf20Sopenharmony_ci entry->descriptor.dctl |= CA91CX42_DCTL_VCT_BLT; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci /* Setup data width */ 10888c2ecf20Sopenharmony_ci switch (vme_attr->dwidth) { 10898c2ecf20Sopenharmony_ci case VME_D8: 10908c2ecf20Sopenharmony_ci entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D8; 10918c2ecf20Sopenharmony_ci break; 10928c2ecf20Sopenharmony_ci case VME_D16: 10938c2ecf20Sopenharmony_ci entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D16; 10948c2ecf20Sopenharmony_ci break; 10958c2ecf20Sopenharmony_ci case VME_D32: 10968c2ecf20Sopenharmony_ci entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D32; 10978c2ecf20Sopenharmony_ci break; 10988c2ecf20Sopenharmony_ci case VME_D64: 10998c2ecf20Sopenharmony_ci entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D64; 11008c2ecf20Sopenharmony_ci break; 11018c2ecf20Sopenharmony_ci default: 11028c2ecf20Sopenharmony_ci dev_err(dev, "Invalid data width\n"); 11038c2ecf20Sopenharmony_ci return -EINVAL; 11048c2ecf20Sopenharmony_ci } 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci /* Setup address space */ 11078c2ecf20Sopenharmony_ci switch (vme_attr->aspace) { 11088c2ecf20Sopenharmony_ci case VME_A16: 11098c2ecf20Sopenharmony_ci entry->descriptor.dctl |= CA91CX42_DCTL_VAS_A16; 11108c2ecf20Sopenharmony_ci break; 11118c2ecf20Sopenharmony_ci case VME_A24: 11128c2ecf20Sopenharmony_ci entry->descriptor.dctl |= CA91CX42_DCTL_VAS_A24; 11138c2ecf20Sopenharmony_ci break; 11148c2ecf20Sopenharmony_ci case VME_A32: 11158c2ecf20Sopenharmony_ci entry->descriptor.dctl |= CA91CX42_DCTL_VAS_A32; 11168c2ecf20Sopenharmony_ci break; 11178c2ecf20Sopenharmony_ci case VME_USER1: 11188c2ecf20Sopenharmony_ci entry->descriptor.dctl |= CA91CX42_DCTL_VAS_USER1; 11198c2ecf20Sopenharmony_ci break; 11208c2ecf20Sopenharmony_ci case VME_USER2: 11218c2ecf20Sopenharmony_ci entry->descriptor.dctl |= CA91CX42_DCTL_VAS_USER2; 11228c2ecf20Sopenharmony_ci break; 11238c2ecf20Sopenharmony_ci default: 11248c2ecf20Sopenharmony_ci dev_err(dev, "Invalid address space\n"); 11258c2ecf20Sopenharmony_ci return -EINVAL; 11268c2ecf20Sopenharmony_ci break; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci if (vme_attr->cycle & VME_SUPER) 11308c2ecf20Sopenharmony_ci entry->descriptor.dctl |= CA91CX42_DCTL_SUPER_SUPR; 11318c2ecf20Sopenharmony_ci if (vme_attr->cycle & VME_PROG) 11328c2ecf20Sopenharmony_ci entry->descriptor.dctl |= CA91CX42_DCTL_PGM_PGM; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci entry->descriptor.dtbc = count; 11358c2ecf20Sopenharmony_ci entry->descriptor.dla = pci_attr->address; 11368c2ecf20Sopenharmony_ci entry->descriptor.dva = vme_attr->address; 11378c2ecf20Sopenharmony_ci entry->descriptor.dcpp = CA91CX42_DCPP_NULL; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci /* Add to list */ 11408c2ecf20Sopenharmony_ci list_add_tail(&entry->list, &list->entries); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci /* Fill out previous descriptors "Next Address" */ 11438c2ecf20Sopenharmony_ci if (entry->list.prev != &list->entries) { 11448c2ecf20Sopenharmony_ci prev = list_entry(entry->list.prev, struct ca91cx42_dma_entry, 11458c2ecf20Sopenharmony_ci list); 11468c2ecf20Sopenharmony_ci /* We need the bus address for the pointer */ 11478c2ecf20Sopenharmony_ci desc_ptr = virt_to_bus(&entry->descriptor); 11488c2ecf20Sopenharmony_ci prev->descriptor.dcpp = desc_ptr & ~CA91CX42_DCPP_M; 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci return 0; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_cierr_cycle: 11548c2ecf20Sopenharmony_cierr_aspace: 11558c2ecf20Sopenharmony_cierr_direct: 11568c2ecf20Sopenharmony_cierr_align: 11578c2ecf20Sopenharmony_ci kfree(entry); 11588c2ecf20Sopenharmony_cierr_mem: 11598c2ecf20Sopenharmony_ci return retval; 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_cistatic int ca91cx42_dma_busy(struct vme_bridge *ca91cx42_bridge) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci u32 tmp; 11658c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci bridge = ca91cx42_bridge->driver_priv; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci tmp = ioread32(bridge->base + DGCS); 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (tmp & CA91CX42_DGCS_ACT) 11728c2ecf20Sopenharmony_ci return 0; 11738c2ecf20Sopenharmony_ci else 11748c2ecf20Sopenharmony_ci return 1; 11758c2ecf20Sopenharmony_ci} 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_cistatic int ca91cx42_dma_list_exec(struct vme_dma_list *list) 11788c2ecf20Sopenharmony_ci{ 11798c2ecf20Sopenharmony_ci struct vme_dma_resource *ctrlr; 11808c2ecf20Sopenharmony_ci struct ca91cx42_dma_entry *entry; 11818c2ecf20Sopenharmony_ci int retval; 11828c2ecf20Sopenharmony_ci dma_addr_t bus_addr; 11838c2ecf20Sopenharmony_ci u32 val; 11848c2ecf20Sopenharmony_ci struct device *dev; 11858c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci ctrlr = list->parent; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci bridge = ctrlr->parent->driver_priv; 11908c2ecf20Sopenharmony_ci dev = ctrlr->parent->parent; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci mutex_lock(&ctrlr->mtx); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci if (!(list_empty(&ctrlr->running))) { 11958c2ecf20Sopenharmony_ci /* 11968c2ecf20Sopenharmony_ci * XXX We have an active DMA transfer and currently haven't 11978c2ecf20Sopenharmony_ci * sorted out the mechanism for "pending" DMA transfers. 11988c2ecf20Sopenharmony_ci * Return busy. 11998c2ecf20Sopenharmony_ci */ 12008c2ecf20Sopenharmony_ci /* Need to add to pending here */ 12018c2ecf20Sopenharmony_ci mutex_unlock(&ctrlr->mtx); 12028c2ecf20Sopenharmony_ci return -EBUSY; 12038c2ecf20Sopenharmony_ci } else { 12048c2ecf20Sopenharmony_ci list_add(&list->list, &ctrlr->running); 12058c2ecf20Sopenharmony_ci } 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci /* Get first bus address and write into registers */ 12088c2ecf20Sopenharmony_ci entry = list_first_entry(&list->entries, struct ca91cx42_dma_entry, 12098c2ecf20Sopenharmony_ci list); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci bus_addr = virt_to_bus(&entry->descriptor); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci mutex_unlock(&ctrlr->mtx); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci iowrite32(0, bridge->base + DTBC); 12168c2ecf20Sopenharmony_ci iowrite32(bus_addr & ~CA91CX42_DCPP_M, bridge->base + DCPP); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci /* Start the operation */ 12198c2ecf20Sopenharmony_ci val = ioread32(bridge->base + DGCS); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci /* XXX Could set VMEbus On and Off Counters here */ 12228c2ecf20Sopenharmony_ci val &= (CA91CX42_DGCS_VON_M | CA91CX42_DGCS_VOFF_M); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci val |= (CA91CX42_DGCS_CHAIN | CA91CX42_DGCS_STOP | CA91CX42_DGCS_HALT | 12258c2ecf20Sopenharmony_ci CA91CX42_DGCS_DONE | CA91CX42_DGCS_LERR | CA91CX42_DGCS_VERR | 12268c2ecf20Sopenharmony_ci CA91CX42_DGCS_PERR); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci iowrite32(val, bridge->base + DGCS); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci val |= CA91CX42_DGCS_GO; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci iowrite32(val, bridge->base + DGCS); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci retval = wait_event_interruptible(bridge->dma_queue, 12358c2ecf20Sopenharmony_ci ca91cx42_dma_busy(ctrlr->parent)); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci if (retval) { 12388c2ecf20Sopenharmony_ci val = ioread32(bridge->base + DGCS); 12398c2ecf20Sopenharmony_ci iowrite32(val | CA91CX42_DGCS_STOP_REQ, bridge->base + DGCS); 12408c2ecf20Sopenharmony_ci /* Wait for the operation to abort */ 12418c2ecf20Sopenharmony_ci wait_event(bridge->dma_queue, 12428c2ecf20Sopenharmony_ci ca91cx42_dma_busy(ctrlr->parent)); 12438c2ecf20Sopenharmony_ci retval = -EINTR; 12448c2ecf20Sopenharmony_ci goto exit; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci /* 12488c2ecf20Sopenharmony_ci * Read status register, this register is valid until we kick off a 12498c2ecf20Sopenharmony_ci * new transfer. 12508c2ecf20Sopenharmony_ci */ 12518c2ecf20Sopenharmony_ci val = ioread32(bridge->base + DGCS); 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci if (val & (CA91CX42_DGCS_LERR | CA91CX42_DGCS_VERR | 12548c2ecf20Sopenharmony_ci CA91CX42_DGCS_PERR)) { 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci dev_err(dev, "ca91c042: DMA Error. DGCS=%08X\n", val); 12578c2ecf20Sopenharmony_ci val = ioread32(bridge->base + DCTL); 12588c2ecf20Sopenharmony_ci retval = -EIO; 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ciexit: 12628c2ecf20Sopenharmony_ci /* Remove list from running list */ 12638c2ecf20Sopenharmony_ci mutex_lock(&ctrlr->mtx); 12648c2ecf20Sopenharmony_ci list_del(&list->list); 12658c2ecf20Sopenharmony_ci mutex_unlock(&ctrlr->mtx); 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci return retval; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci} 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_cistatic int ca91cx42_dma_list_empty(struct vme_dma_list *list) 12728c2ecf20Sopenharmony_ci{ 12738c2ecf20Sopenharmony_ci struct list_head *pos, *temp; 12748c2ecf20Sopenharmony_ci struct ca91cx42_dma_entry *entry; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci /* detach and free each entry */ 12778c2ecf20Sopenharmony_ci list_for_each_safe(pos, temp, &list->entries) { 12788c2ecf20Sopenharmony_ci list_del(pos); 12798c2ecf20Sopenharmony_ci entry = list_entry(pos, struct ca91cx42_dma_entry, list); 12808c2ecf20Sopenharmony_ci kfree(entry); 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci return 0; 12848c2ecf20Sopenharmony_ci} 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci/* 12878c2ecf20Sopenharmony_ci * All 4 location monitors reside at the same base - this is therefore a 12888c2ecf20Sopenharmony_ci * system wide configuration. 12898c2ecf20Sopenharmony_ci * 12908c2ecf20Sopenharmony_ci * This does not enable the LM monitor - that should be done when the first 12918c2ecf20Sopenharmony_ci * callback is attached and disabled when the last callback is removed. 12928c2ecf20Sopenharmony_ci */ 12938c2ecf20Sopenharmony_cistatic int ca91cx42_lm_set(struct vme_lm_resource *lm, 12948c2ecf20Sopenharmony_ci unsigned long long lm_base, u32 aspace, u32 cycle) 12958c2ecf20Sopenharmony_ci{ 12968c2ecf20Sopenharmony_ci u32 temp_base, lm_ctl = 0; 12978c2ecf20Sopenharmony_ci int i; 12988c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 12998c2ecf20Sopenharmony_ci struct device *dev; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci bridge = lm->parent->driver_priv; 13028c2ecf20Sopenharmony_ci dev = lm->parent->parent; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci /* Check the alignment of the location monitor */ 13058c2ecf20Sopenharmony_ci temp_base = (u32)lm_base; 13068c2ecf20Sopenharmony_ci if (temp_base & 0xffff) { 13078c2ecf20Sopenharmony_ci dev_err(dev, "Location monitor must be aligned to 64KB " 13088c2ecf20Sopenharmony_ci "boundary"); 13098c2ecf20Sopenharmony_ci return -EINVAL; 13108c2ecf20Sopenharmony_ci } 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci mutex_lock(&lm->mtx); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci /* If we already have a callback attached, we can't move it! */ 13158c2ecf20Sopenharmony_ci for (i = 0; i < lm->monitors; i++) { 13168c2ecf20Sopenharmony_ci if (bridge->lm_callback[i]) { 13178c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 13188c2ecf20Sopenharmony_ci dev_err(dev, "Location monitor callback attached, " 13198c2ecf20Sopenharmony_ci "can't reset\n"); 13208c2ecf20Sopenharmony_ci return -EBUSY; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci } 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci switch (aspace) { 13258c2ecf20Sopenharmony_ci case VME_A16: 13268c2ecf20Sopenharmony_ci lm_ctl |= CA91CX42_LM_CTL_AS_A16; 13278c2ecf20Sopenharmony_ci break; 13288c2ecf20Sopenharmony_ci case VME_A24: 13298c2ecf20Sopenharmony_ci lm_ctl |= CA91CX42_LM_CTL_AS_A24; 13308c2ecf20Sopenharmony_ci break; 13318c2ecf20Sopenharmony_ci case VME_A32: 13328c2ecf20Sopenharmony_ci lm_ctl |= CA91CX42_LM_CTL_AS_A32; 13338c2ecf20Sopenharmony_ci break; 13348c2ecf20Sopenharmony_ci default: 13358c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 13368c2ecf20Sopenharmony_ci dev_err(dev, "Invalid address space\n"); 13378c2ecf20Sopenharmony_ci return -EINVAL; 13388c2ecf20Sopenharmony_ci break; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci if (cycle & VME_SUPER) 13428c2ecf20Sopenharmony_ci lm_ctl |= CA91CX42_LM_CTL_SUPR; 13438c2ecf20Sopenharmony_ci if (cycle & VME_USER) 13448c2ecf20Sopenharmony_ci lm_ctl |= CA91CX42_LM_CTL_NPRIV; 13458c2ecf20Sopenharmony_ci if (cycle & VME_PROG) 13468c2ecf20Sopenharmony_ci lm_ctl |= CA91CX42_LM_CTL_PGM; 13478c2ecf20Sopenharmony_ci if (cycle & VME_DATA) 13488c2ecf20Sopenharmony_ci lm_ctl |= CA91CX42_LM_CTL_DATA; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci iowrite32(lm_base, bridge->base + LM_BS); 13518c2ecf20Sopenharmony_ci iowrite32(lm_ctl, bridge->base + LM_CTL); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci return 0; 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci/* Get configuration of the callback monitor and return whether it is enabled 13598c2ecf20Sopenharmony_ci * or disabled. 13608c2ecf20Sopenharmony_ci */ 13618c2ecf20Sopenharmony_cistatic int ca91cx42_lm_get(struct vme_lm_resource *lm, 13628c2ecf20Sopenharmony_ci unsigned long long *lm_base, u32 *aspace, u32 *cycle) 13638c2ecf20Sopenharmony_ci{ 13648c2ecf20Sopenharmony_ci u32 lm_ctl, enabled = 0; 13658c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci bridge = lm->parent->driver_priv; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci mutex_lock(&lm->mtx); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci *lm_base = (unsigned long long)ioread32(bridge->base + LM_BS); 13728c2ecf20Sopenharmony_ci lm_ctl = ioread32(bridge->base + LM_CTL); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci if (lm_ctl & CA91CX42_LM_CTL_EN) 13758c2ecf20Sopenharmony_ci enabled = 1; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci if ((lm_ctl & CA91CX42_LM_CTL_AS_M) == CA91CX42_LM_CTL_AS_A16) 13788c2ecf20Sopenharmony_ci *aspace = VME_A16; 13798c2ecf20Sopenharmony_ci if ((lm_ctl & CA91CX42_LM_CTL_AS_M) == CA91CX42_LM_CTL_AS_A24) 13808c2ecf20Sopenharmony_ci *aspace = VME_A24; 13818c2ecf20Sopenharmony_ci if ((lm_ctl & CA91CX42_LM_CTL_AS_M) == CA91CX42_LM_CTL_AS_A32) 13828c2ecf20Sopenharmony_ci *aspace = VME_A32; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci *cycle = 0; 13858c2ecf20Sopenharmony_ci if (lm_ctl & CA91CX42_LM_CTL_SUPR) 13868c2ecf20Sopenharmony_ci *cycle |= VME_SUPER; 13878c2ecf20Sopenharmony_ci if (lm_ctl & CA91CX42_LM_CTL_NPRIV) 13888c2ecf20Sopenharmony_ci *cycle |= VME_USER; 13898c2ecf20Sopenharmony_ci if (lm_ctl & CA91CX42_LM_CTL_PGM) 13908c2ecf20Sopenharmony_ci *cycle |= VME_PROG; 13918c2ecf20Sopenharmony_ci if (lm_ctl & CA91CX42_LM_CTL_DATA) 13928c2ecf20Sopenharmony_ci *cycle |= VME_DATA; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci return enabled; 13978c2ecf20Sopenharmony_ci} 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci/* 14008c2ecf20Sopenharmony_ci * Attach a callback to a specific location monitor. 14018c2ecf20Sopenharmony_ci * 14028c2ecf20Sopenharmony_ci * Callback will be passed the monitor triggered. 14038c2ecf20Sopenharmony_ci */ 14048c2ecf20Sopenharmony_cistatic int ca91cx42_lm_attach(struct vme_lm_resource *lm, int monitor, 14058c2ecf20Sopenharmony_ci void (*callback)(void *), void *data) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci u32 lm_ctl, tmp; 14088c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 14098c2ecf20Sopenharmony_ci struct device *dev; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci bridge = lm->parent->driver_priv; 14128c2ecf20Sopenharmony_ci dev = lm->parent->parent; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci mutex_lock(&lm->mtx); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci /* Ensure that the location monitor is configured - need PGM or DATA */ 14178c2ecf20Sopenharmony_ci lm_ctl = ioread32(bridge->base + LM_CTL); 14188c2ecf20Sopenharmony_ci if ((lm_ctl & (CA91CX42_LM_CTL_PGM | CA91CX42_LM_CTL_DATA)) == 0) { 14198c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 14208c2ecf20Sopenharmony_ci dev_err(dev, "Location monitor not properly configured\n"); 14218c2ecf20Sopenharmony_ci return -EINVAL; 14228c2ecf20Sopenharmony_ci } 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci /* Check that a callback isn't already attached */ 14258c2ecf20Sopenharmony_ci if (bridge->lm_callback[monitor]) { 14268c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 14278c2ecf20Sopenharmony_ci dev_err(dev, "Existing callback attached\n"); 14288c2ecf20Sopenharmony_ci return -EBUSY; 14298c2ecf20Sopenharmony_ci } 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci /* Attach callback */ 14328c2ecf20Sopenharmony_ci bridge->lm_callback[monitor] = callback; 14338c2ecf20Sopenharmony_ci bridge->lm_data[monitor] = data; 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci /* Enable Location Monitor interrupt */ 14368c2ecf20Sopenharmony_ci tmp = ioread32(bridge->base + LINT_EN); 14378c2ecf20Sopenharmony_ci tmp |= CA91CX42_LINT_LM[monitor]; 14388c2ecf20Sopenharmony_ci iowrite32(tmp, bridge->base + LINT_EN); 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci /* Ensure that global Location Monitor Enable set */ 14418c2ecf20Sopenharmony_ci if ((lm_ctl & CA91CX42_LM_CTL_EN) == 0) { 14428c2ecf20Sopenharmony_ci lm_ctl |= CA91CX42_LM_CTL_EN; 14438c2ecf20Sopenharmony_ci iowrite32(lm_ctl, bridge->base + LM_CTL); 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci return 0; 14498c2ecf20Sopenharmony_ci} 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci/* 14528c2ecf20Sopenharmony_ci * Detach a callback function forn a specific location monitor. 14538c2ecf20Sopenharmony_ci */ 14548c2ecf20Sopenharmony_cistatic int ca91cx42_lm_detach(struct vme_lm_resource *lm, int monitor) 14558c2ecf20Sopenharmony_ci{ 14568c2ecf20Sopenharmony_ci u32 tmp; 14578c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci bridge = lm->parent->driver_priv; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci mutex_lock(&lm->mtx); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci /* Disable Location Monitor and ensure previous interrupts are clear */ 14648c2ecf20Sopenharmony_ci tmp = ioread32(bridge->base + LINT_EN); 14658c2ecf20Sopenharmony_ci tmp &= ~CA91CX42_LINT_LM[monitor]; 14668c2ecf20Sopenharmony_ci iowrite32(tmp, bridge->base + LINT_EN); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci iowrite32(CA91CX42_LINT_LM[monitor], 14698c2ecf20Sopenharmony_ci bridge->base + LINT_STAT); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci /* Detach callback */ 14728c2ecf20Sopenharmony_ci bridge->lm_callback[monitor] = NULL; 14738c2ecf20Sopenharmony_ci bridge->lm_data[monitor] = NULL; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci /* If all location monitors disabled, disable global Location Monitor */ 14768c2ecf20Sopenharmony_ci if ((tmp & (CA91CX42_LINT_LM0 | CA91CX42_LINT_LM1 | CA91CX42_LINT_LM2 | 14778c2ecf20Sopenharmony_ci CA91CX42_LINT_LM3)) == 0) { 14788c2ecf20Sopenharmony_ci tmp = ioread32(bridge->base + LM_CTL); 14798c2ecf20Sopenharmony_ci tmp &= ~CA91CX42_LM_CTL_EN; 14808c2ecf20Sopenharmony_ci iowrite32(tmp, bridge->base + LM_CTL); 14818c2ecf20Sopenharmony_ci } 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci return 0; 14868c2ecf20Sopenharmony_ci} 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_cistatic int ca91cx42_slot_get(struct vme_bridge *ca91cx42_bridge) 14898c2ecf20Sopenharmony_ci{ 14908c2ecf20Sopenharmony_ci u32 slot = 0; 14918c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci bridge = ca91cx42_bridge->driver_priv; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci if (!geoid) { 14968c2ecf20Sopenharmony_ci slot = ioread32(bridge->base + VCSR_BS); 14978c2ecf20Sopenharmony_ci slot = ((slot & CA91CX42_VCSR_BS_SLOT_M) >> 27); 14988c2ecf20Sopenharmony_ci } else 14998c2ecf20Sopenharmony_ci slot = geoid; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci return (int)slot; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci} 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_cistatic void *ca91cx42_alloc_consistent(struct device *parent, size_t size, 15068c2ecf20Sopenharmony_ci dma_addr_t *dma) 15078c2ecf20Sopenharmony_ci{ 15088c2ecf20Sopenharmony_ci struct pci_dev *pdev; 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci /* Find pci_dev container of dev */ 15118c2ecf20Sopenharmony_ci pdev = to_pci_dev(parent); 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci return pci_alloc_consistent(pdev, size, dma); 15148c2ecf20Sopenharmony_ci} 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_cistatic void ca91cx42_free_consistent(struct device *parent, size_t size, 15178c2ecf20Sopenharmony_ci void *vaddr, dma_addr_t dma) 15188c2ecf20Sopenharmony_ci{ 15198c2ecf20Sopenharmony_ci struct pci_dev *pdev; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci /* Find pci_dev container of dev */ 15228c2ecf20Sopenharmony_ci pdev = to_pci_dev(parent); 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci pci_free_consistent(pdev, size, vaddr, dma); 15258c2ecf20Sopenharmony_ci} 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci/* 15288c2ecf20Sopenharmony_ci * Configure CR/CSR space 15298c2ecf20Sopenharmony_ci * 15308c2ecf20Sopenharmony_ci * Access to the CR/CSR can be configured at power-up. The location of the 15318c2ecf20Sopenharmony_ci * CR/CSR registers in the CR/CSR address space is determined by the boards 15328c2ecf20Sopenharmony_ci * Auto-ID or Geographic address. This function ensures that the window is 15338c2ecf20Sopenharmony_ci * enabled at an offset consistent with the boards geopgraphic address. 15348c2ecf20Sopenharmony_ci */ 15358c2ecf20Sopenharmony_cistatic int ca91cx42_crcsr_init(struct vme_bridge *ca91cx42_bridge, 15368c2ecf20Sopenharmony_ci struct pci_dev *pdev) 15378c2ecf20Sopenharmony_ci{ 15388c2ecf20Sopenharmony_ci unsigned int crcsr_addr; 15398c2ecf20Sopenharmony_ci int tmp, slot; 15408c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci bridge = ca91cx42_bridge->driver_priv; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci slot = ca91cx42_slot_get(ca91cx42_bridge); 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci /* Write CSR Base Address if slot ID is supplied as a module param */ 15478c2ecf20Sopenharmony_ci if (geoid) 15488c2ecf20Sopenharmony_ci iowrite32(geoid << 27, bridge->base + VCSR_BS); 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "CR/CSR Offset: %d\n", slot); 15518c2ecf20Sopenharmony_ci if (slot == 0) { 15528c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Slot number is unset, not configuring " 15538c2ecf20Sopenharmony_ci "CR/CSR space\n"); 15548c2ecf20Sopenharmony_ci return -EINVAL; 15558c2ecf20Sopenharmony_ci } 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci /* Allocate mem for CR/CSR image */ 15588c2ecf20Sopenharmony_ci bridge->crcsr_kernel = pci_zalloc_consistent(pdev, VME_CRCSR_BUF_SIZE, 15598c2ecf20Sopenharmony_ci &bridge->crcsr_bus); 15608c2ecf20Sopenharmony_ci if (!bridge->crcsr_kernel) { 15618c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to allocate memory for CR/CSR " 15628c2ecf20Sopenharmony_ci "image\n"); 15638c2ecf20Sopenharmony_ci return -ENOMEM; 15648c2ecf20Sopenharmony_ci } 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci crcsr_addr = slot * (512 * 1024); 15678c2ecf20Sopenharmony_ci iowrite32(bridge->crcsr_bus - crcsr_addr, bridge->base + VCSR_TO); 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci tmp = ioread32(bridge->base + VCSR_CTL); 15708c2ecf20Sopenharmony_ci tmp |= CA91CX42_VCSR_CTL_EN; 15718c2ecf20Sopenharmony_ci iowrite32(tmp, bridge->base + VCSR_CTL); 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci return 0; 15748c2ecf20Sopenharmony_ci} 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_cistatic void ca91cx42_crcsr_exit(struct vme_bridge *ca91cx42_bridge, 15778c2ecf20Sopenharmony_ci struct pci_dev *pdev) 15788c2ecf20Sopenharmony_ci{ 15798c2ecf20Sopenharmony_ci u32 tmp; 15808c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci bridge = ca91cx42_bridge->driver_priv; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci /* Turn off CR/CSR space */ 15858c2ecf20Sopenharmony_ci tmp = ioread32(bridge->base + VCSR_CTL); 15868c2ecf20Sopenharmony_ci tmp &= ~CA91CX42_VCSR_CTL_EN; 15878c2ecf20Sopenharmony_ci iowrite32(tmp, bridge->base + VCSR_CTL); 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci /* Free image */ 15908c2ecf20Sopenharmony_ci iowrite32(0, bridge->base + VCSR_TO); 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci pci_free_consistent(pdev, VME_CRCSR_BUF_SIZE, bridge->crcsr_kernel, 15938c2ecf20Sopenharmony_ci bridge->crcsr_bus); 15948c2ecf20Sopenharmony_ci} 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_cistatic int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id) 15978c2ecf20Sopenharmony_ci{ 15988c2ecf20Sopenharmony_ci int retval, i; 15998c2ecf20Sopenharmony_ci u32 data; 16008c2ecf20Sopenharmony_ci struct list_head *pos = NULL, *n; 16018c2ecf20Sopenharmony_ci struct vme_bridge *ca91cx42_bridge; 16028c2ecf20Sopenharmony_ci struct ca91cx42_driver *ca91cx42_device; 16038c2ecf20Sopenharmony_ci struct vme_master_resource *master_image; 16048c2ecf20Sopenharmony_ci struct vme_slave_resource *slave_image; 16058c2ecf20Sopenharmony_ci struct vme_dma_resource *dma_ctrlr; 16068c2ecf20Sopenharmony_ci struct vme_lm_resource *lm; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci /* We want to support more than one of each bridge so we need to 16098c2ecf20Sopenharmony_ci * dynamically allocate the bridge structure 16108c2ecf20Sopenharmony_ci */ 16118c2ecf20Sopenharmony_ci ca91cx42_bridge = kzalloc(sizeof(*ca91cx42_bridge), GFP_KERNEL); 16128c2ecf20Sopenharmony_ci if (!ca91cx42_bridge) { 16138c2ecf20Sopenharmony_ci retval = -ENOMEM; 16148c2ecf20Sopenharmony_ci goto err_struct; 16158c2ecf20Sopenharmony_ci } 16168c2ecf20Sopenharmony_ci vme_init_bridge(ca91cx42_bridge); 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci ca91cx42_device = kzalloc(sizeof(*ca91cx42_device), GFP_KERNEL); 16198c2ecf20Sopenharmony_ci if (!ca91cx42_device) { 16208c2ecf20Sopenharmony_ci retval = -ENOMEM; 16218c2ecf20Sopenharmony_ci goto err_driver; 16228c2ecf20Sopenharmony_ci } 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci ca91cx42_bridge->driver_priv = ca91cx42_device; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci /* Enable the device */ 16278c2ecf20Sopenharmony_ci retval = pci_enable_device(pdev); 16288c2ecf20Sopenharmony_ci if (retval) { 16298c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to enable device\n"); 16308c2ecf20Sopenharmony_ci goto err_enable; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci /* Map Registers */ 16348c2ecf20Sopenharmony_ci retval = pci_request_regions(pdev, driver_name); 16358c2ecf20Sopenharmony_ci if (retval) { 16368c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to reserve resources\n"); 16378c2ecf20Sopenharmony_ci goto err_resource; 16388c2ecf20Sopenharmony_ci } 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci /* map registers in BAR 0 */ 16418c2ecf20Sopenharmony_ci ca91cx42_device->base = ioremap(pci_resource_start(pdev, 0), 16428c2ecf20Sopenharmony_ci 4096); 16438c2ecf20Sopenharmony_ci if (!ca91cx42_device->base) { 16448c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to remap CRG region\n"); 16458c2ecf20Sopenharmony_ci retval = -EIO; 16468c2ecf20Sopenharmony_ci goto err_remap; 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci /* Check to see if the mapping worked out */ 16508c2ecf20Sopenharmony_ci data = ioread32(ca91cx42_device->base + CA91CX42_PCI_ID) & 0x0000FFFF; 16518c2ecf20Sopenharmony_ci if (data != PCI_VENDOR_ID_TUNDRA) { 16528c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "PCI_ID check failed\n"); 16538c2ecf20Sopenharmony_ci retval = -EIO; 16548c2ecf20Sopenharmony_ci goto err_test; 16558c2ecf20Sopenharmony_ci } 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci /* Initialize wait queues & mutual exclusion flags */ 16588c2ecf20Sopenharmony_ci init_waitqueue_head(&ca91cx42_device->dma_queue); 16598c2ecf20Sopenharmony_ci init_waitqueue_head(&ca91cx42_device->iack_queue); 16608c2ecf20Sopenharmony_ci mutex_init(&ca91cx42_device->vme_int); 16618c2ecf20Sopenharmony_ci mutex_init(&ca91cx42_device->vme_rmw); 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci ca91cx42_bridge->parent = &pdev->dev; 16648c2ecf20Sopenharmony_ci strcpy(ca91cx42_bridge->name, driver_name); 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci /* Setup IRQ */ 16678c2ecf20Sopenharmony_ci retval = ca91cx42_irq_init(ca91cx42_bridge); 16688c2ecf20Sopenharmony_ci if (retval != 0) { 16698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Chip Initialization failed.\n"); 16708c2ecf20Sopenharmony_ci goto err_irq; 16718c2ecf20Sopenharmony_ci } 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci /* Add master windows to list */ 16748c2ecf20Sopenharmony_ci for (i = 0; i < CA91C142_MAX_MASTER; i++) { 16758c2ecf20Sopenharmony_ci master_image = kmalloc(sizeof(*master_image), GFP_KERNEL); 16768c2ecf20Sopenharmony_ci if (!master_image) { 16778c2ecf20Sopenharmony_ci retval = -ENOMEM; 16788c2ecf20Sopenharmony_ci goto err_master; 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci master_image->parent = ca91cx42_bridge; 16818c2ecf20Sopenharmony_ci spin_lock_init(&master_image->lock); 16828c2ecf20Sopenharmony_ci master_image->locked = 0; 16838c2ecf20Sopenharmony_ci master_image->number = i; 16848c2ecf20Sopenharmony_ci master_image->address_attr = VME_A16 | VME_A24 | VME_A32 | 16858c2ecf20Sopenharmony_ci VME_CRCSR | VME_USER1 | VME_USER2; 16868c2ecf20Sopenharmony_ci master_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT | 16878c2ecf20Sopenharmony_ci VME_SUPER | VME_USER | VME_PROG | VME_DATA; 16888c2ecf20Sopenharmony_ci master_image->width_attr = VME_D8 | VME_D16 | VME_D32 | VME_D64; 16898c2ecf20Sopenharmony_ci memset(&master_image->bus_resource, 0, 16908c2ecf20Sopenharmony_ci sizeof(master_image->bus_resource)); 16918c2ecf20Sopenharmony_ci master_image->kern_base = NULL; 16928c2ecf20Sopenharmony_ci list_add_tail(&master_image->list, 16938c2ecf20Sopenharmony_ci &ca91cx42_bridge->master_resources); 16948c2ecf20Sopenharmony_ci } 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci /* Add slave windows to list */ 16978c2ecf20Sopenharmony_ci for (i = 0; i < CA91C142_MAX_SLAVE; i++) { 16988c2ecf20Sopenharmony_ci slave_image = kmalloc(sizeof(*slave_image), GFP_KERNEL); 16998c2ecf20Sopenharmony_ci if (!slave_image) { 17008c2ecf20Sopenharmony_ci retval = -ENOMEM; 17018c2ecf20Sopenharmony_ci goto err_slave; 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci slave_image->parent = ca91cx42_bridge; 17048c2ecf20Sopenharmony_ci mutex_init(&slave_image->mtx); 17058c2ecf20Sopenharmony_ci slave_image->locked = 0; 17068c2ecf20Sopenharmony_ci slave_image->number = i; 17078c2ecf20Sopenharmony_ci slave_image->address_attr = VME_A24 | VME_A32 | VME_USER1 | 17088c2ecf20Sopenharmony_ci VME_USER2; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci /* Only windows 0 and 4 support A16 */ 17118c2ecf20Sopenharmony_ci if (i == 0 || i == 4) 17128c2ecf20Sopenharmony_ci slave_image->address_attr |= VME_A16; 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci slave_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT | 17158c2ecf20Sopenharmony_ci VME_SUPER | VME_USER | VME_PROG | VME_DATA; 17168c2ecf20Sopenharmony_ci list_add_tail(&slave_image->list, 17178c2ecf20Sopenharmony_ci &ca91cx42_bridge->slave_resources); 17188c2ecf20Sopenharmony_ci } 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci /* Add dma engines to list */ 17218c2ecf20Sopenharmony_ci for (i = 0; i < CA91C142_MAX_DMA; i++) { 17228c2ecf20Sopenharmony_ci dma_ctrlr = kmalloc(sizeof(*dma_ctrlr), GFP_KERNEL); 17238c2ecf20Sopenharmony_ci if (!dma_ctrlr) { 17248c2ecf20Sopenharmony_ci retval = -ENOMEM; 17258c2ecf20Sopenharmony_ci goto err_dma; 17268c2ecf20Sopenharmony_ci } 17278c2ecf20Sopenharmony_ci dma_ctrlr->parent = ca91cx42_bridge; 17288c2ecf20Sopenharmony_ci mutex_init(&dma_ctrlr->mtx); 17298c2ecf20Sopenharmony_ci dma_ctrlr->locked = 0; 17308c2ecf20Sopenharmony_ci dma_ctrlr->number = i; 17318c2ecf20Sopenharmony_ci dma_ctrlr->route_attr = VME_DMA_VME_TO_MEM | 17328c2ecf20Sopenharmony_ci VME_DMA_MEM_TO_VME; 17338c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dma_ctrlr->pending); 17348c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dma_ctrlr->running); 17358c2ecf20Sopenharmony_ci list_add_tail(&dma_ctrlr->list, 17368c2ecf20Sopenharmony_ci &ca91cx42_bridge->dma_resources); 17378c2ecf20Sopenharmony_ci } 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci /* Add location monitor to list */ 17408c2ecf20Sopenharmony_ci lm = kmalloc(sizeof(*lm), GFP_KERNEL); 17418c2ecf20Sopenharmony_ci if (!lm) { 17428c2ecf20Sopenharmony_ci retval = -ENOMEM; 17438c2ecf20Sopenharmony_ci goto err_lm; 17448c2ecf20Sopenharmony_ci } 17458c2ecf20Sopenharmony_ci lm->parent = ca91cx42_bridge; 17468c2ecf20Sopenharmony_ci mutex_init(&lm->mtx); 17478c2ecf20Sopenharmony_ci lm->locked = 0; 17488c2ecf20Sopenharmony_ci lm->number = 1; 17498c2ecf20Sopenharmony_ci lm->monitors = 4; 17508c2ecf20Sopenharmony_ci list_add_tail(&lm->list, &ca91cx42_bridge->lm_resources); 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci ca91cx42_bridge->slave_get = ca91cx42_slave_get; 17538c2ecf20Sopenharmony_ci ca91cx42_bridge->slave_set = ca91cx42_slave_set; 17548c2ecf20Sopenharmony_ci ca91cx42_bridge->master_get = ca91cx42_master_get; 17558c2ecf20Sopenharmony_ci ca91cx42_bridge->master_set = ca91cx42_master_set; 17568c2ecf20Sopenharmony_ci ca91cx42_bridge->master_read = ca91cx42_master_read; 17578c2ecf20Sopenharmony_ci ca91cx42_bridge->master_write = ca91cx42_master_write; 17588c2ecf20Sopenharmony_ci ca91cx42_bridge->master_rmw = ca91cx42_master_rmw; 17598c2ecf20Sopenharmony_ci ca91cx42_bridge->dma_list_add = ca91cx42_dma_list_add; 17608c2ecf20Sopenharmony_ci ca91cx42_bridge->dma_list_exec = ca91cx42_dma_list_exec; 17618c2ecf20Sopenharmony_ci ca91cx42_bridge->dma_list_empty = ca91cx42_dma_list_empty; 17628c2ecf20Sopenharmony_ci ca91cx42_bridge->irq_set = ca91cx42_irq_set; 17638c2ecf20Sopenharmony_ci ca91cx42_bridge->irq_generate = ca91cx42_irq_generate; 17648c2ecf20Sopenharmony_ci ca91cx42_bridge->lm_set = ca91cx42_lm_set; 17658c2ecf20Sopenharmony_ci ca91cx42_bridge->lm_get = ca91cx42_lm_get; 17668c2ecf20Sopenharmony_ci ca91cx42_bridge->lm_attach = ca91cx42_lm_attach; 17678c2ecf20Sopenharmony_ci ca91cx42_bridge->lm_detach = ca91cx42_lm_detach; 17688c2ecf20Sopenharmony_ci ca91cx42_bridge->slot_get = ca91cx42_slot_get; 17698c2ecf20Sopenharmony_ci ca91cx42_bridge->alloc_consistent = ca91cx42_alloc_consistent; 17708c2ecf20Sopenharmony_ci ca91cx42_bridge->free_consistent = ca91cx42_free_consistent; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci data = ioread32(ca91cx42_device->base + MISC_CTL); 17738c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Board is%s the VME system controller\n", 17748c2ecf20Sopenharmony_ci (data & CA91CX42_MISC_CTL_SYSCON) ? "" : " not"); 17758c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Slot ID is %d\n", 17768c2ecf20Sopenharmony_ci ca91cx42_slot_get(ca91cx42_bridge)); 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci if (ca91cx42_crcsr_init(ca91cx42_bridge, pdev)) 17798c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "CR/CSR configuration failed.\n"); 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci /* Need to save ca91cx42_bridge pointer locally in link list for use in 17828c2ecf20Sopenharmony_ci * ca91cx42_remove() 17838c2ecf20Sopenharmony_ci */ 17848c2ecf20Sopenharmony_ci retval = vme_register_bridge(ca91cx42_bridge); 17858c2ecf20Sopenharmony_ci if (retval != 0) { 17868c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Chip Registration failed.\n"); 17878c2ecf20Sopenharmony_ci goto err_reg; 17888c2ecf20Sopenharmony_ci } 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, ca91cx42_bridge); 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci return 0; 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_cierr_reg: 17958c2ecf20Sopenharmony_ci ca91cx42_crcsr_exit(ca91cx42_bridge, pdev); 17968c2ecf20Sopenharmony_cierr_lm: 17978c2ecf20Sopenharmony_ci /* resources are stored in link list */ 17988c2ecf20Sopenharmony_ci list_for_each_safe(pos, n, &ca91cx42_bridge->lm_resources) { 17998c2ecf20Sopenharmony_ci lm = list_entry(pos, struct vme_lm_resource, list); 18008c2ecf20Sopenharmony_ci list_del(pos); 18018c2ecf20Sopenharmony_ci kfree(lm); 18028c2ecf20Sopenharmony_ci } 18038c2ecf20Sopenharmony_cierr_dma: 18048c2ecf20Sopenharmony_ci /* resources are stored in link list */ 18058c2ecf20Sopenharmony_ci list_for_each_safe(pos, n, &ca91cx42_bridge->dma_resources) { 18068c2ecf20Sopenharmony_ci dma_ctrlr = list_entry(pos, struct vme_dma_resource, list); 18078c2ecf20Sopenharmony_ci list_del(pos); 18088c2ecf20Sopenharmony_ci kfree(dma_ctrlr); 18098c2ecf20Sopenharmony_ci } 18108c2ecf20Sopenharmony_cierr_slave: 18118c2ecf20Sopenharmony_ci /* resources are stored in link list */ 18128c2ecf20Sopenharmony_ci list_for_each_safe(pos, n, &ca91cx42_bridge->slave_resources) { 18138c2ecf20Sopenharmony_ci slave_image = list_entry(pos, struct vme_slave_resource, list); 18148c2ecf20Sopenharmony_ci list_del(pos); 18158c2ecf20Sopenharmony_ci kfree(slave_image); 18168c2ecf20Sopenharmony_ci } 18178c2ecf20Sopenharmony_cierr_master: 18188c2ecf20Sopenharmony_ci /* resources are stored in link list */ 18198c2ecf20Sopenharmony_ci list_for_each_safe(pos, n, &ca91cx42_bridge->master_resources) { 18208c2ecf20Sopenharmony_ci master_image = list_entry(pos, struct vme_master_resource, 18218c2ecf20Sopenharmony_ci list); 18228c2ecf20Sopenharmony_ci list_del(pos); 18238c2ecf20Sopenharmony_ci kfree(master_image); 18248c2ecf20Sopenharmony_ci } 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci ca91cx42_irq_exit(ca91cx42_device, pdev); 18278c2ecf20Sopenharmony_cierr_irq: 18288c2ecf20Sopenharmony_cierr_test: 18298c2ecf20Sopenharmony_ci iounmap(ca91cx42_device->base); 18308c2ecf20Sopenharmony_cierr_remap: 18318c2ecf20Sopenharmony_ci pci_release_regions(pdev); 18328c2ecf20Sopenharmony_cierr_resource: 18338c2ecf20Sopenharmony_ci pci_disable_device(pdev); 18348c2ecf20Sopenharmony_cierr_enable: 18358c2ecf20Sopenharmony_ci kfree(ca91cx42_device); 18368c2ecf20Sopenharmony_cierr_driver: 18378c2ecf20Sopenharmony_ci kfree(ca91cx42_bridge); 18388c2ecf20Sopenharmony_cierr_struct: 18398c2ecf20Sopenharmony_ci return retval; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci} 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_cistatic void ca91cx42_remove(struct pci_dev *pdev) 18448c2ecf20Sopenharmony_ci{ 18458c2ecf20Sopenharmony_ci struct list_head *pos = NULL, *n; 18468c2ecf20Sopenharmony_ci struct vme_master_resource *master_image; 18478c2ecf20Sopenharmony_ci struct vme_slave_resource *slave_image; 18488c2ecf20Sopenharmony_ci struct vme_dma_resource *dma_ctrlr; 18498c2ecf20Sopenharmony_ci struct vme_lm_resource *lm; 18508c2ecf20Sopenharmony_ci struct ca91cx42_driver *bridge; 18518c2ecf20Sopenharmony_ci struct vme_bridge *ca91cx42_bridge = pci_get_drvdata(pdev); 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci bridge = ca91cx42_bridge->driver_priv; 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci /* Turn off Ints */ 18578c2ecf20Sopenharmony_ci iowrite32(0, bridge->base + LINT_EN); 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci /* Turn off the windows */ 18608c2ecf20Sopenharmony_ci iowrite32(0x00800000, bridge->base + LSI0_CTL); 18618c2ecf20Sopenharmony_ci iowrite32(0x00800000, bridge->base + LSI1_CTL); 18628c2ecf20Sopenharmony_ci iowrite32(0x00800000, bridge->base + LSI2_CTL); 18638c2ecf20Sopenharmony_ci iowrite32(0x00800000, bridge->base + LSI3_CTL); 18648c2ecf20Sopenharmony_ci iowrite32(0x00800000, bridge->base + LSI4_CTL); 18658c2ecf20Sopenharmony_ci iowrite32(0x00800000, bridge->base + LSI5_CTL); 18668c2ecf20Sopenharmony_ci iowrite32(0x00800000, bridge->base + LSI6_CTL); 18678c2ecf20Sopenharmony_ci iowrite32(0x00800000, bridge->base + LSI7_CTL); 18688c2ecf20Sopenharmony_ci iowrite32(0x00F00000, bridge->base + VSI0_CTL); 18698c2ecf20Sopenharmony_ci iowrite32(0x00F00000, bridge->base + VSI1_CTL); 18708c2ecf20Sopenharmony_ci iowrite32(0x00F00000, bridge->base + VSI2_CTL); 18718c2ecf20Sopenharmony_ci iowrite32(0x00F00000, bridge->base + VSI3_CTL); 18728c2ecf20Sopenharmony_ci iowrite32(0x00F00000, bridge->base + VSI4_CTL); 18738c2ecf20Sopenharmony_ci iowrite32(0x00F00000, bridge->base + VSI5_CTL); 18748c2ecf20Sopenharmony_ci iowrite32(0x00F00000, bridge->base + VSI6_CTL); 18758c2ecf20Sopenharmony_ci iowrite32(0x00F00000, bridge->base + VSI7_CTL); 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci vme_unregister_bridge(ca91cx42_bridge); 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci ca91cx42_crcsr_exit(ca91cx42_bridge, pdev); 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci /* resources are stored in link list */ 18828c2ecf20Sopenharmony_ci list_for_each_safe(pos, n, &ca91cx42_bridge->lm_resources) { 18838c2ecf20Sopenharmony_ci lm = list_entry(pos, struct vme_lm_resource, list); 18848c2ecf20Sopenharmony_ci list_del(pos); 18858c2ecf20Sopenharmony_ci kfree(lm); 18868c2ecf20Sopenharmony_ci } 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci /* resources are stored in link list */ 18898c2ecf20Sopenharmony_ci list_for_each_safe(pos, n, &ca91cx42_bridge->dma_resources) { 18908c2ecf20Sopenharmony_ci dma_ctrlr = list_entry(pos, struct vme_dma_resource, list); 18918c2ecf20Sopenharmony_ci list_del(pos); 18928c2ecf20Sopenharmony_ci kfree(dma_ctrlr); 18938c2ecf20Sopenharmony_ci } 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci /* resources are stored in link list */ 18968c2ecf20Sopenharmony_ci list_for_each_safe(pos, n, &ca91cx42_bridge->slave_resources) { 18978c2ecf20Sopenharmony_ci slave_image = list_entry(pos, struct vme_slave_resource, list); 18988c2ecf20Sopenharmony_ci list_del(pos); 18998c2ecf20Sopenharmony_ci kfree(slave_image); 19008c2ecf20Sopenharmony_ci } 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci /* resources are stored in link list */ 19038c2ecf20Sopenharmony_ci list_for_each_safe(pos, n, &ca91cx42_bridge->master_resources) { 19048c2ecf20Sopenharmony_ci master_image = list_entry(pos, struct vme_master_resource, 19058c2ecf20Sopenharmony_ci list); 19068c2ecf20Sopenharmony_ci list_del(pos); 19078c2ecf20Sopenharmony_ci kfree(master_image); 19088c2ecf20Sopenharmony_ci } 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci ca91cx42_irq_exit(bridge, pdev); 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci iounmap(bridge->base); 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci pci_release_regions(pdev); 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci pci_disable_device(pdev); 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci kfree(ca91cx42_bridge); 19198c2ecf20Sopenharmony_ci} 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_cimodule_pci_driver(ca91cx42_driver); 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(geoid, "Override geographical addressing"); 19248c2ecf20Sopenharmony_cimodule_param(geoid, int, 0); 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("VME driver for the Tundra Universe II VME bridge"); 19278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1928