18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Support for the Tundra TSI148 VME-PCI Bridge Chip 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 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 148c2ecf20Sopenharmony_ci#include <linux/mm.h> 158c2ecf20Sopenharmony_ci#include <linux/types.h> 168c2ecf20Sopenharmony_ci#include <linux/errno.h> 178c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 188c2ecf20Sopenharmony_ci#include <linux/pci.h> 198c2ecf20Sopenharmony_ci#include <linux/poll.h> 208c2ecf20Sopenharmony_ci#include <linux/dma-mapping.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/byteorder/generic.h> 298c2ecf20Sopenharmony_ci#include <linux/vme.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include "../vme_bridge.h" 328c2ecf20Sopenharmony_ci#include "vme_tsi148.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int tsi148_probe(struct pci_dev *, const struct pci_device_id *); 358c2ecf20Sopenharmony_cistatic void tsi148_remove(struct pci_dev *); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* Module parameter */ 398c2ecf20Sopenharmony_cistatic bool err_chk; 408c2ecf20Sopenharmony_cistatic int geoid; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic const char driver_name[] = "vme_tsi148"; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic const struct pci_device_id tsi148_ids[] = { 458c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_TUNDRA, PCI_DEVICE_ID_TUNDRA_TSI148) }, 468c2ecf20Sopenharmony_ci { }, 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, tsi148_ids); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic struct pci_driver tsi148_driver = { 528c2ecf20Sopenharmony_ci .name = driver_name, 538c2ecf20Sopenharmony_ci .id_table = tsi148_ids, 548c2ecf20Sopenharmony_ci .probe = tsi148_probe, 558c2ecf20Sopenharmony_ci .remove = tsi148_remove, 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic void reg_join(unsigned int high, unsigned int low, 598c2ecf20Sopenharmony_ci unsigned long long *variable) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci *variable = (unsigned long long)high << 32; 628c2ecf20Sopenharmony_ci *variable |= (unsigned long long)low; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void reg_split(unsigned long long variable, unsigned int *high, 668c2ecf20Sopenharmony_ci unsigned int *low) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci *low = (unsigned int)variable & 0xFFFFFFFF; 698c2ecf20Sopenharmony_ci *high = (unsigned int)(variable >> 32); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * Wakes up DMA queue. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_cistatic u32 tsi148_DMA_irqhandler(struct tsi148_driver *bridge, 768c2ecf20Sopenharmony_ci int channel_mask) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci u32 serviced = 0; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (channel_mask & TSI148_LCSR_INTS_DMA0S) { 818c2ecf20Sopenharmony_ci wake_up(&bridge->dma_queue[0]); 828c2ecf20Sopenharmony_ci serviced |= TSI148_LCSR_INTC_DMA0C; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci if (channel_mask & TSI148_LCSR_INTS_DMA1S) { 858c2ecf20Sopenharmony_ci wake_up(&bridge->dma_queue[1]); 868c2ecf20Sopenharmony_ci serviced |= TSI148_LCSR_INTC_DMA1C; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return serviced; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* 938c2ecf20Sopenharmony_ci * Wake up location monitor queue 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_cistatic u32 tsi148_LM_irqhandler(struct tsi148_driver *bridge, u32 stat) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci int i; 988c2ecf20Sopenharmony_ci u32 serviced = 0; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 1018c2ecf20Sopenharmony_ci if (stat & TSI148_LCSR_INTS_LMS[i]) { 1028c2ecf20Sopenharmony_ci /* We only enable interrupts if the callback is set */ 1038c2ecf20Sopenharmony_ci bridge->lm_callback[i](bridge->lm_data[i]); 1048c2ecf20Sopenharmony_ci serviced |= TSI148_LCSR_INTC_LMC[i]; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return serviced; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * Wake up mail box queue. 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * XXX This functionality is not exposed up though API. 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_cistatic u32 tsi148_MB_irqhandler(struct vme_bridge *tsi148_bridge, u32 stat) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci int i; 1198c2ecf20Sopenharmony_ci u32 val; 1208c2ecf20Sopenharmony_ci u32 serviced = 0; 1218c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 1268c2ecf20Sopenharmony_ci if (stat & TSI148_LCSR_INTS_MBS[i]) { 1278c2ecf20Sopenharmony_ci val = ioread32be(bridge->base + TSI148_GCSR_MBOX[i]); 1288c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "VME Mailbox %d received" 1298c2ecf20Sopenharmony_ci ": 0x%x\n", i, val); 1308c2ecf20Sopenharmony_ci serviced |= TSI148_LCSR_INTC_MBC[i]; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return serviced; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* 1388c2ecf20Sopenharmony_ci * Display error & status message when PERR (PCI) exception interrupt occurs. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_cistatic u32 tsi148_PERR_irqhandler(struct vme_bridge *tsi148_bridge) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "PCI Exception at address: 0x%08x:%08x, " 1478c2ecf20Sopenharmony_ci "attributes: %08x\n", 1488c2ecf20Sopenharmony_ci ioread32be(bridge->base + TSI148_LCSR_EDPAU), 1498c2ecf20Sopenharmony_ci ioread32be(bridge->base + TSI148_LCSR_EDPAL), 1508c2ecf20Sopenharmony_ci ioread32be(bridge->base + TSI148_LCSR_EDPAT)); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "PCI-X attribute reg: %08x, PCI-X split " 1538c2ecf20Sopenharmony_ci "completion reg: %08x\n", 1548c2ecf20Sopenharmony_ci ioread32be(bridge->base + TSI148_LCSR_EDPXA), 1558c2ecf20Sopenharmony_ci ioread32be(bridge->base + TSI148_LCSR_EDPXS)); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci iowrite32be(TSI148_LCSR_EDPAT_EDPCL, bridge->base + TSI148_LCSR_EDPAT); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return TSI148_LCSR_INTC_PERRC; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* 1638c2ecf20Sopenharmony_ci * Save address and status when VME error interrupt occurs. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_cistatic u32 tsi148_VERR_irqhandler(struct vme_bridge *tsi148_bridge) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci unsigned int error_addr_high, error_addr_low; 1688c2ecf20Sopenharmony_ci unsigned long long error_addr; 1698c2ecf20Sopenharmony_ci u32 error_attrib; 1708c2ecf20Sopenharmony_ci int error_am; 1718c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci error_addr_high = ioread32be(bridge->base + TSI148_LCSR_VEAU); 1768c2ecf20Sopenharmony_ci error_addr_low = ioread32be(bridge->base + TSI148_LCSR_VEAL); 1778c2ecf20Sopenharmony_ci error_attrib = ioread32be(bridge->base + TSI148_LCSR_VEAT); 1788c2ecf20Sopenharmony_ci error_am = (error_attrib & TSI148_LCSR_VEAT_AM_M) >> 8; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci reg_join(error_addr_high, error_addr_low, &error_addr); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* Check for exception register overflow (we have lost error data) */ 1838c2ecf20Sopenharmony_ci if (error_attrib & TSI148_LCSR_VEAT_VEOF) { 1848c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "VME Bus Exception Overflow " 1858c2ecf20Sopenharmony_ci "Occurred\n"); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (err_chk) 1898c2ecf20Sopenharmony_ci vme_bus_error_handler(tsi148_bridge, error_addr, error_am); 1908c2ecf20Sopenharmony_ci else 1918c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, 1928c2ecf20Sopenharmony_ci "VME Bus Error at address: 0x%llx, attributes: %08x\n", 1938c2ecf20Sopenharmony_ci error_addr, error_attrib); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* Clear Status */ 1968c2ecf20Sopenharmony_ci iowrite32be(TSI148_LCSR_VEAT_VESCL, bridge->base + TSI148_LCSR_VEAT); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return TSI148_LCSR_INTC_VERRC; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/* 2028c2ecf20Sopenharmony_ci * Wake up IACK queue. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_cistatic u32 tsi148_IACK_irqhandler(struct tsi148_driver *bridge) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci wake_up(&bridge->iack_queue); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return TSI148_LCSR_INTC_IACKC; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* 2128c2ecf20Sopenharmony_ci * Calling VME bus interrupt callback if provided. 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_cistatic u32 tsi148_VIRQ_irqhandler(struct vme_bridge *tsi148_bridge, 2158c2ecf20Sopenharmony_ci u32 stat) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci int vec, i, serviced = 0; 2188c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci for (i = 7; i > 0; i--) { 2238c2ecf20Sopenharmony_ci if (stat & (1 << i)) { 2248c2ecf20Sopenharmony_ci /* 2258c2ecf20Sopenharmony_ci * Note: Even though the registers are defined as 2268c2ecf20Sopenharmony_ci * 32-bits in the spec, we only want to issue 8-bit 2278c2ecf20Sopenharmony_ci * IACK cycles on the bus, read from offset 3. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_ci vec = ioread8(bridge->base + TSI148_LCSR_VIACK[i] + 3); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci vme_irq_handler(tsi148_bridge, i, vec); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci serviced |= (1 << i); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return serviced; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci/* 2418c2ecf20Sopenharmony_ci * Top level interrupt handler. Clears appropriate interrupt status bits and 2428c2ecf20Sopenharmony_ci * then calls appropriate sub handler(s). 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_cistatic irqreturn_t tsi148_irqhandler(int irq, void *ptr) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci u32 stat, enable, serviced = 0; 2478c2ecf20Sopenharmony_ci struct vme_bridge *tsi148_bridge; 2488c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci tsi148_bridge = ptr; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* Determine which interrupts are unmasked and set */ 2558c2ecf20Sopenharmony_ci enable = ioread32be(bridge->base + TSI148_LCSR_INTEO); 2568c2ecf20Sopenharmony_ci stat = ioread32be(bridge->base + TSI148_LCSR_INTS); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* Only look at unmasked interrupts */ 2598c2ecf20Sopenharmony_ci stat &= enable; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (unlikely(!stat)) 2628c2ecf20Sopenharmony_ci return IRQ_NONE; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Call subhandlers as appropriate */ 2658c2ecf20Sopenharmony_ci /* DMA irqs */ 2668c2ecf20Sopenharmony_ci if (stat & (TSI148_LCSR_INTS_DMA1S | TSI148_LCSR_INTS_DMA0S)) 2678c2ecf20Sopenharmony_ci serviced |= tsi148_DMA_irqhandler(bridge, stat); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* Location monitor irqs */ 2708c2ecf20Sopenharmony_ci if (stat & (TSI148_LCSR_INTS_LM3S | TSI148_LCSR_INTS_LM2S | 2718c2ecf20Sopenharmony_ci TSI148_LCSR_INTS_LM1S | TSI148_LCSR_INTS_LM0S)) 2728c2ecf20Sopenharmony_ci serviced |= tsi148_LM_irqhandler(bridge, stat); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* Mail box irqs */ 2758c2ecf20Sopenharmony_ci if (stat & (TSI148_LCSR_INTS_MB3S | TSI148_LCSR_INTS_MB2S | 2768c2ecf20Sopenharmony_ci TSI148_LCSR_INTS_MB1S | TSI148_LCSR_INTS_MB0S)) 2778c2ecf20Sopenharmony_ci serviced |= tsi148_MB_irqhandler(tsi148_bridge, stat); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* PCI bus error */ 2808c2ecf20Sopenharmony_ci if (stat & TSI148_LCSR_INTS_PERRS) 2818c2ecf20Sopenharmony_ci serviced |= tsi148_PERR_irqhandler(tsi148_bridge); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* VME bus error */ 2848c2ecf20Sopenharmony_ci if (stat & TSI148_LCSR_INTS_VERRS) 2858c2ecf20Sopenharmony_ci serviced |= tsi148_VERR_irqhandler(tsi148_bridge); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* IACK irq */ 2888c2ecf20Sopenharmony_ci if (stat & TSI148_LCSR_INTS_IACKS) 2898c2ecf20Sopenharmony_ci serviced |= tsi148_IACK_irqhandler(bridge); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* VME bus irqs */ 2928c2ecf20Sopenharmony_ci if (stat & (TSI148_LCSR_INTS_IRQ7S | TSI148_LCSR_INTS_IRQ6S | 2938c2ecf20Sopenharmony_ci TSI148_LCSR_INTS_IRQ5S | TSI148_LCSR_INTS_IRQ4S | 2948c2ecf20Sopenharmony_ci TSI148_LCSR_INTS_IRQ3S | TSI148_LCSR_INTS_IRQ2S | 2958c2ecf20Sopenharmony_ci TSI148_LCSR_INTS_IRQ1S)) 2968c2ecf20Sopenharmony_ci serviced |= tsi148_VIRQ_irqhandler(tsi148_bridge, stat); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* Clear serviced interrupts */ 2998c2ecf20Sopenharmony_ci iowrite32be(serviced, bridge->base + TSI148_LCSR_INTC); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int tsi148_irq_init(struct vme_bridge *tsi148_bridge) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci int result; 3078c2ecf20Sopenharmony_ci unsigned int tmp; 3088c2ecf20Sopenharmony_ci struct pci_dev *pdev; 3098c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci pdev = to_pci_dev(tsi148_bridge->parent); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci result = request_irq(pdev->irq, 3168c2ecf20Sopenharmony_ci tsi148_irqhandler, 3178c2ecf20Sopenharmony_ci IRQF_SHARED, 3188c2ecf20Sopenharmony_ci driver_name, tsi148_bridge); 3198c2ecf20Sopenharmony_ci if (result) { 3208c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Can't get assigned pci irq " 3218c2ecf20Sopenharmony_ci "vector %02X\n", pdev->irq); 3228c2ecf20Sopenharmony_ci return result; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* Enable and unmask interrupts */ 3268c2ecf20Sopenharmony_ci tmp = TSI148_LCSR_INTEO_DMA1EO | TSI148_LCSR_INTEO_DMA0EO | 3278c2ecf20Sopenharmony_ci TSI148_LCSR_INTEO_MB3EO | TSI148_LCSR_INTEO_MB2EO | 3288c2ecf20Sopenharmony_ci TSI148_LCSR_INTEO_MB1EO | TSI148_LCSR_INTEO_MB0EO | 3298c2ecf20Sopenharmony_ci TSI148_LCSR_INTEO_PERREO | TSI148_LCSR_INTEO_VERREO | 3308c2ecf20Sopenharmony_ci TSI148_LCSR_INTEO_IACKEO; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* This leaves the following interrupts masked. 3338c2ecf20Sopenharmony_ci * TSI148_LCSR_INTEO_VIEEO 3348c2ecf20Sopenharmony_ci * TSI148_LCSR_INTEO_SYSFLEO 3358c2ecf20Sopenharmony_ci * TSI148_LCSR_INTEO_ACFLEO 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* Don't enable Location Monitor interrupts here - they will be 3398c2ecf20Sopenharmony_ci * enabled when the location monitors are properly configured and 3408c2ecf20Sopenharmony_ci * a callback has been attached. 3418c2ecf20Sopenharmony_ci * TSI148_LCSR_INTEO_LM0EO 3428c2ecf20Sopenharmony_ci * TSI148_LCSR_INTEO_LM1EO 3438c2ecf20Sopenharmony_ci * TSI148_LCSR_INTEO_LM2EO 3448c2ecf20Sopenharmony_ci * TSI148_LCSR_INTEO_LM3EO 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* Don't enable VME interrupts until we add a handler, else the board 3488c2ecf20Sopenharmony_ci * will respond to it and we don't want that unless it knows how to 3498c2ecf20Sopenharmony_ci * properly deal with it. 3508c2ecf20Sopenharmony_ci * TSI148_LCSR_INTEO_IRQ7EO 3518c2ecf20Sopenharmony_ci * TSI148_LCSR_INTEO_IRQ6EO 3528c2ecf20Sopenharmony_ci * TSI148_LCSR_INTEO_IRQ5EO 3538c2ecf20Sopenharmony_ci * TSI148_LCSR_INTEO_IRQ4EO 3548c2ecf20Sopenharmony_ci * TSI148_LCSR_INTEO_IRQ3EO 3558c2ecf20Sopenharmony_ci * TSI148_LCSR_INTEO_IRQ2EO 3568c2ecf20Sopenharmony_ci * TSI148_LCSR_INTEO_IRQ1EO 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO); 3608c2ecf20Sopenharmony_ci iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEN); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic void tsi148_irq_exit(struct vme_bridge *tsi148_bridge, 3668c2ecf20Sopenharmony_ci struct pci_dev *pdev) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct tsi148_driver *bridge = tsi148_bridge->driver_priv; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* Turn off interrupts */ 3718c2ecf20Sopenharmony_ci iowrite32be(0x0, bridge->base + TSI148_LCSR_INTEO); 3728c2ecf20Sopenharmony_ci iowrite32be(0x0, bridge->base + TSI148_LCSR_INTEN); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* Clear all interrupts */ 3758c2ecf20Sopenharmony_ci iowrite32be(0xFFFFFFFF, bridge->base + TSI148_LCSR_INTC); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* Detach interrupt handler */ 3788c2ecf20Sopenharmony_ci free_irq(pdev->irq, tsi148_bridge); 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci/* 3828c2ecf20Sopenharmony_ci * Check to see if an IACk has been received, return true (1) or false (0). 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_cistatic int tsi148_iack_received(struct tsi148_driver *bridge) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci u32 tmp; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci tmp = ioread32be(bridge->base + TSI148_LCSR_VICR); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (tmp & TSI148_LCSR_VICR_IRQS) 3918c2ecf20Sopenharmony_ci return 0; 3928c2ecf20Sopenharmony_ci else 3938c2ecf20Sopenharmony_ci return 1; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci/* 3978c2ecf20Sopenharmony_ci * Configure VME interrupt 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_cistatic void tsi148_irq_set(struct vme_bridge *tsi148_bridge, int level, 4008c2ecf20Sopenharmony_ci int state, int sync) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct pci_dev *pdev; 4038c2ecf20Sopenharmony_ci u32 tmp; 4048c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* We need to do the ordering differently for enabling and disabling */ 4098c2ecf20Sopenharmony_ci if (state == 0) { 4108c2ecf20Sopenharmony_ci tmp = ioread32be(bridge->base + TSI148_LCSR_INTEN); 4118c2ecf20Sopenharmony_ci tmp &= ~TSI148_LCSR_INTEN_IRQEN[level - 1]; 4128c2ecf20Sopenharmony_ci iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEN); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci tmp = ioread32be(bridge->base + TSI148_LCSR_INTEO); 4158c2ecf20Sopenharmony_ci tmp &= ~TSI148_LCSR_INTEO_IRQEO[level - 1]; 4168c2ecf20Sopenharmony_ci iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (sync != 0) { 4198c2ecf20Sopenharmony_ci pdev = to_pci_dev(tsi148_bridge->parent); 4208c2ecf20Sopenharmony_ci synchronize_irq(pdev->irq); 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci } else { 4238c2ecf20Sopenharmony_ci tmp = ioread32be(bridge->base + TSI148_LCSR_INTEO); 4248c2ecf20Sopenharmony_ci tmp |= TSI148_LCSR_INTEO_IRQEO[level - 1]; 4258c2ecf20Sopenharmony_ci iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci tmp = ioread32be(bridge->base + TSI148_LCSR_INTEN); 4288c2ecf20Sopenharmony_ci tmp |= TSI148_LCSR_INTEN_IRQEN[level - 1]; 4298c2ecf20Sopenharmony_ci iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEN); 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci/* 4348c2ecf20Sopenharmony_ci * Generate a VME bus interrupt at the requested level & vector. Wait for 4358c2ecf20Sopenharmony_ci * interrupt to be acked. 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_cistatic int tsi148_irq_generate(struct vme_bridge *tsi148_bridge, int level, 4388c2ecf20Sopenharmony_ci int statid) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci u32 tmp; 4418c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci mutex_lock(&bridge->vme_int); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* Read VICR register */ 4488c2ecf20Sopenharmony_ci tmp = ioread32be(bridge->base + TSI148_LCSR_VICR); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* Set Status/ID */ 4518c2ecf20Sopenharmony_ci tmp = (tmp & ~TSI148_LCSR_VICR_STID_M) | 4528c2ecf20Sopenharmony_ci (statid & TSI148_LCSR_VICR_STID_M); 4538c2ecf20Sopenharmony_ci iowrite32be(tmp, bridge->base + TSI148_LCSR_VICR); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* Assert VMEbus IRQ */ 4568c2ecf20Sopenharmony_ci tmp = tmp | TSI148_LCSR_VICR_IRQL[level]; 4578c2ecf20Sopenharmony_ci iowrite32be(tmp, bridge->base + TSI148_LCSR_VICR); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* XXX Consider implementing a timeout? */ 4608c2ecf20Sopenharmony_ci wait_event_interruptible(bridge->iack_queue, 4618c2ecf20Sopenharmony_ci tsi148_iack_received(bridge)); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci mutex_unlock(&bridge->vme_int); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci return 0; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci/* 4698c2ecf20Sopenharmony_ci * Initialize a slave window with the requested attributes. 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_cistatic int tsi148_slave_set(struct vme_slave_resource *image, int enabled, 4728c2ecf20Sopenharmony_ci unsigned long long vme_base, unsigned long long size, 4738c2ecf20Sopenharmony_ci dma_addr_t pci_base, u32 aspace, u32 cycle) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci unsigned int i, addr = 0, granularity = 0; 4768c2ecf20Sopenharmony_ci unsigned int temp_ctl = 0; 4778c2ecf20Sopenharmony_ci unsigned int vme_base_low, vme_base_high; 4788c2ecf20Sopenharmony_ci unsigned int vme_bound_low, vme_bound_high; 4798c2ecf20Sopenharmony_ci unsigned int pci_offset_low, pci_offset_high; 4808c2ecf20Sopenharmony_ci unsigned long long vme_bound, pci_offset; 4818c2ecf20Sopenharmony_ci struct vme_bridge *tsi148_bridge; 4828c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci tsi148_bridge = image->parent; 4858c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci i = image->number; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci switch (aspace) { 4908c2ecf20Sopenharmony_ci case VME_A16: 4918c2ecf20Sopenharmony_ci granularity = 0x10; 4928c2ecf20Sopenharmony_ci addr |= TSI148_LCSR_ITAT_AS_A16; 4938c2ecf20Sopenharmony_ci break; 4948c2ecf20Sopenharmony_ci case VME_A24: 4958c2ecf20Sopenharmony_ci granularity = 0x1000; 4968c2ecf20Sopenharmony_ci addr |= TSI148_LCSR_ITAT_AS_A24; 4978c2ecf20Sopenharmony_ci break; 4988c2ecf20Sopenharmony_ci case VME_A32: 4998c2ecf20Sopenharmony_ci granularity = 0x10000; 5008c2ecf20Sopenharmony_ci addr |= TSI148_LCSR_ITAT_AS_A32; 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci case VME_A64: 5038c2ecf20Sopenharmony_ci granularity = 0x10000; 5048c2ecf20Sopenharmony_ci addr |= TSI148_LCSR_ITAT_AS_A64; 5058c2ecf20Sopenharmony_ci break; 5068c2ecf20Sopenharmony_ci default: 5078c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Invalid address space\n"); 5088c2ecf20Sopenharmony_ci return -EINVAL; 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* Convert 64-bit variables to 2x 32-bit variables */ 5138c2ecf20Sopenharmony_ci reg_split(vme_base, &vme_base_high, &vme_base_low); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* 5168c2ecf20Sopenharmony_ci * Bound address is a valid address for the window, adjust 5178c2ecf20Sopenharmony_ci * accordingly 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_ci vme_bound = vme_base + size - granularity; 5208c2ecf20Sopenharmony_ci reg_split(vme_bound, &vme_bound_high, &vme_bound_low); 5218c2ecf20Sopenharmony_ci pci_offset = (unsigned long long)pci_base - vme_base; 5228c2ecf20Sopenharmony_ci reg_split(pci_offset, &pci_offset_high, &pci_offset_low); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (vme_base_low & (granularity - 1)) { 5258c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Invalid VME base alignment\n"); 5268c2ecf20Sopenharmony_ci return -EINVAL; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci if (vme_bound_low & (granularity - 1)) { 5298c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Invalid VME bound alignment\n"); 5308c2ecf20Sopenharmony_ci return -EINVAL; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci if (pci_offset_low & (granularity - 1)) { 5338c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Invalid PCI Offset " 5348c2ecf20Sopenharmony_ci "alignment\n"); 5358c2ecf20Sopenharmony_ci return -EINVAL; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* Disable while we are mucking around */ 5398c2ecf20Sopenharmony_ci temp_ctl = ioread32be(bridge->base + TSI148_LCSR_IT[i] + 5408c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITAT); 5418c2ecf20Sopenharmony_ci temp_ctl &= ~TSI148_LCSR_ITAT_EN; 5428c2ecf20Sopenharmony_ci iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_IT[i] + 5438c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITAT); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* Setup mapping */ 5468c2ecf20Sopenharmony_ci iowrite32be(vme_base_high, bridge->base + TSI148_LCSR_IT[i] + 5478c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITSAU); 5488c2ecf20Sopenharmony_ci iowrite32be(vme_base_low, bridge->base + TSI148_LCSR_IT[i] + 5498c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITSAL); 5508c2ecf20Sopenharmony_ci iowrite32be(vme_bound_high, bridge->base + TSI148_LCSR_IT[i] + 5518c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITEAU); 5528c2ecf20Sopenharmony_ci iowrite32be(vme_bound_low, bridge->base + TSI148_LCSR_IT[i] + 5538c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITEAL); 5548c2ecf20Sopenharmony_ci iowrite32be(pci_offset_high, bridge->base + TSI148_LCSR_IT[i] + 5558c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITOFU); 5568c2ecf20Sopenharmony_ci iowrite32be(pci_offset_low, bridge->base + TSI148_LCSR_IT[i] + 5578c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITOFL); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* Setup 2eSST speeds */ 5608c2ecf20Sopenharmony_ci temp_ctl &= ~TSI148_LCSR_ITAT_2eSSTM_M; 5618c2ecf20Sopenharmony_ci switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) { 5628c2ecf20Sopenharmony_ci case VME_2eSST160: 5638c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_ITAT_2eSSTM_160; 5648c2ecf20Sopenharmony_ci break; 5658c2ecf20Sopenharmony_ci case VME_2eSST267: 5668c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_ITAT_2eSSTM_267; 5678c2ecf20Sopenharmony_ci break; 5688c2ecf20Sopenharmony_ci case VME_2eSST320: 5698c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_ITAT_2eSSTM_320; 5708c2ecf20Sopenharmony_ci break; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* Setup cycle types */ 5748c2ecf20Sopenharmony_ci temp_ctl &= ~(0x1F << 7); 5758c2ecf20Sopenharmony_ci if (cycle & VME_BLT) 5768c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_ITAT_BLT; 5778c2ecf20Sopenharmony_ci if (cycle & VME_MBLT) 5788c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_ITAT_MBLT; 5798c2ecf20Sopenharmony_ci if (cycle & VME_2eVME) 5808c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_ITAT_2eVME; 5818c2ecf20Sopenharmony_ci if (cycle & VME_2eSST) 5828c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_ITAT_2eSST; 5838c2ecf20Sopenharmony_ci if (cycle & VME_2eSSTB) 5848c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_ITAT_2eSSTB; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci /* Setup address space */ 5878c2ecf20Sopenharmony_ci temp_ctl &= ~TSI148_LCSR_ITAT_AS_M; 5888c2ecf20Sopenharmony_ci temp_ctl |= addr; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci temp_ctl &= ~0xF; 5918c2ecf20Sopenharmony_ci if (cycle & VME_SUPER) 5928c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_ITAT_SUPR ; 5938c2ecf20Sopenharmony_ci if (cycle & VME_USER) 5948c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_ITAT_NPRIV; 5958c2ecf20Sopenharmony_ci if (cycle & VME_PROG) 5968c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_ITAT_PGM; 5978c2ecf20Sopenharmony_ci if (cycle & VME_DATA) 5988c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_ITAT_DATA; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* Write ctl reg without enable */ 6018c2ecf20Sopenharmony_ci iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_IT[i] + 6028c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITAT); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (enabled) 6058c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_ITAT_EN; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_IT[i] + 6088c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITAT); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return 0; 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci/* 6148c2ecf20Sopenharmony_ci * Get slave window configuration. 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_cistatic int tsi148_slave_get(struct vme_slave_resource *image, int *enabled, 6178c2ecf20Sopenharmony_ci unsigned long long *vme_base, unsigned long long *size, 6188c2ecf20Sopenharmony_ci dma_addr_t *pci_base, u32 *aspace, u32 *cycle) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci unsigned int i, granularity = 0, ctl = 0; 6218c2ecf20Sopenharmony_ci unsigned int vme_base_low, vme_base_high; 6228c2ecf20Sopenharmony_ci unsigned int vme_bound_low, vme_bound_high; 6238c2ecf20Sopenharmony_ci unsigned int pci_offset_low, pci_offset_high; 6248c2ecf20Sopenharmony_ci unsigned long long vme_bound, pci_offset; 6258c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci bridge = image->parent->driver_priv; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci i = image->number; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci /* Read registers */ 6328c2ecf20Sopenharmony_ci ctl = ioread32be(bridge->base + TSI148_LCSR_IT[i] + 6338c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITAT); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci vme_base_high = ioread32be(bridge->base + TSI148_LCSR_IT[i] + 6368c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITSAU); 6378c2ecf20Sopenharmony_ci vme_base_low = ioread32be(bridge->base + TSI148_LCSR_IT[i] + 6388c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITSAL); 6398c2ecf20Sopenharmony_ci vme_bound_high = ioread32be(bridge->base + TSI148_LCSR_IT[i] + 6408c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITEAU); 6418c2ecf20Sopenharmony_ci vme_bound_low = ioread32be(bridge->base + TSI148_LCSR_IT[i] + 6428c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITEAL); 6438c2ecf20Sopenharmony_ci pci_offset_high = ioread32be(bridge->base + TSI148_LCSR_IT[i] + 6448c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITOFU); 6458c2ecf20Sopenharmony_ci pci_offset_low = ioread32be(bridge->base + TSI148_LCSR_IT[i] + 6468c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITOFL); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci /* Convert 64-bit variables to 2x 32-bit variables */ 6498c2ecf20Sopenharmony_ci reg_join(vme_base_high, vme_base_low, vme_base); 6508c2ecf20Sopenharmony_ci reg_join(vme_bound_high, vme_bound_low, &vme_bound); 6518c2ecf20Sopenharmony_ci reg_join(pci_offset_high, pci_offset_low, &pci_offset); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci *pci_base = (dma_addr_t)(*vme_base + pci_offset); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci *enabled = 0; 6568c2ecf20Sopenharmony_ci *aspace = 0; 6578c2ecf20Sopenharmony_ci *cycle = 0; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (ctl & TSI148_LCSR_ITAT_EN) 6608c2ecf20Sopenharmony_ci *enabled = 1; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_ITAT_AS_M) == TSI148_LCSR_ITAT_AS_A16) { 6638c2ecf20Sopenharmony_ci granularity = 0x10; 6648c2ecf20Sopenharmony_ci *aspace |= VME_A16; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_ITAT_AS_M) == TSI148_LCSR_ITAT_AS_A24) { 6678c2ecf20Sopenharmony_ci granularity = 0x1000; 6688c2ecf20Sopenharmony_ci *aspace |= VME_A24; 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_ITAT_AS_M) == TSI148_LCSR_ITAT_AS_A32) { 6718c2ecf20Sopenharmony_ci granularity = 0x10000; 6728c2ecf20Sopenharmony_ci *aspace |= VME_A32; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_ITAT_AS_M) == TSI148_LCSR_ITAT_AS_A64) { 6758c2ecf20Sopenharmony_ci granularity = 0x10000; 6768c2ecf20Sopenharmony_ci *aspace |= VME_A64; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci /* Need granularity before we set the size */ 6808c2ecf20Sopenharmony_ci *size = (unsigned long long)((vme_bound - *vme_base) + granularity); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_ITAT_2eSSTM_M) == TSI148_LCSR_ITAT_2eSSTM_160) 6848c2ecf20Sopenharmony_ci *cycle |= VME_2eSST160; 6858c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_ITAT_2eSSTM_M) == TSI148_LCSR_ITAT_2eSSTM_267) 6868c2ecf20Sopenharmony_ci *cycle |= VME_2eSST267; 6878c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_ITAT_2eSSTM_M) == TSI148_LCSR_ITAT_2eSSTM_320) 6888c2ecf20Sopenharmony_ci *cycle |= VME_2eSST320; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci if (ctl & TSI148_LCSR_ITAT_BLT) 6918c2ecf20Sopenharmony_ci *cycle |= VME_BLT; 6928c2ecf20Sopenharmony_ci if (ctl & TSI148_LCSR_ITAT_MBLT) 6938c2ecf20Sopenharmony_ci *cycle |= VME_MBLT; 6948c2ecf20Sopenharmony_ci if (ctl & TSI148_LCSR_ITAT_2eVME) 6958c2ecf20Sopenharmony_ci *cycle |= VME_2eVME; 6968c2ecf20Sopenharmony_ci if (ctl & TSI148_LCSR_ITAT_2eSST) 6978c2ecf20Sopenharmony_ci *cycle |= VME_2eSST; 6988c2ecf20Sopenharmony_ci if (ctl & TSI148_LCSR_ITAT_2eSSTB) 6998c2ecf20Sopenharmony_ci *cycle |= VME_2eSSTB; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (ctl & TSI148_LCSR_ITAT_SUPR) 7028c2ecf20Sopenharmony_ci *cycle |= VME_SUPER; 7038c2ecf20Sopenharmony_ci if (ctl & TSI148_LCSR_ITAT_NPRIV) 7048c2ecf20Sopenharmony_ci *cycle |= VME_USER; 7058c2ecf20Sopenharmony_ci if (ctl & TSI148_LCSR_ITAT_PGM) 7068c2ecf20Sopenharmony_ci *cycle |= VME_PROG; 7078c2ecf20Sopenharmony_ci if (ctl & TSI148_LCSR_ITAT_DATA) 7088c2ecf20Sopenharmony_ci *cycle |= VME_DATA; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci return 0; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci/* 7148c2ecf20Sopenharmony_ci * Allocate and map PCI Resource 7158c2ecf20Sopenharmony_ci */ 7168c2ecf20Sopenharmony_cistatic int tsi148_alloc_resource(struct vme_master_resource *image, 7178c2ecf20Sopenharmony_ci unsigned long long size) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci unsigned long long existing_size; 7208c2ecf20Sopenharmony_ci int retval = 0; 7218c2ecf20Sopenharmony_ci struct pci_dev *pdev; 7228c2ecf20Sopenharmony_ci struct vme_bridge *tsi148_bridge; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci tsi148_bridge = image->parent; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci pdev = to_pci_dev(tsi148_bridge->parent); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci existing_size = (unsigned long long)(image->bus_resource.end - 7298c2ecf20Sopenharmony_ci image->bus_resource.start); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* If the existing size is OK, return */ 7328c2ecf20Sopenharmony_ci if ((size != 0) && (existing_size == (size - 1))) 7338c2ecf20Sopenharmony_ci return 0; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (existing_size != 0) { 7368c2ecf20Sopenharmony_ci iounmap(image->kern_base); 7378c2ecf20Sopenharmony_ci image->kern_base = NULL; 7388c2ecf20Sopenharmony_ci kfree(image->bus_resource.name); 7398c2ecf20Sopenharmony_ci release_resource(&image->bus_resource); 7408c2ecf20Sopenharmony_ci memset(&image->bus_resource, 0, sizeof(image->bus_resource)); 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci /* Exit here if size is zero */ 7448c2ecf20Sopenharmony_ci if (size == 0) 7458c2ecf20Sopenharmony_ci return 0; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (!image->bus_resource.name) { 7488c2ecf20Sopenharmony_ci image->bus_resource.name = kmalloc(VMENAMSIZ+3, GFP_ATOMIC); 7498c2ecf20Sopenharmony_ci if (!image->bus_resource.name) { 7508c2ecf20Sopenharmony_ci retval = -ENOMEM; 7518c2ecf20Sopenharmony_ci goto err_name; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci sprintf((char *)image->bus_resource.name, "%s.%d", tsi148_bridge->name, 7568c2ecf20Sopenharmony_ci image->number); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci image->bus_resource.start = 0; 7598c2ecf20Sopenharmony_ci image->bus_resource.end = (unsigned long)size; 7608c2ecf20Sopenharmony_ci image->bus_resource.flags = IORESOURCE_MEM; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci retval = pci_bus_alloc_resource(pdev->bus, 7638c2ecf20Sopenharmony_ci &image->bus_resource, size, 0x10000, PCIBIOS_MIN_MEM, 7648c2ecf20Sopenharmony_ci 0, NULL, NULL); 7658c2ecf20Sopenharmony_ci if (retval) { 7668c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Failed to allocate mem " 7678c2ecf20Sopenharmony_ci "resource for window %d size 0x%lx start 0x%lx\n", 7688c2ecf20Sopenharmony_ci image->number, (unsigned long)size, 7698c2ecf20Sopenharmony_ci (unsigned long)image->bus_resource.start); 7708c2ecf20Sopenharmony_ci goto err_resource; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci image->kern_base = ioremap( 7748c2ecf20Sopenharmony_ci image->bus_resource.start, size); 7758c2ecf20Sopenharmony_ci if (!image->kern_base) { 7768c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Failed to remap resource\n"); 7778c2ecf20Sopenharmony_ci retval = -ENOMEM; 7788c2ecf20Sopenharmony_ci goto err_remap; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci return 0; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cierr_remap: 7848c2ecf20Sopenharmony_ci release_resource(&image->bus_resource); 7858c2ecf20Sopenharmony_cierr_resource: 7868c2ecf20Sopenharmony_ci kfree(image->bus_resource.name); 7878c2ecf20Sopenharmony_ci memset(&image->bus_resource, 0, sizeof(image->bus_resource)); 7888c2ecf20Sopenharmony_cierr_name: 7898c2ecf20Sopenharmony_ci return retval; 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci/* 7938c2ecf20Sopenharmony_ci * Free and unmap PCI Resource 7948c2ecf20Sopenharmony_ci */ 7958c2ecf20Sopenharmony_cistatic void tsi148_free_resource(struct vme_master_resource *image) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci iounmap(image->kern_base); 7988c2ecf20Sopenharmony_ci image->kern_base = NULL; 7998c2ecf20Sopenharmony_ci release_resource(&image->bus_resource); 8008c2ecf20Sopenharmony_ci kfree(image->bus_resource.name); 8018c2ecf20Sopenharmony_ci memset(&image->bus_resource, 0, sizeof(image->bus_resource)); 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci/* 8058c2ecf20Sopenharmony_ci * Set the attributes of an outbound window. 8068c2ecf20Sopenharmony_ci */ 8078c2ecf20Sopenharmony_cistatic int tsi148_master_set(struct vme_master_resource *image, int enabled, 8088c2ecf20Sopenharmony_ci unsigned long long vme_base, unsigned long long size, u32 aspace, 8098c2ecf20Sopenharmony_ci u32 cycle, u32 dwidth) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci int retval = 0; 8128c2ecf20Sopenharmony_ci unsigned int i; 8138c2ecf20Sopenharmony_ci unsigned int temp_ctl = 0; 8148c2ecf20Sopenharmony_ci unsigned int pci_base_low, pci_base_high; 8158c2ecf20Sopenharmony_ci unsigned int pci_bound_low, pci_bound_high; 8168c2ecf20Sopenharmony_ci unsigned int vme_offset_low, vme_offset_high; 8178c2ecf20Sopenharmony_ci unsigned long long pci_bound, vme_offset, pci_base; 8188c2ecf20Sopenharmony_ci struct vme_bridge *tsi148_bridge; 8198c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 8208c2ecf20Sopenharmony_ci struct pci_bus_region region; 8218c2ecf20Sopenharmony_ci struct pci_dev *pdev; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci tsi148_bridge = image->parent; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci pdev = to_pci_dev(tsi148_bridge->parent); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci /* Verify input data */ 8308c2ecf20Sopenharmony_ci if (vme_base & 0xFFFF) { 8318c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Invalid VME Window " 8328c2ecf20Sopenharmony_ci "alignment\n"); 8338c2ecf20Sopenharmony_ci retval = -EINVAL; 8348c2ecf20Sopenharmony_ci goto err_window; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if ((size == 0) && (enabled != 0)) { 8388c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Size must be non-zero for " 8398c2ecf20Sopenharmony_ci "enabled windows\n"); 8408c2ecf20Sopenharmony_ci retval = -EINVAL; 8418c2ecf20Sopenharmony_ci goto err_window; 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci spin_lock(&image->lock); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci /* Let's allocate the resource here rather than further up the stack as 8478c2ecf20Sopenharmony_ci * it avoids pushing loads of bus dependent stuff up the stack. If size 8488c2ecf20Sopenharmony_ci * is zero, any existing resource will be freed. 8498c2ecf20Sopenharmony_ci */ 8508c2ecf20Sopenharmony_ci retval = tsi148_alloc_resource(image, size); 8518c2ecf20Sopenharmony_ci if (retval) { 8528c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 8538c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Unable to allocate memory for " 8548c2ecf20Sopenharmony_ci "resource\n"); 8558c2ecf20Sopenharmony_ci goto err_res; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (size == 0) { 8598c2ecf20Sopenharmony_ci pci_base = 0; 8608c2ecf20Sopenharmony_ci pci_bound = 0; 8618c2ecf20Sopenharmony_ci vme_offset = 0; 8628c2ecf20Sopenharmony_ci } else { 8638c2ecf20Sopenharmony_ci pcibios_resource_to_bus(pdev->bus, ®ion, 8648c2ecf20Sopenharmony_ci &image->bus_resource); 8658c2ecf20Sopenharmony_ci pci_base = region.start; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci /* 8688c2ecf20Sopenharmony_ci * Bound address is a valid address for the window, adjust 8698c2ecf20Sopenharmony_ci * according to window granularity. 8708c2ecf20Sopenharmony_ci */ 8718c2ecf20Sopenharmony_ci pci_bound = pci_base + (size - 0x10000); 8728c2ecf20Sopenharmony_ci vme_offset = vme_base - pci_base; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci /* Convert 64-bit variables to 2x 32-bit variables */ 8768c2ecf20Sopenharmony_ci reg_split(pci_base, &pci_base_high, &pci_base_low); 8778c2ecf20Sopenharmony_ci reg_split(pci_bound, &pci_bound_high, &pci_bound_low); 8788c2ecf20Sopenharmony_ci reg_split(vme_offset, &vme_offset_high, &vme_offset_low); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (pci_base_low & 0xFFFF) { 8818c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 8828c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Invalid PCI base alignment\n"); 8838c2ecf20Sopenharmony_ci retval = -EINVAL; 8848c2ecf20Sopenharmony_ci goto err_gran; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci if (pci_bound_low & 0xFFFF) { 8878c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 8888c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Invalid PCI bound alignment\n"); 8898c2ecf20Sopenharmony_ci retval = -EINVAL; 8908c2ecf20Sopenharmony_ci goto err_gran; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci if (vme_offset_low & 0xFFFF) { 8938c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 8948c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Invalid VME Offset " 8958c2ecf20Sopenharmony_ci "alignment\n"); 8968c2ecf20Sopenharmony_ci retval = -EINVAL; 8978c2ecf20Sopenharmony_ci goto err_gran; 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci i = image->number; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci /* Disable while we are mucking around */ 9038c2ecf20Sopenharmony_ci temp_ctl = ioread32be(bridge->base + TSI148_LCSR_OT[i] + 9048c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTAT); 9058c2ecf20Sopenharmony_ci temp_ctl &= ~TSI148_LCSR_OTAT_EN; 9068c2ecf20Sopenharmony_ci iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_OT[i] + 9078c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTAT); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci /* Setup 2eSST speeds */ 9108c2ecf20Sopenharmony_ci temp_ctl &= ~TSI148_LCSR_OTAT_2eSSTM_M; 9118c2ecf20Sopenharmony_ci switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) { 9128c2ecf20Sopenharmony_ci case VME_2eSST160: 9138c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_2eSSTM_160; 9148c2ecf20Sopenharmony_ci break; 9158c2ecf20Sopenharmony_ci case VME_2eSST267: 9168c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_2eSSTM_267; 9178c2ecf20Sopenharmony_ci break; 9188c2ecf20Sopenharmony_ci case VME_2eSST320: 9198c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_2eSSTM_320; 9208c2ecf20Sopenharmony_ci break; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci /* Setup cycle types */ 9248c2ecf20Sopenharmony_ci if (cycle & VME_BLT) { 9258c2ecf20Sopenharmony_ci temp_ctl &= ~TSI148_LCSR_OTAT_TM_M; 9268c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_TM_BLT; 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci if (cycle & VME_MBLT) { 9298c2ecf20Sopenharmony_ci temp_ctl &= ~TSI148_LCSR_OTAT_TM_M; 9308c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_TM_MBLT; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci if (cycle & VME_2eVME) { 9338c2ecf20Sopenharmony_ci temp_ctl &= ~TSI148_LCSR_OTAT_TM_M; 9348c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_TM_2eVME; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci if (cycle & VME_2eSST) { 9378c2ecf20Sopenharmony_ci temp_ctl &= ~TSI148_LCSR_OTAT_TM_M; 9388c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_TM_2eSST; 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci if (cycle & VME_2eSSTB) { 9418c2ecf20Sopenharmony_ci dev_warn(tsi148_bridge->parent, "Currently not setting " 9428c2ecf20Sopenharmony_ci "Broadcast Select Registers\n"); 9438c2ecf20Sopenharmony_ci temp_ctl &= ~TSI148_LCSR_OTAT_TM_M; 9448c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_TM_2eSSTB; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci /* Setup data width */ 9488c2ecf20Sopenharmony_ci temp_ctl &= ~TSI148_LCSR_OTAT_DBW_M; 9498c2ecf20Sopenharmony_ci switch (dwidth) { 9508c2ecf20Sopenharmony_ci case VME_D16: 9518c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_DBW_16; 9528c2ecf20Sopenharmony_ci break; 9538c2ecf20Sopenharmony_ci case VME_D32: 9548c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_DBW_32; 9558c2ecf20Sopenharmony_ci break; 9568c2ecf20Sopenharmony_ci default: 9578c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 9588c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Invalid data width\n"); 9598c2ecf20Sopenharmony_ci retval = -EINVAL; 9608c2ecf20Sopenharmony_ci goto err_dwidth; 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci /* Setup address space */ 9648c2ecf20Sopenharmony_ci temp_ctl &= ~TSI148_LCSR_OTAT_AMODE_M; 9658c2ecf20Sopenharmony_ci switch (aspace) { 9668c2ecf20Sopenharmony_ci case VME_A16: 9678c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_AMODE_A16; 9688c2ecf20Sopenharmony_ci break; 9698c2ecf20Sopenharmony_ci case VME_A24: 9708c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_AMODE_A24; 9718c2ecf20Sopenharmony_ci break; 9728c2ecf20Sopenharmony_ci case VME_A32: 9738c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_AMODE_A32; 9748c2ecf20Sopenharmony_ci break; 9758c2ecf20Sopenharmony_ci case VME_A64: 9768c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_AMODE_A64; 9778c2ecf20Sopenharmony_ci break; 9788c2ecf20Sopenharmony_ci case VME_CRCSR: 9798c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_AMODE_CRCSR; 9808c2ecf20Sopenharmony_ci break; 9818c2ecf20Sopenharmony_ci case VME_USER1: 9828c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_AMODE_USER1; 9838c2ecf20Sopenharmony_ci break; 9848c2ecf20Sopenharmony_ci case VME_USER2: 9858c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_AMODE_USER2; 9868c2ecf20Sopenharmony_ci break; 9878c2ecf20Sopenharmony_ci case VME_USER3: 9888c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_AMODE_USER3; 9898c2ecf20Sopenharmony_ci break; 9908c2ecf20Sopenharmony_ci case VME_USER4: 9918c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_AMODE_USER4; 9928c2ecf20Sopenharmony_ci break; 9938c2ecf20Sopenharmony_ci default: 9948c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 9958c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Invalid address space\n"); 9968c2ecf20Sopenharmony_ci retval = -EINVAL; 9978c2ecf20Sopenharmony_ci goto err_aspace; 9988c2ecf20Sopenharmony_ci break; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci temp_ctl &= ~(3<<4); 10028c2ecf20Sopenharmony_ci if (cycle & VME_SUPER) 10038c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_SUP; 10048c2ecf20Sopenharmony_ci if (cycle & VME_PROG) 10058c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_PGM; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci /* Setup mapping */ 10088c2ecf20Sopenharmony_ci iowrite32be(pci_base_high, bridge->base + TSI148_LCSR_OT[i] + 10098c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTSAU); 10108c2ecf20Sopenharmony_ci iowrite32be(pci_base_low, bridge->base + TSI148_LCSR_OT[i] + 10118c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTSAL); 10128c2ecf20Sopenharmony_ci iowrite32be(pci_bound_high, bridge->base + TSI148_LCSR_OT[i] + 10138c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTEAU); 10148c2ecf20Sopenharmony_ci iowrite32be(pci_bound_low, bridge->base + TSI148_LCSR_OT[i] + 10158c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTEAL); 10168c2ecf20Sopenharmony_ci iowrite32be(vme_offset_high, bridge->base + TSI148_LCSR_OT[i] + 10178c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTOFU); 10188c2ecf20Sopenharmony_ci iowrite32be(vme_offset_low, bridge->base + TSI148_LCSR_OT[i] + 10198c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTOFL); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci /* Write ctl reg without enable */ 10228c2ecf20Sopenharmony_ci iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_OT[i] + 10238c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTAT); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci if (enabled) 10268c2ecf20Sopenharmony_ci temp_ctl |= TSI148_LCSR_OTAT_EN; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci iowrite32be(temp_ctl, bridge->base + TSI148_LCSR_OT[i] + 10298c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTAT); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 10328c2ecf20Sopenharmony_ci return 0; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_cierr_aspace: 10358c2ecf20Sopenharmony_cierr_dwidth: 10368c2ecf20Sopenharmony_cierr_gran: 10378c2ecf20Sopenharmony_ci tsi148_free_resource(image); 10388c2ecf20Sopenharmony_cierr_res: 10398c2ecf20Sopenharmony_cierr_window: 10408c2ecf20Sopenharmony_ci return retval; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci} 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci/* 10458c2ecf20Sopenharmony_ci * Set the attributes of an outbound window. 10468c2ecf20Sopenharmony_ci * 10478c2ecf20Sopenharmony_ci * XXX Not parsing prefetch information. 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_cistatic int __tsi148_master_get(struct vme_master_resource *image, int *enabled, 10508c2ecf20Sopenharmony_ci unsigned long long *vme_base, unsigned long long *size, u32 *aspace, 10518c2ecf20Sopenharmony_ci u32 *cycle, u32 *dwidth) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci unsigned int i, ctl; 10548c2ecf20Sopenharmony_ci unsigned int pci_base_low, pci_base_high; 10558c2ecf20Sopenharmony_ci unsigned int pci_bound_low, pci_bound_high; 10568c2ecf20Sopenharmony_ci unsigned int vme_offset_low, vme_offset_high; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci unsigned long long pci_base, pci_bound, vme_offset; 10598c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci bridge = image->parent->driver_priv; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci i = image->number; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci ctl = ioread32be(bridge->base + TSI148_LCSR_OT[i] + 10668c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTAT); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci pci_base_high = ioread32be(bridge->base + TSI148_LCSR_OT[i] + 10698c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTSAU); 10708c2ecf20Sopenharmony_ci pci_base_low = ioread32be(bridge->base + TSI148_LCSR_OT[i] + 10718c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTSAL); 10728c2ecf20Sopenharmony_ci pci_bound_high = ioread32be(bridge->base + TSI148_LCSR_OT[i] + 10738c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTEAU); 10748c2ecf20Sopenharmony_ci pci_bound_low = ioread32be(bridge->base + TSI148_LCSR_OT[i] + 10758c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTEAL); 10768c2ecf20Sopenharmony_ci vme_offset_high = ioread32be(bridge->base + TSI148_LCSR_OT[i] + 10778c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTOFU); 10788c2ecf20Sopenharmony_ci vme_offset_low = ioread32be(bridge->base + TSI148_LCSR_OT[i] + 10798c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTOFL); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci /* Convert 64-bit variables to 2x 32-bit variables */ 10828c2ecf20Sopenharmony_ci reg_join(pci_base_high, pci_base_low, &pci_base); 10838c2ecf20Sopenharmony_ci reg_join(pci_bound_high, pci_bound_low, &pci_bound); 10848c2ecf20Sopenharmony_ci reg_join(vme_offset_high, vme_offset_low, &vme_offset); 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci *vme_base = pci_base + vme_offset; 10878c2ecf20Sopenharmony_ci *size = (unsigned long long)(pci_bound - pci_base) + 0x10000; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci *enabled = 0; 10908c2ecf20Sopenharmony_ci *aspace = 0; 10918c2ecf20Sopenharmony_ci *cycle = 0; 10928c2ecf20Sopenharmony_ci *dwidth = 0; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci if (ctl & TSI148_LCSR_OTAT_EN) 10958c2ecf20Sopenharmony_ci *enabled = 1; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci /* Setup address space */ 10988c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_A16) 10998c2ecf20Sopenharmony_ci *aspace |= VME_A16; 11008c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_A24) 11018c2ecf20Sopenharmony_ci *aspace |= VME_A24; 11028c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_A32) 11038c2ecf20Sopenharmony_ci *aspace |= VME_A32; 11048c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_A64) 11058c2ecf20Sopenharmony_ci *aspace |= VME_A64; 11068c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_CRCSR) 11078c2ecf20Sopenharmony_ci *aspace |= VME_CRCSR; 11088c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_USER1) 11098c2ecf20Sopenharmony_ci *aspace |= VME_USER1; 11108c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_USER2) 11118c2ecf20Sopenharmony_ci *aspace |= VME_USER2; 11128c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_USER3) 11138c2ecf20Sopenharmony_ci *aspace |= VME_USER3; 11148c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_AMODE_M) == TSI148_LCSR_OTAT_AMODE_USER4) 11158c2ecf20Sopenharmony_ci *aspace |= VME_USER4; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci /* Setup 2eSST speeds */ 11188c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_2eSSTM_M) == TSI148_LCSR_OTAT_2eSSTM_160) 11198c2ecf20Sopenharmony_ci *cycle |= VME_2eSST160; 11208c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_2eSSTM_M) == TSI148_LCSR_OTAT_2eSSTM_267) 11218c2ecf20Sopenharmony_ci *cycle |= VME_2eSST267; 11228c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_2eSSTM_M) == TSI148_LCSR_OTAT_2eSSTM_320) 11238c2ecf20Sopenharmony_ci *cycle |= VME_2eSST320; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci /* Setup cycle types */ 11268c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_TM_M) == TSI148_LCSR_OTAT_TM_SCT) 11278c2ecf20Sopenharmony_ci *cycle |= VME_SCT; 11288c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_TM_M) == TSI148_LCSR_OTAT_TM_BLT) 11298c2ecf20Sopenharmony_ci *cycle |= VME_BLT; 11308c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_TM_M) == TSI148_LCSR_OTAT_TM_MBLT) 11318c2ecf20Sopenharmony_ci *cycle |= VME_MBLT; 11328c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_TM_M) == TSI148_LCSR_OTAT_TM_2eVME) 11338c2ecf20Sopenharmony_ci *cycle |= VME_2eVME; 11348c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_TM_M) == TSI148_LCSR_OTAT_TM_2eSST) 11358c2ecf20Sopenharmony_ci *cycle |= VME_2eSST; 11368c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_TM_M) == TSI148_LCSR_OTAT_TM_2eSSTB) 11378c2ecf20Sopenharmony_ci *cycle |= VME_2eSSTB; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (ctl & TSI148_LCSR_OTAT_SUP) 11408c2ecf20Sopenharmony_ci *cycle |= VME_SUPER; 11418c2ecf20Sopenharmony_ci else 11428c2ecf20Sopenharmony_ci *cycle |= VME_USER; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if (ctl & TSI148_LCSR_OTAT_PGM) 11458c2ecf20Sopenharmony_ci *cycle |= VME_PROG; 11468c2ecf20Sopenharmony_ci else 11478c2ecf20Sopenharmony_ci *cycle |= VME_DATA; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci /* Setup data width */ 11508c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_DBW_M) == TSI148_LCSR_OTAT_DBW_16) 11518c2ecf20Sopenharmony_ci *dwidth = VME_D16; 11528c2ecf20Sopenharmony_ci if ((ctl & TSI148_LCSR_OTAT_DBW_M) == TSI148_LCSR_OTAT_DBW_32) 11538c2ecf20Sopenharmony_ci *dwidth = VME_D32; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci return 0; 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_cistatic int tsi148_master_get(struct vme_master_resource *image, int *enabled, 11608c2ecf20Sopenharmony_ci unsigned long long *vme_base, unsigned long long *size, u32 *aspace, 11618c2ecf20Sopenharmony_ci u32 *cycle, u32 *dwidth) 11628c2ecf20Sopenharmony_ci{ 11638c2ecf20Sopenharmony_ci int retval; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci spin_lock(&image->lock); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci retval = __tsi148_master_get(image, enabled, vme_base, size, aspace, 11688c2ecf20Sopenharmony_ci cycle, dwidth); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci return retval; 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_cistatic ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf, 11768c2ecf20Sopenharmony_ci size_t count, loff_t offset) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci int retval, enabled; 11798c2ecf20Sopenharmony_ci unsigned long long vme_base, size; 11808c2ecf20Sopenharmony_ci u32 aspace, cycle, dwidth; 11818c2ecf20Sopenharmony_ci struct vme_error_handler *handler = NULL; 11828c2ecf20Sopenharmony_ci struct vme_bridge *tsi148_bridge; 11838c2ecf20Sopenharmony_ci void __iomem *addr = image->kern_base + offset; 11848c2ecf20Sopenharmony_ci unsigned int done = 0; 11858c2ecf20Sopenharmony_ci unsigned int count32; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci tsi148_bridge = image->parent; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci spin_lock(&image->lock); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci if (err_chk) { 11928c2ecf20Sopenharmony_ci __tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, 11938c2ecf20Sopenharmony_ci &cycle, &dwidth); 11948c2ecf20Sopenharmony_ci handler = vme_register_error_handler(tsi148_bridge, aspace, 11958c2ecf20Sopenharmony_ci vme_base + offset, count); 11968c2ecf20Sopenharmony_ci if (!handler) { 11978c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 11988c2ecf20Sopenharmony_ci return -ENOMEM; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci /* The following code handles VME address alignment. We cannot use 12038c2ecf20Sopenharmony_ci * memcpy_xxx here because it may cut data transfers in to 8-bit 12048c2ecf20Sopenharmony_ci * cycles when D16 or D32 cycles are required on the VME bus. 12058c2ecf20Sopenharmony_ci * On the other hand, the bridge itself assures that the maximum data 12068c2ecf20Sopenharmony_ci * cycle configured for the transfer is used and splits it 12078c2ecf20Sopenharmony_ci * automatically for non-aligned addresses, so we don't want the 12088c2ecf20Sopenharmony_ci * overhead of needlessly forcing small transfers for the entire cycle. 12098c2ecf20Sopenharmony_ci */ 12108c2ecf20Sopenharmony_ci if ((uintptr_t)addr & 0x1) { 12118c2ecf20Sopenharmony_ci *(u8 *)buf = ioread8(addr); 12128c2ecf20Sopenharmony_ci done += 1; 12138c2ecf20Sopenharmony_ci if (done == count) 12148c2ecf20Sopenharmony_ci goto out; 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci if ((uintptr_t)(addr + done) & 0x2) { 12178c2ecf20Sopenharmony_ci if ((count - done) < 2) { 12188c2ecf20Sopenharmony_ci *(u8 *)(buf + done) = ioread8(addr + done); 12198c2ecf20Sopenharmony_ci done += 1; 12208c2ecf20Sopenharmony_ci goto out; 12218c2ecf20Sopenharmony_ci } else { 12228c2ecf20Sopenharmony_ci *(u16 *)(buf + done) = ioread16(addr + done); 12238c2ecf20Sopenharmony_ci done += 2; 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci count32 = (count - done) & ~0x3; 12288c2ecf20Sopenharmony_ci while (done < count32) { 12298c2ecf20Sopenharmony_ci *(u32 *)(buf + done) = ioread32(addr + done); 12308c2ecf20Sopenharmony_ci done += 4; 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci if ((count - done) & 0x2) { 12348c2ecf20Sopenharmony_ci *(u16 *)(buf + done) = ioread16(addr + done); 12358c2ecf20Sopenharmony_ci done += 2; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci if ((count - done) & 0x1) { 12388c2ecf20Sopenharmony_ci *(u8 *)(buf + done) = ioread8(addr + done); 12398c2ecf20Sopenharmony_ci done += 1; 12408c2ecf20Sopenharmony_ci } 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ciout: 12438c2ecf20Sopenharmony_ci retval = count; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci if (err_chk) { 12468c2ecf20Sopenharmony_ci if (handler->num_errors) { 12478c2ecf20Sopenharmony_ci dev_err(image->parent->parent, 12488c2ecf20Sopenharmony_ci "First VME read error detected an at address 0x%llx\n", 12498c2ecf20Sopenharmony_ci handler->first_error); 12508c2ecf20Sopenharmony_ci retval = handler->first_error - (vme_base + offset); 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci vme_unregister_error_handler(handler); 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci return retval; 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_cistatic ssize_t tsi148_master_write(struct vme_master_resource *image, void *buf, 12628c2ecf20Sopenharmony_ci size_t count, loff_t offset) 12638c2ecf20Sopenharmony_ci{ 12648c2ecf20Sopenharmony_ci int retval = 0, enabled; 12658c2ecf20Sopenharmony_ci unsigned long long vme_base, size; 12668c2ecf20Sopenharmony_ci u32 aspace, cycle, dwidth; 12678c2ecf20Sopenharmony_ci void __iomem *addr = image->kern_base + offset; 12688c2ecf20Sopenharmony_ci unsigned int done = 0; 12698c2ecf20Sopenharmony_ci unsigned int count32; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci struct vme_error_handler *handler = NULL; 12728c2ecf20Sopenharmony_ci struct vme_bridge *tsi148_bridge; 12738c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci tsi148_bridge = image->parent; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci spin_lock(&image->lock); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci if (err_chk) { 12828c2ecf20Sopenharmony_ci __tsi148_master_get(image, &enabled, &vme_base, &size, &aspace, 12838c2ecf20Sopenharmony_ci &cycle, &dwidth); 12848c2ecf20Sopenharmony_ci handler = vme_register_error_handler(tsi148_bridge, aspace, 12858c2ecf20Sopenharmony_ci vme_base + offset, count); 12868c2ecf20Sopenharmony_ci if (!handler) { 12878c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 12888c2ecf20Sopenharmony_ci return -ENOMEM; 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci /* Here we apply for the same strategy we do in master_read 12938c2ecf20Sopenharmony_ci * function in order to assure the correct cycles. 12948c2ecf20Sopenharmony_ci */ 12958c2ecf20Sopenharmony_ci if ((uintptr_t)addr & 0x1) { 12968c2ecf20Sopenharmony_ci iowrite8(*(u8 *)buf, addr); 12978c2ecf20Sopenharmony_ci done += 1; 12988c2ecf20Sopenharmony_ci if (done == count) 12998c2ecf20Sopenharmony_ci goto out; 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci if ((uintptr_t)(addr + done) & 0x2) { 13028c2ecf20Sopenharmony_ci if ((count - done) < 2) { 13038c2ecf20Sopenharmony_ci iowrite8(*(u8 *)(buf + done), addr + done); 13048c2ecf20Sopenharmony_ci done += 1; 13058c2ecf20Sopenharmony_ci goto out; 13068c2ecf20Sopenharmony_ci } else { 13078c2ecf20Sopenharmony_ci iowrite16(*(u16 *)(buf + done), addr + done); 13088c2ecf20Sopenharmony_ci done += 2; 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci } 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci count32 = (count - done) & ~0x3; 13138c2ecf20Sopenharmony_ci while (done < count32) { 13148c2ecf20Sopenharmony_ci iowrite32(*(u32 *)(buf + done), addr + done); 13158c2ecf20Sopenharmony_ci done += 4; 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci if ((count - done) & 0x2) { 13198c2ecf20Sopenharmony_ci iowrite16(*(u16 *)(buf + done), addr + done); 13208c2ecf20Sopenharmony_ci done += 2; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci if ((count - done) & 0x1) { 13238c2ecf20Sopenharmony_ci iowrite8(*(u8 *)(buf + done), addr + done); 13248c2ecf20Sopenharmony_ci done += 1; 13258c2ecf20Sopenharmony_ci } 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ciout: 13288c2ecf20Sopenharmony_ci retval = count; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci /* 13318c2ecf20Sopenharmony_ci * Writes are posted. We need to do a read on the VME bus to flush out 13328c2ecf20Sopenharmony_ci * all of the writes before we check for errors. We can't guarantee 13338c2ecf20Sopenharmony_ci * that reading the data we have just written is safe. It is believed 13348c2ecf20Sopenharmony_ci * that there isn't any read, write re-ordering, so we can read any 13358c2ecf20Sopenharmony_ci * location in VME space, so lets read the Device ID from the tsi148's 13368c2ecf20Sopenharmony_ci * own registers as mapped into CR/CSR space. 13378c2ecf20Sopenharmony_ci * 13388c2ecf20Sopenharmony_ci * We check for saved errors in the written address range/space. 13398c2ecf20Sopenharmony_ci */ 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci if (err_chk) { 13428c2ecf20Sopenharmony_ci ioread16(bridge->flush_image->kern_base + 0x7F000); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci if (handler->num_errors) { 13458c2ecf20Sopenharmony_ci dev_warn(tsi148_bridge->parent, 13468c2ecf20Sopenharmony_ci "First VME write error detected an at address 0x%llx\n", 13478c2ecf20Sopenharmony_ci handler->first_error); 13488c2ecf20Sopenharmony_ci retval = handler->first_error - (vme_base + offset); 13498c2ecf20Sopenharmony_ci } 13508c2ecf20Sopenharmony_ci vme_unregister_error_handler(handler); 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci return retval; 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci/* 13598c2ecf20Sopenharmony_ci * Perform an RMW cycle on the VME bus. 13608c2ecf20Sopenharmony_ci * 13618c2ecf20Sopenharmony_ci * Requires a previously configured master window, returns final value. 13628c2ecf20Sopenharmony_ci */ 13638c2ecf20Sopenharmony_cistatic unsigned int tsi148_master_rmw(struct vme_master_resource *image, 13648c2ecf20Sopenharmony_ci unsigned int mask, unsigned int compare, unsigned int swap, 13658c2ecf20Sopenharmony_ci loff_t offset) 13668c2ecf20Sopenharmony_ci{ 13678c2ecf20Sopenharmony_ci unsigned long long pci_addr; 13688c2ecf20Sopenharmony_ci unsigned int pci_addr_high, pci_addr_low; 13698c2ecf20Sopenharmony_ci u32 tmp, result; 13708c2ecf20Sopenharmony_ci int i; 13718c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci bridge = image->parent->driver_priv; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci /* Find the PCI address that maps to the desired VME address */ 13768c2ecf20Sopenharmony_ci i = image->number; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci /* Locking as we can only do one of these at a time */ 13798c2ecf20Sopenharmony_ci mutex_lock(&bridge->vme_rmw); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci /* Lock image */ 13828c2ecf20Sopenharmony_ci spin_lock(&image->lock); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci pci_addr_high = ioread32be(bridge->base + TSI148_LCSR_OT[i] + 13858c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTSAU); 13868c2ecf20Sopenharmony_ci pci_addr_low = ioread32be(bridge->base + TSI148_LCSR_OT[i] + 13878c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTSAL); 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci reg_join(pci_addr_high, pci_addr_low, &pci_addr); 13908c2ecf20Sopenharmony_ci reg_split(pci_addr + offset, &pci_addr_high, &pci_addr_low); 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci /* Configure registers */ 13938c2ecf20Sopenharmony_ci iowrite32be(mask, bridge->base + TSI148_LCSR_RMWEN); 13948c2ecf20Sopenharmony_ci iowrite32be(compare, bridge->base + TSI148_LCSR_RMWC); 13958c2ecf20Sopenharmony_ci iowrite32be(swap, bridge->base + TSI148_LCSR_RMWS); 13968c2ecf20Sopenharmony_ci iowrite32be(pci_addr_high, bridge->base + TSI148_LCSR_RMWAU); 13978c2ecf20Sopenharmony_ci iowrite32be(pci_addr_low, bridge->base + TSI148_LCSR_RMWAL); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci /* Enable RMW */ 14008c2ecf20Sopenharmony_ci tmp = ioread32be(bridge->base + TSI148_LCSR_VMCTRL); 14018c2ecf20Sopenharmony_ci tmp |= TSI148_LCSR_VMCTRL_RMWEN; 14028c2ecf20Sopenharmony_ci iowrite32be(tmp, bridge->base + TSI148_LCSR_VMCTRL); 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci /* Kick process off with a read to the required address. */ 14058c2ecf20Sopenharmony_ci result = ioread32be(image->kern_base + offset); 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci /* Disable RMW */ 14088c2ecf20Sopenharmony_ci tmp = ioread32be(bridge->base + TSI148_LCSR_VMCTRL); 14098c2ecf20Sopenharmony_ci tmp &= ~TSI148_LCSR_VMCTRL_RMWEN; 14108c2ecf20Sopenharmony_ci iowrite32be(tmp, bridge->base + TSI148_LCSR_VMCTRL); 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci spin_unlock(&image->lock); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci mutex_unlock(&bridge->vme_rmw); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci return result; 14178c2ecf20Sopenharmony_ci} 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_cistatic int tsi148_dma_set_vme_src_attributes(struct device *dev, __be32 *attr, 14208c2ecf20Sopenharmony_ci u32 aspace, u32 cycle, u32 dwidth) 14218c2ecf20Sopenharmony_ci{ 14228c2ecf20Sopenharmony_ci u32 val; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci val = be32_to_cpu(*attr); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci /* Setup 2eSST speeds */ 14278c2ecf20Sopenharmony_ci switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) { 14288c2ecf20Sopenharmony_ci case VME_2eSST160: 14298c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_2eSSTM_160; 14308c2ecf20Sopenharmony_ci break; 14318c2ecf20Sopenharmony_ci case VME_2eSST267: 14328c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_2eSSTM_267; 14338c2ecf20Sopenharmony_ci break; 14348c2ecf20Sopenharmony_ci case VME_2eSST320: 14358c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_2eSSTM_320; 14368c2ecf20Sopenharmony_ci break; 14378c2ecf20Sopenharmony_ci } 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci /* Setup cycle types */ 14408c2ecf20Sopenharmony_ci if (cycle & VME_SCT) 14418c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_TM_SCT; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci if (cycle & VME_BLT) 14448c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_TM_BLT; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci if (cycle & VME_MBLT) 14478c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_TM_MBLT; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci if (cycle & VME_2eVME) 14508c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_TM_2eVME; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci if (cycle & VME_2eSST) 14538c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_TM_2eSST; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci if (cycle & VME_2eSSTB) { 14568c2ecf20Sopenharmony_ci dev_err(dev, "Currently not setting Broadcast Select " 14578c2ecf20Sopenharmony_ci "Registers\n"); 14588c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_TM_2eSSTB; 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci /* Setup data width */ 14628c2ecf20Sopenharmony_ci switch (dwidth) { 14638c2ecf20Sopenharmony_ci case VME_D16: 14648c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_DBW_16; 14658c2ecf20Sopenharmony_ci break; 14668c2ecf20Sopenharmony_ci case VME_D32: 14678c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_DBW_32; 14688c2ecf20Sopenharmony_ci break; 14698c2ecf20Sopenharmony_ci default: 14708c2ecf20Sopenharmony_ci dev_err(dev, "Invalid data width\n"); 14718c2ecf20Sopenharmony_ci return -EINVAL; 14728c2ecf20Sopenharmony_ci } 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci /* Setup address space */ 14758c2ecf20Sopenharmony_ci switch (aspace) { 14768c2ecf20Sopenharmony_ci case VME_A16: 14778c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_AMODE_A16; 14788c2ecf20Sopenharmony_ci break; 14798c2ecf20Sopenharmony_ci case VME_A24: 14808c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_AMODE_A24; 14818c2ecf20Sopenharmony_ci break; 14828c2ecf20Sopenharmony_ci case VME_A32: 14838c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_AMODE_A32; 14848c2ecf20Sopenharmony_ci break; 14858c2ecf20Sopenharmony_ci case VME_A64: 14868c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_AMODE_A64; 14878c2ecf20Sopenharmony_ci break; 14888c2ecf20Sopenharmony_ci case VME_CRCSR: 14898c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_AMODE_CRCSR; 14908c2ecf20Sopenharmony_ci break; 14918c2ecf20Sopenharmony_ci case VME_USER1: 14928c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_AMODE_USER1; 14938c2ecf20Sopenharmony_ci break; 14948c2ecf20Sopenharmony_ci case VME_USER2: 14958c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_AMODE_USER2; 14968c2ecf20Sopenharmony_ci break; 14978c2ecf20Sopenharmony_ci case VME_USER3: 14988c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_AMODE_USER3; 14998c2ecf20Sopenharmony_ci break; 15008c2ecf20Sopenharmony_ci case VME_USER4: 15018c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_AMODE_USER4; 15028c2ecf20Sopenharmony_ci break; 15038c2ecf20Sopenharmony_ci default: 15048c2ecf20Sopenharmony_ci dev_err(dev, "Invalid address space\n"); 15058c2ecf20Sopenharmony_ci return -EINVAL; 15068c2ecf20Sopenharmony_ci break; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci if (cycle & VME_SUPER) 15108c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_SUP; 15118c2ecf20Sopenharmony_ci if (cycle & VME_PROG) 15128c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_PGM; 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci *attr = cpu_to_be32(val); 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci return 0; 15178c2ecf20Sopenharmony_ci} 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_cistatic int tsi148_dma_set_vme_dest_attributes(struct device *dev, __be32 *attr, 15208c2ecf20Sopenharmony_ci u32 aspace, u32 cycle, u32 dwidth) 15218c2ecf20Sopenharmony_ci{ 15228c2ecf20Sopenharmony_ci u32 val; 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci val = be32_to_cpu(*attr); 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci /* Setup 2eSST speeds */ 15278c2ecf20Sopenharmony_ci switch (cycle & (VME_2eSST160 | VME_2eSST267 | VME_2eSST320)) { 15288c2ecf20Sopenharmony_ci case VME_2eSST160: 15298c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_2eSSTM_160; 15308c2ecf20Sopenharmony_ci break; 15318c2ecf20Sopenharmony_ci case VME_2eSST267: 15328c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_2eSSTM_267; 15338c2ecf20Sopenharmony_ci break; 15348c2ecf20Sopenharmony_ci case VME_2eSST320: 15358c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_2eSSTM_320; 15368c2ecf20Sopenharmony_ci break; 15378c2ecf20Sopenharmony_ci } 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci /* Setup cycle types */ 15408c2ecf20Sopenharmony_ci if (cycle & VME_SCT) 15418c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_TM_SCT; 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci if (cycle & VME_BLT) 15448c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_TM_BLT; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci if (cycle & VME_MBLT) 15478c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_TM_MBLT; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci if (cycle & VME_2eVME) 15508c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_TM_2eVME; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci if (cycle & VME_2eSST) 15538c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_TM_2eSST; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci if (cycle & VME_2eSSTB) { 15568c2ecf20Sopenharmony_ci dev_err(dev, "Currently not setting Broadcast Select " 15578c2ecf20Sopenharmony_ci "Registers\n"); 15588c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_TM_2eSSTB; 15598c2ecf20Sopenharmony_ci } 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci /* Setup data width */ 15628c2ecf20Sopenharmony_ci switch (dwidth) { 15638c2ecf20Sopenharmony_ci case VME_D16: 15648c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_DBW_16; 15658c2ecf20Sopenharmony_ci break; 15668c2ecf20Sopenharmony_ci case VME_D32: 15678c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_DBW_32; 15688c2ecf20Sopenharmony_ci break; 15698c2ecf20Sopenharmony_ci default: 15708c2ecf20Sopenharmony_ci dev_err(dev, "Invalid data width\n"); 15718c2ecf20Sopenharmony_ci return -EINVAL; 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci /* Setup address space */ 15758c2ecf20Sopenharmony_ci switch (aspace) { 15768c2ecf20Sopenharmony_ci case VME_A16: 15778c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_AMODE_A16; 15788c2ecf20Sopenharmony_ci break; 15798c2ecf20Sopenharmony_ci case VME_A24: 15808c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_AMODE_A24; 15818c2ecf20Sopenharmony_ci break; 15828c2ecf20Sopenharmony_ci case VME_A32: 15838c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_AMODE_A32; 15848c2ecf20Sopenharmony_ci break; 15858c2ecf20Sopenharmony_ci case VME_A64: 15868c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_AMODE_A64; 15878c2ecf20Sopenharmony_ci break; 15888c2ecf20Sopenharmony_ci case VME_CRCSR: 15898c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_AMODE_CRCSR; 15908c2ecf20Sopenharmony_ci break; 15918c2ecf20Sopenharmony_ci case VME_USER1: 15928c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_AMODE_USER1; 15938c2ecf20Sopenharmony_ci break; 15948c2ecf20Sopenharmony_ci case VME_USER2: 15958c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_AMODE_USER2; 15968c2ecf20Sopenharmony_ci break; 15978c2ecf20Sopenharmony_ci case VME_USER3: 15988c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_AMODE_USER3; 15998c2ecf20Sopenharmony_ci break; 16008c2ecf20Sopenharmony_ci case VME_USER4: 16018c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_AMODE_USER4; 16028c2ecf20Sopenharmony_ci break; 16038c2ecf20Sopenharmony_ci default: 16048c2ecf20Sopenharmony_ci dev_err(dev, "Invalid address space\n"); 16058c2ecf20Sopenharmony_ci return -EINVAL; 16068c2ecf20Sopenharmony_ci break; 16078c2ecf20Sopenharmony_ci } 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci if (cycle & VME_SUPER) 16108c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_SUP; 16118c2ecf20Sopenharmony_ci if (cycle & VME_PROG) 16128c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DDAT_PGM; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci *attr = cpu_to_be32(val); 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci return 0; 16178c2ecf20Sopenharmony_ci} 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci/* 16208c2ecf20Sopenharmony_ci * Add a link list descriptor to the list 16218c2ecf20Sopenharmony_ci * 16228c2ecf20Sopenharmony_ci * Note: DMA engine expects the DMA descriptor to be big endian. 16238c2ecf20Sopenharmony_ci */ 16248c2ecf20Sopenharmony_cistatic int tsi148_dma_list_add(struct vme_dma_list *list, 16258c2ecf20Sopenharmony_ci struct vme_dma_attr *src, struct vme_dma_attr *dest, size_t count) 16268c2ecf20Sopenharmony_ci{ 16278c2ecf20Sopenharmony_ci struct tsi148_dma_entry *entry, *prev; 16288c2ecf20Sopenharmony_ci u32 address_high, address_low, val; 16298c2ecf20Sopenharmony_ci struct vme_dma_pattern *pattern_attr; 16308c2ecf20Sopenharmony_ci struct vme_dma_pci *pci_attr; 16318c2ecf20Sopenharmony_ci struct vme_dma_vme *vme_attr; 16328c2ecf20Sopenharmony_ci int retval = 0; 16338c2ecf20Sopenharmony_ci struct vme_bridge *tsi148_bridge; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci tsi148_bridge = list->parent->parent; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci /* Descriptor must be aligned on 64-bit boundaries */ 16388c2ecf20Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 16398c2ecf20Sopenharmony_ci if (!entry) { 16408c2ecf20Sopenharmony_ci retval = -ENOMEM; 16418c2ecf20Sopenharmony_ci goto err_mem; 16428c2ecf20Sopenharmony_ci } 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci /* Test descriptor alignment */ 16458c2ecf20Sopenharmony_ci if ((unsigned long)&entry->descriptor & 0x7) { 16468c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Descriptor not aligned to 8 " 16478c2ecf20Sopenharmony_ci "byte boundary as required: %p\n", 16488c2ecf20Sopenharmony_ci &entry->descriptor); 16498c2ecf20Sopenharmony_ci retval = -EINVAL; 16508c2ecf20Sopenharmony_ci goto err_align; 16518c2ecf20Sopenharmony_ci } 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci /* Given we are going to fill out the structure, we probably don't 16548c2ecf20Sopenharmony_ci * need to zero it, but better safe than sorry for now. 16558c2ecf20Sopenharmony_ci */ 16568c2ecf20Sopenharmony_ci memset(&entry->descriptor, 0, sizeof(entry->descriptor)); 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci /* Fill out source part */ 16598c2ecf20Sopenharmony_ci switch (src->type) { 16608c2ecf20Sopenharmony_ci case VME_DMA_PATTERN: 16618c2ecf20Sopenharmony_ci pattern_attr = src->private; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci entry->descriptor.dsal = cpu_to_be32(pattern_attr->pattern); 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci val = TSI148_LCSR_DSAT_TYP_PAT; 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci /* Default behaviour is 32 bit pattern */ 16688c2ecf20Sopenharmony_ci if (pattern_attr->type & VME_DMA_PATTERN_BYTE) 16698c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_PSZ; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci /* It seems that the default behaviour is to increment */ 16728c2ecf20Sopenharmony_ci if ((pattern_attr->type & VME_DMA_PATTERN_INCREMENT) == 0) 16738c2ecf20Sopenharmony_ci val |= TSI148_LCSR_DSAT_NIN; 16748c2ecf20Sopenharmony_ci entry->descriptor.dsat = cpu_to_be32(val); 16758c2ecf20Sopenharmony_ci break; 16768c2ecf20Sopenharmony_ci case VME_DMA_PCI: 16778c2ecf20Sopenharmony_ci pci_attr = src->private; 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci reg_split((unsigned long long)pci_attr->address, &address_high, 16808c2ecf20Sopenharmony_ci &address_low); 16818c2ecf20Sopenharmony_ci entry->descriptor.dsau = cpu_to_be32(address_high); 16828c2ecf20Sopenharmony_ci entry->descriptor.dsal = cpu_to_be32(address_low); 16838c2ecf20Sopenharmony_ci entry->descriptor.dsat = cpu_to_be32(TSI148_LCSR_DSAT_TYP_PCI); 16848c2ecf20Sopenharmony_ci break; 16858c2ecf20Sopenharmony_ci case VME_DMA_VME: 16868c2ecf20Sopenharmony_ci vme_attr = src->private; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci reg_split((unsigned long long)vme_attr->address, &address_high, 16898c2ecf20Sopenharmony_ci &address_low); 16908c2ecf20Sopenharmony_ci entry->descriptor.dsau = cpu_to_be32(address_high); 16918c2ecf20Sopenharmony_ci entry->descriptor.dsal = cpu_to_be32(address_low); 16928c2ecf20Sopenharmony_ci entry->descriptor.dsat = cpu_to_be32(TSI148_LCSR_DSAT_TYP_VME); 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci retval = tsi148_dma_set_vme_src_attributes( 16958c2ecf20Sopenharmony_ci tsi148_bridge->parent, &entry->descriptor.dsat, 16968c2ecf20Sopenharmony_ci vme_attr->aspace, vme_attr->cycle, vme_attr->dwidth); 16978c2ecf20Sopenharmony_ci if (retval < 0) 16988c2ecf20Sopenharmony_ci goto err_source; 16998c2ecf20Sopenharmony_ci break; 17008c2ecf20Sopenharmony_ci default: 17018c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Invalid source type\n"); 17028c2ecf20Sopenharmony_ci retval = -EINVAL; 17038c2ecf20Sopenharmony_ci goto err_source; 17048c2ecf20Sopenharmony_ci break; 17058c2ecf20Sopenharmony_ci } 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci /* Assume last link - this will be over-written by adding another */ 17088c2ecf20Sopenharmony_ci entry->descriptor.dnlau = cpu_to_be32(0); 17098c2ecf20Sopenharmony_ci entry->descriptor.dnlal = cpu_to_be32(TSI148_LCSR_DNLAL_LLA); 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci /* Fill out destination part */ 17128c2ecf20Sopenharmony_ci switch (dest->type) { 17138c2ecf20Sopenharmony_ci case VME_DMA_PCI: 17148c2ecf20Sopenharmony_ci pci_attr = dest->private; 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci reg_split((unsigned long long)pci_attr->address, &address_high, 17178c2ecf20Sopenharmony_ci &address_low); 17188c2ecf20Sopenharmony_ci entry->descriptor.ddau = cpu_to_be32(address_high); 17198c2ecf20Sopenharmony_ci entry->descriptor.ddal = cpu_to_be32(address_low); 17208c2ecf20Sopenharmony_ci entry->descriptor.ddat = cpu_to_be32(TSI148_LCSR_DDAT_TYP_PCI); 17218c2ecf20Sopenharmony_ci break; 17228c2ecf20Sopenharmony_ci case VME_DMA_VME: 17238c2ecf20Sopenharmony_ci vme_attr = dest->private; 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci reg_split((unsigned long long)vme_attr->address, &address_high, 17268c2ecf20Sopenharmony_ci &address_low); 17278c2ecf20Sopenharmony_ci entry->descriptor.ddau = cpu_to_be32(address_high); 17288c2ecf20Sopenharmony_ci entry->descriptor.ddal = cpu_to_be32(address_low); 17298c2ecf20Sopenharmony_ci entry->descriptor.ddat = cpu_to_be32(TSI148_LCSR_DDAT_TYP_VME); 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci retval = tsi148_dma_set_vme_dest_attributes( 17328c2ecf20Sopenharmony_ci tsi148_bridge->parent, &entry->descriptor.ddat, 17338c2ecf20Sopenharmony_ci vme_attr->aspace, vme_attr->cycle, vme_attr->dwidth); 17348c2ecf20Sopenharmony_ci if (retval < 0) 17358c2ecf20Sopenharmony_ci goto err_dest; 17368c2ecf20Sopenharmony_ci break; 17378c2ecf20Sopenharmony_ci default: 17388c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Invalid destination type\n"); 17398c2ecf20Sopenharmony_ci retval = -EINVAL; 17408c2ecf20Sopenharmony_ci goto err_dest; 17418c2ecf20Sopenharmony_ci break; 17428c2ecf20Sopenharmony_ci } 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci /* Fill out count */ 17458c2ecf20Sopenharmony_ci entry->descriptor.dcnt = cpu_to_be32((u32)count); 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci /* Add to list */ 17488c2ecf20Sopenharmony_ci list_add_tail(&entry->list, &list->entries); 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci entry->dma_handle = dma_map_single(tsi148_bridge->parent, 17518c2ecf20Sopenharmony_ci &entry->descriptor, 17528c2ecf20Sopenharmony_ci sizeof(entry->descriptor), 17538c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 17548c2ecf20Sopenharmony_ci if (dma_mapping_error(tsi148_bridge->parent, entry->dma_handle)) { 17558c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "DMA mapping error\n"); 17568c2ecf20Sopenharmony_ci retval = -EINVAL; 17578c2ecf20Sopenharmony_ci goto err_dma; 17588c2ecf20Sopenharmony_ci } 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci /* Fill out previous descriptors "Next Address" */ 17618c2ecf20Sopenharmony_ci if (entry->list.prev != &list->entries) { 17628c2ecf20Sopenharmony_ci reg_split((unsigned long long)entry->dma_handle, &address_high, 17638c2ecf20Sopenharmony_ci &address_low); 17648c2ecf20Sopenharmony_ci prev = list_entry(entry->list.prev, struct tsi148_dma_entry, 17658c2ecf20Sopenharmony_ci list); 17668c2ecf20Sopenharmony_ci prev->descriptor.dnlau = cpu_to_be32(address_high); 17678c2ecf20Sopenharmony_ci prev->descriptor.dnlal = cpu_to_be32(address_low); 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci } 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci return 0; 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_cierr_dma: 17748c2ecf20Sopenharmony_ci list_del(&entry->list); 17758c2ecf20Sopenharmony_cierr_dest: 17768c2ecf20Sopenharmony_cierr_source: 17778c2ecf20Sopenharmony_cierr_align: 17788c2ecf20Sopenharmony_ci kfree(entry); 17798c2ecf20Sopenharmony_cierr_mem: 17808c2ecf20Sopenharmony_ci return retval; 17818c2ecf20Sopenharmony_ci} 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci/* 17848c2ecf20Sopenharmony_ci * Check to see if the provided DMA channel is busy. 17858c2ecf20Sopenharmony_ci */ 17868c2ecf20Sopenharmony_cistatic int tsi148_dma_busy(struct vme_bridge *tsi148_bridge, int channel) 17878c2ecf20Sopenharmony_ci{ 17888c2ecf20Sopenharmony_ci u32 tmp; 17898c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci tmp = ioread32be(bridge->base + TSI148_LCSR_DMA[channel] + 17948c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_DSTA); 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci if (tmp & TSI148_LCSR_DSTA_BSY) 17978c2ecf20Sopenharmony_ci return 0; 17988c2ecf20Sopenharmony_ci else 17998c2ecf20Sopenharmony_ci return 1; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci} 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci/* 18048c2ecf20Sopenharmony_ci * Execute a previously generated link list 18058c2ecf20Sopenharmony_ci * 18068c2ecf20Sopenharmony_ci * XXX Need to provide control register configuration. 18078c2ecf20Sopenharmony_ci */ 18088c2ecf20Sopenharmony_cistatic int tsi148_dma_list_exec(struct vme_dma_list *list) 18098c2ecf20Sopenharmony_ci{ 18108c2ecf20Sopenharmony_ci struct vme_dma_resource *ctrlr; 18118c2ecf20Sopenharmony_ci int channel, retval; 18128c2ecf20Sopenharmony_ci struct tsi148_dma_entry *entry; 18138c2ecf20Sopenharmony_ci u32 bus_addr_high, bus_addr_low; 18148c2ecf20Sopenharmony_ci u32 val, dctlreg = 0; 18158c2ecf20Sopenharmony_ci struct vme_bridge *tsi148_bridge; 18168c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci ctrlr = list->parent; 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci tsi148_bridge = ctrlr->parent; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci mutex_lock(&ctrlr->mtx); 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci channel = ctrlr->number; 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci if (!list_empty(&ctrlr->running)) { 18298c2ecf20Sopenharmony_ci /* 18308c2ecf20Sopenharmony_ci * XXX We have an active DMA transfer and currently haven't 18318c2ecf20Sopenharmony_ci * sorted out the mechanism for "pending" DMA transfers. 18328c2ecf20Sopenharmony_ci * Return busy. 18338c2ecf20Sopenharmony_ci */ 18348c2ecf20Sopenharmony_ci /* Need to add to pending here */ 18358c2ecf20Sopenharmony_ci mutex_unlock(&ctrlr->mtx); 18368c2ecf20Sopenharmony_ci return -EBUSY; 18378c2ecf20Sopenharmony_ci } else { 18388c2ecf20Sopenharmony_ci list_add(&list->list, &ctrlr->running); 18398c2ecf20Sopenharmony_ci } 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci /* Get first bus address and write into registers */ 18428c2ecf20Sopenharmony_ci entry = list_first_entry(&list->entries, struct tsi148_dma_entry, 18438c2ecf20Sopenharmony_ci list); 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci mutex_unlock(&ctrlr->mtx); 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci reg_split(entry->dma_handle, &bus_addr_high, &bus_addr_low); 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci iowrite32be(bus_addr_high, bridge->base + 18508c2ecf20Sopenharmony_ci TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DNLAU); 18518c2ecf20Sopenharmony_ci iowrite32be(bus_addr_low, bridge->base + 18528c2ecf20Sopenharmony_ci TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DNLAL); 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci dctlreg = ioread32be(bridge->base + TSI148_LCSR_DMA[channel] + 18558c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_DCTL); 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci /* Start the operation */ 18588c2ecf20Sopenharmony_ci iowrite32be(dctlreg | TSI148_LCSR_DCTL_DGO, bridge->base + 18598c2ecf20Sopenharmony_ci TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DCTL); 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci retval = wait_event_interruptible(bridge->dma_queue[channel], 18628c2ecf20Sopenharmony_ci tsi148_dma_busy(ctrlr->parent, channel)); 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci if (retval) { 18658c2ecf20Sopenharmony_ci iowrite32be(dctlreg | TSI148_LCSR_DCTL_ABT, bridge->base + 18668c2ecf20Sopenharmony_ci TSI148_LCSR_DMA[channel] + TSI148_LCSR_OFFSET_DCTL); 18678c2ecf20Sopenharmony_ci /* Wait for the operation to abort */ 18688c2ecf20Sopenharmony_ci wait_event(bridge->dma_queue[channel], 18698c2ecf20Sopenharmony_ci tsi148_dma_busy(ctrlr->parent, channel)); 18708c2ecf20Sopenharmony_ci retval = -EINTR; 18718c2ecf20Sopenharmony_ci goto exit; 18728c2ecf20Sopenharmony_ci } 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci /* 18758c2ecf20Sopenharmony_ci * Read status register, this register is valid until we kick off a 18768c2ecf20Sopenharmony_ci * new transfer. 18778c2ecf20Sopenharmony_ci */ 18788c2ecf20Sopenharmony_ci val = ioread32be(bridge->base + TSI148_LCSR_DMA[channel] + 18798c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_DSTA); 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci if (val & TSI148_LCSR_DSTA_VBE) { 18828c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "DMA Error. DSTA=%08X\n", val); 18838c2ecf20Sopenharmony_ci retval = -EIO; 18848c2ecf20Sopenharmony_ci } 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ciexit: 18878c2ecf20Sopenharmony_ci /* Remove list from running list */ 18888c2ecf20Sopenharmony_ci mutex_lock(&ctrlr->mtx); 18898c2ecf20Sopenharmony_ci list_del(&list->list); 18908c2ecf20Sopenharmony_ci mutex_unlock(&ctrlr->mtx); 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci return retval; 18938c2ecf20Sopenharmony_ci} 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci/* 18968c2ecf20Sopenharmony_ci * Clean up a previously generated link list 18978c2ecf20Sopenharmony_ci * 18988c2ecf20Sopenharmony_ci * We have a separate function, don't assume that the chain can't be reused. 18998c2ecf20Sopenharmony_ci */ 19008c2ecf20Sopenharmony_cistatic int tsi148_dma_list_empty(struct vme_dma_list *list) 19018c2ecf20Sopenharmony_ci{ 19028c2ecf20Sopenharmony_ci struct list_head *pos, *temp; 19038c2ecf20Sopenharmony_ci struct tsi148_dma_entry *entry; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci struct vme_bridge *tsi148_bridge = list->parent->parent; 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci /* detach and free each entry */ 19088c2ecf20Sopenharmony_ci list_for_each_safe(pos, temp, &list->entries) { 19098c2ecf20Sopenharmony_ci list_del(pos); 19108c2ecf20Sopenharmony_ci entry = list_entry(pos, struct tsi148_dma_entry, list); 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci dma_unmap_single(tsi148_bridge->parent, entry->dma_handle, 19138c2ecf20Sopenharmony_ci sizeof(struct tsi148_dma_descriptor), DMA_TO_DEVICE); 19148c2ecf20Sopenharmony_ci kfree(entry); 19158c2ecf20Sopenharmony_ci } 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci return 0; 19188c2ecf20Sopenharmony_ci} 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci/* 19218c2ecf20Sopenharmony_ci * All 4 location monitors reside at the same base - this is therefore a 19228c2ecf20Sopenharmony_ci * system wide configuration. 19238c2ecf20Sopenharmony_ci * 19248c2ecf20Sopenharmony_ci * This does not enable the LM monitor - that should be done when the first 19258c2ecf20Sopenharmony_ci * callback is attached and disabled when the last callback is removed. 19268c2ecf20Sopenharmony_ci */ 19278c2ecf20Sopenharmony_cistatic int tsi148_lm_set(struct vme_lm_resource *lm, unsigned long long lm_base, 19288c2ecf20Sopenharmony_ci u32 aspace, u32 cycle) 19298c2ecf20Sopenharmony_ci{ 19308c2ecf20Sopenharmony_ci u32 lm_base_high, lm_base_low, lm_ctl = 0; 19318c2ecf20Sopenharmony_ci int i; 19328c2ecf20Sopenharmony_ci struct vme_bridge *tsi148_bridge; 19338c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci tsi148_bridge = lm->parent; 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci mutex_lock(&lm->mtx); 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci /* If we already have a callback attached, we can't move it! */ 19428c2ecf20Sopenharmony_ci for (i = 0; i < lm->monitors; i++) { 19438c2ecf20Sopenharmony_ci if (bridge->lm_callback[i]) { 19448c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 19458c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Location monitor " 19468c2ecf20Sopenharmony_ci "callback attached, can't reset\n"); 19478c2ecf20Sopenharmony_ci return -EBUSY; 19488c2ecf20Sopenharmony_ci } 19498c2ecf20Sopenharmony_ci } 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci switch (aspace) { 19528c2ecf20Sopenharmony_ci case VME_A16: 19538c2ecf20Sopenharmony_ci lm_ctl |= TSI148_LCSR_LMAT_AS_A16; 19548c2ecf20Sopenharmony_ci break; 19558c2ecf20Sopenharmony_ci case VME_A24: 19568c2ecf20Sopenharmony_ci lm_ctl |= TSI148_LCSR_LMAT_AS_A24; 19578c2ecf20Sopenharmony_ci break; 19588c2ecf20Sopenharmony_ci case VME_A32: 19598c2ecf20Sopenharmony_ci lm_ctl |= TSI148_LCSR_LMAT_AS_A32; 19608c2ecf20Sopenharmony_ci break; 19618c2ecf20Sopenharmony_ci case VME_A64: 19628c2ecf20Sopenharmony_ci lm_ctl |= TSI148_LCSR_LMAT_AS_A64; 19638c2ecf20Sopenharmony_ci break; 19648c2ecf20Sopenharmony_ci default: 19658c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 19668c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Invalid address space\n"); 19678c2ecf20Sopenharmony_ci return -EINVAL; 19688c2ecf20Sopenharmony_ci break; 19698c2ecf20Sopenharmony_ci } 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci if (cycle & VME_SUPER) 19728c2ecf20Sopenharmony_ci lm_ctl |= TSI148_LCSR_LMAT_SUPR ; 19738c2ecf20Sopenharmony_ci if (cycle & VME_USER) 19748c2ecf20Sopenharmony_ci lm_ctl |= TSI148_LCSR_LMAT_NPRIV; 19758c2ecf20Sopenharmony_ci if (cycle & VME_PROG) 19768c2ecf20Sopenharmony_ci lm_ctl |= TSI148_LCSR_LMAT_PGM; 19778c2ecf20Sopenharmony_ci if (cycle & VME_DATA) 19788c2ecf20Sopenharmony_ci lm_ctl |= TSI148_LCSR_LMAT_DATA; 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci reg_split(lm_base, &lm_base_high, &lm_base_low); 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci iowrite32be(lm_base_high, bridge->base + TSI148_LCSR_LMBAU); 19838c2ecf20Sopenharmony_ci iowrite32be(lm_base_low, bridge->base + TSI148_LCSR_LMBAL); 19848c2ecf20Sopenharmony_ci iowrite32be(lm_ctl, bridge->base + TSI148_LCSR_LMAT); 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci return 0; 19898c2ecf20Sopenharmony_ci} 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci/* Get configuration of the callback monitor and return whether it is enabled 19928c2ecf20Sopenharmony_ci * or disabled. 19938c2ecf20Sopenharmony_ci */ 19948c2ecf20Sopenharmony_cistatic int tsi148_lm_get(struct vme_lm_resource *lm, 19958c2ecf20Sopenharmony_ci unsigned long long *lm_base, u32 *aspace, u32 *cycle) 19968c2ecf20Sopenharmony_ci{ 19978c2ecf20Sopenharmony_ci u32 lm_base_high, lm_base_low, lm_ctl, enabled = 0; 19988c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci bridge = lm->parent->driver_priv; 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci mutex_lock(&lm->mtx); 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci lm_base_high = ioread32be(bridge->base + TSI148_LCSR_LMBAU); 20058c2ecf20Sopenharmony_ci lm_base_low = ioread32be(bridge->base + TSI148_LCSR_LMBAL); 20068c2ecf20Sopenharmony_ci lm_ctl = ioread32be(bridge->base + TSI148_LCSR_LMAT); 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci reg_join(lm_base_high, lm_base_low, lm_base); 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci if (lm_ctl & TSI148_LCSR_LMAT_EN) 20118c2ecf20Sopenharmony_ci enabled = 1; 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_ci if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) == TSI148_LCSR_LMAT_AS_A16) 20148c2ecf20Sopenharmony_ci *aspace |= VME_A16; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) == TSI148_LCSR_LMAT_AS_A24) 20178c2ecf20Sopenharmony_ci *aspace |= VME_A24; 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) == TSI148_LCSR_LMAT_AS_A32) 20208c2ecf20Sopenharmony_ci *aspace |= VME_A32; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci if ((lm_ctl & TSI148_LCSR_LMAT_AS_M) == TSI148_LCSR_LMAT_AS_A64) 20238c2ecf20Sopenharmony_ci *aspace |= VME_A64; 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci if (lm_ctl & TSI148_LCSR_LMAT_SUPR) 20278c2ecf20Sopenharmony_ci *cycle |= VME_SUPER; 20288c2ecf20Sopenharmony_ci if (lm_ctl & TSI148_LCSR_LMAT_NPRIV) 20298c2ecf20Sopenharmony_ci *cycle |= VME_USER; 20308c2ecf20Sopenharmony_ci if (lm_ctl & TSI148_LCSR_LMAT_PGM) 20318c2ecf20Sopenharmony_ci *cycle |= VME_PROG; 20328c2ecf20Sopenharmony_ci if (lm_ctl & TSI148_LCSR_LMAT_DATA) 20338c2ecf20Sopenharmony_ci *cycle |= VME_DATA; 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci return enabled; 20388c2ecf20Sopenharmony_ci} 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci/* 20418c2ecf20Sopenharmony_ci * Attach a callback to a specific location monitor. 20428c2ecf20Sopenharmony_ci * 20438c2ecf20Sopenharmony_ci * Callback will be passed the monitor triggered. 20448c2ecf20Sopenharmony_ci */ 20458c2ecf20Sopenharmony_cistatic int tsi148_lm_attach(struct vme_lm_resource *lm, int monitor, 20468c2ecf20Sopenharmony_ci void (*callback)(void *), void *data) 20478c2ecf20Sopenharmony_ci{ 20488c2ecf20Sopenharmony_ci u32 lm_ctl, tmp; 20498c2ecf20Sopenharmony_ci struct vme_bridge *tsi148_bridge; 20508c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci tsi148_bridge = lm->parent; 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci mutex_lock(&lm->mtx); 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci /* Ensure that the location monitor is configured - need PGM or DATA */ 20598c2ecf20Sopenharmony_ci lm_ctl = ioread32be(bridge->base + TSI148_LCSR_LMAT); 20608c2ecf20Sopenharmony_ci if ((lm_ctl & (TSI148_LCSR_LMAT_PGM | TSI148_LCSR_LMAT_DATA)) == 0) { 20618c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 20628c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Location monitor not properly " 20638c2ecf20Sopenharmony_ci "configured\n"); 20648c2ecf20Sopenharmony_ci return -EINVAL; 20658c2ecf20Sopenharmony_ci } 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci /* Check that a callback isn't already attached */ 20688c2ecf20Sopenharmony_ci if (bridge->lm_callback[monitor]) { 20698c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 20708c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Existing callback attached\n"); 20718c2ecf20Sopenharmony_ci return -EBUSY; 20728c2ecf20Sopenharmony_ci } 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci /* Attach callback */ 20758c2ecf20Sopenharmony_ci bridge->lm_callback[monitor] = callback; 20768c2ecf20Sopenharmony_ci bridge->lm_data[monitor] = data; 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci /* Enable Location Monitor interrupt */ 20798c2ecf20Sopenharmony_ci tmp = ioread32be(bridge->base + TSI148_LCSR_INTEN); 20808c2ecf20Sopenharmony_ci tmp |= TSI148_LCSR_INTEN_LMEN[monitor]; 20818c2ecf20Sopenharmony_ci iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEN); 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci tmp = ioread32be(bridge->base + TSI148_LCSR_INTEO); 20848c2ecf20Sopenharmony_ci tmp |= TSI148_LCSR_INTEO_LMEO[monitor]; 20858c2ecf20Sopenharmony_ci iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO); 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci /* Ensure that global Location Monitor Enable set */ 20888c2ecf20Sopenharmony_ci if ((lm_ctl & TSI148_LCSR_LMAT_EN) == 0) { 20898c2ecf20Sopenharmony_ci lm_ctl |= TSI148_LCSR_LMAT_EN; 20908c2ecf20Sopenharmony_ci iowrite32be(lm_ctl, bridge->base + TSI148_LCSR_LMAT); 20918c2ecf20Sopenharmony_ci } 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci return 0; 20968c2ecf20Sopenharmony_ci} 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci/* 20998c2ecf20Sopenharmony_ci * Detach a callback function forn a specific location monitor. 21008c2ecf20Sopenharmony_ci */ 21018c2ecf20Sopenharmony_cistatic int tsi148_lm_detach(struct vme_lm_resource *lm, int monitor) 21028c2ecf20Sopenharmony_ci{ 21038c2ecf20Sopenharmony_ci u32 lm_en, tmp; 21048c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci bridge = lm->parent->driver_priv; 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci mutex_lock(&lm->mtx); 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci /* Disable Location Monitor and ensure previous interrupts are clear */ 21118c2ecf20Sopenharmony_ci lm_en = ioread32be(bridge->base + TSI148_LCSR_INTEN); 21128c2ecf20Sopenharmony_ci lm_en &= ~TSI148_LCSR_INTEN_LMEN[monitor]; 21138c2ecf20Sopenharmony_ci iowrite32be(lm_en, bridge->base + TSI148_LCSR_INTEN); 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci tmp = ioread32be(bridge->base + TSI148_LCSR_INTEO); 21168c2ecf20Sopenharmony_ci tmp &= ~TSI148_LCSR_INTEO_LMEO[monitor]; 21178c2ecf20Sopenharmony_ci iowrite32be(tmp, bridge->base + TSI148_LCSR_INTEO); 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_ci iowrite32be(TSI148_LCSR_INTC_LMC[monitor], 21208c2ecf20Sopenharmony_ci bridge->base + TSI148_LCSR_INTC); 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci /* Detach callback */ 21238c2ecf20Sopenharmony_ci bridge->lm_callback[monitor] = NULL; 21248c2ecf20Sopenharmony_ci bridge->lm_data[monitor] = NULL; 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci /* If all location monitors disabled, disable global Location Monitor */ 21278c2ecf20Sopenharmony_ci if ((lm_en & (TSI148_LCSR_INTS_LM0S | TSI148_LCSR_INTS_LM1S | 21288c2ecf20Sopenharmony_ci TSI148_LCSR_INTS_LM2S | TSI148_LCSR_INTS_LM3S)) == 0) { 21298c2ecf20Sopenharmony_ci tmp = ioread32be(bridge->base + TSI148_LCSR_LMAT); 21308c2ecf20Sopenharmony_ci tmp &= ~TSI148_LCSR_LMAT_EN; 21318c2ecf20Sopenharmony_ci iowrite32be(tmp, bridge->base + TSI148_LCSR_LMAT); 21328c2ecf20Sopenharmony_ci } 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci mutex_unlock(&lm->mtx); 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci return 0; 21378c2ecf20Sopenharmony_ci} 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci/* 21408c2ecf20Sopenharmony_ci * Determine Geographical Addressing 21418c2ecf20Sopenharmony_ci */ 21428c2ecf20Sopenharmony_cistatic int tsi148_slot_get(struct vme_bridge *tsi148_bridge) 21438c2ecf20Sopenharmony_ci{ 21448c2ecf20Sopenharmony_ci u32 slot = 0; 21458c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci if (!geoid) { 21508c2ecf20Sopenharmony_ci slot = ioread32be(bridge->base + TSI148_LCSR_VSTAT); 21518c2ecf20Sopenharmony_ci slot = slot & TSI148_LCSR_VSTAT_GA_M; 21528c2ecf20Sopenharmony_ci } else 21538c2ecf20Sopenharmony_ci slot = geoid; 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci return (int)slot; 21568c2ecf20Sopenharmony_ci} 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_cistatic void *tsi148_alloc_consistent(struct device *parent, size_t size, 21598c2ecf20Sopenharmony_ci dma_addr_t *dma) 21608c2ecf20Sopenharmony_ci{ 21618c2ecf20Sopenharmony_ci struct pci_dev *pdev; 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci /* Find pci_dev container of dev */ 21648c2ecf20Sopenharmony_ci pdev = to_pci_dev(parent); 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci return pci_alloc_consistent(pdev, size, dma); 21678c2ecf20Sopenharmony_ci} 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_cistatic void tsi148_free_consistent(struct device *parent, size_t size, 21708c2ecf20Sopenharmony_ci void *vaddr, dma_addr_t dma) 21718c2ecf20Sopenharmony_ci{ 21728c2ecf20Sopenharmony_ci struct pci_dev *pdev; 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci /* Find pci_dev container of dev */ 21758c2ecf20Sopenharmony_ci pdev = to_pci_dev(parent); 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci pci_free_consistent(pdev, size, vaddr, dma); 21788c2ecf20Sopenharmony_ci} 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci/* 21818c2ecf20Sopenharmony_ci * Configure CR/CSR space 21828c2ecf20Sopenharmony_ci * 21838c2ecf20Sopenharmony_ci * Access to the CR/CSR can be configured at power-up. The location of the 21848c2ecf20Sopenharmony_ci * CR/CSR registers in the CR/CSR address space is determined by the boards 21858c2ecf20Sopenharmony_ci * Auto-ID or Geographic address. This function ensures that the window is 21868c2ecf20Sopenharmony_ci * enabled at an offset consistent with the boards geopgraphic address. 21878c2ecf20Sopenharmony_ci * 21888c2ecf20Sopenharmony_ci * Each board has a 512kB window, with the highest 4kB being used for the 21898c2ecf20Sopenharmony_ci * boards registers, this means there is a fix length 508kB window which must 21908c2ecf20Sopenharmony_ci * be mapped onto PCI memory. 21918c2ecf20Sopenharmony_ci */ 21928c2ecf20Sopenharmony_cistatic int tsi148_crcsr_init(struct vme_bridge *tsi148_bridge, 21938c2ecf20Sopenharmony_ci struct pci_dev *pdev) 21948c2ecf20Sopenharmony_ci{ 21958c2ecf20Sopenharmony_ci u32 cbar, crat, vstat; 21968c2ecf20Sopenharmony_ci u32 crcsr_bus_high, crcsr_bus_low; 21978c2ecf20Sopenharmony_ci int retval; 21988c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci /* Allocate mem for CR/CSR image */ 22038c2ecf20Sopenharmony_ci bridge->crcsr_kernel = pci_zalloc_consistent(pdev, VME_CRCSR_BUF_SIZE, 22048c2ecf20Sopenharmony_ci &bridge->crcsr_bus); 22058c2ecf20Sopenharmony_ci if (!bridge->crcsr_kernel) { 22068c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Failed to allocate memory for " 22078c2ecf20Sopenharmony_ci "CR/CSR image\n"); 22088c2ecf20Sopenharmony_ci return -ENOMEM; 22098c2ecf20Sopenharmony_ci } 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci reg_split(bridge->crcsr_bus, &crcsr_bus_high, &crcsr_bus_low); 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci iowrite32be(crcsr_bus_high, bridge->base + TSI148_LCSR_CROU); 22148c2ecf20Sopenharmony_ci iowrite32be(crcsr_bus_low, bridge->base + TSI148_LCSR_CROL); 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci /* Ensure that the CR/CSR is configured at the correct offset */ 22178c2ecf20Sopenharmony_ci cbar = ioread32be(bridge->base + TSI148_CBAR); 22188c2ecf20Sopenharmony_ci cbar = (cbar & TSI148_CRCSR_CBAR_M)>>3; 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci vstat = tsi148_slot_get(tsi148_bridge); 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci if (cbar != vstat) { 22238c2ecf20Sopenharmony_ci cbar = vstat; 22248c2ecf20Sopenharmony_ci dev_info(tsi148_bridge->parent, "Setting CR/CSR offset\n"); 22258c2ecf20Sopenharmony_ci iowrite32be(cbar<<3, bridge->base + TSI148_CBAR); 22268c2ecf20Sopenharmony_ci } 22278c2ecf20Sopenharmony_ci dev_info(tsi148_bridge->parent, "CR/CSR Offset: %d\n", cbar); 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci crat = ioread32be(bridge->base + TSI148_LCSR_CRAT); 22308c2ecf20Sopenharmony_ci if (crat & TSI148_LCSR_CRAT_EN) 22318c2ecf20Sopenharmony_ci dev_info(tsi148_bridge->parent, "CR/CSR already enabled\n"); 22328c2ecf20Sopenharmony_ci else { 22338c2ecf20Sopenharmony_ci dev_info(tsi148_bridge->parent, "Enabling CR/CSR space\n"); 22348c2ecf20Sopenharmony_ci iowrite32be(crat | TSI148_LCSR_CRAT_EN, 22358c2ecf20Sopenharmony_ci bridge->base + TSI148_LCSR_CRAT); 22368c2ecf20Sopenharmony_ci } 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci /* If we want flushed, error-checked writes, set up a window 22398c2ecf20Sopenharmony_ci * over the CR/CSR registers. We read from here to safely flush 22408c2ecf20Sopenharmony_ci * through VME writes. 22418c2ecf20Sopenharmony_ci */ 22428c2ecf20Sopenharmony_ci if (err_chk) { 22438c2ecf20Sopenharmony_ci retval = tsi148_master_set(bridge->flush_image, 1, 22448c2ecf20Sopenharmony_ci (vstat * 0x80000), 0x80000, VME_CRCSR, VME_SCT, 22458c2ecf20Sopenharmony_ci VME_D16); 22468c2ecf20Sopenharmony_ci if (retval) 22478c2ecf20Sopenharmony_ci dev_err(tsi148_bridge->parent, "Configuring flush image" 22488c2ecf20Sopenharmony_ci " failed\n"); 22498c2ecf20Sopenharmony_ci } 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ci return 0; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci} 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_cistatic void tsi148_crcsr_exit(struct vme_bridge *tsi148_bridge, 22568c2ecf20Sopenharmony_ci struct pci_dev *pdev) 22578c2ecf20Sopenharmony_ci{ 22588c2ecf20Sopenharmony_ci u32 crat; 22598c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci /* Turn off CR/CSR space */ 22648c2ecf20Sopenharmony_ci crat = ioread32be(bridge->base + TSI148_LCSR_CRAT); 22658c2ecf20Sopenharmony_ci iowrite32be(crat & ~TSI148_LCSR_CRAT_EN, 22668c2ecf20Sopenharmony_ci bridge->base + TSI148_LCSR_CRAT); 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci /* Free image */ 22698c2ecf20Sopenharmony_ci iowrite32be(0, bridge->base + TSI148_LCSR_CROU); 22708c2ecf20Sopenharmony_ci iowrite32be(0, bridge->base + TSI148_LCSR_CROL); 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ci pci_free_consistent(pdev, VME_CRCSR_BUF_SIZE, bridge->crcsr_kernel, 22738c2ecf20Sopenharmony_ci bridge->crcsr_bus); 22748c2ecf20Sopenharmony_ci} 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_cistatic int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id) 22778c2ecf20Sopenharmony_ci{ 22788c2ecf20Sopenharmony_ci int retval, i, master_num; 22798c2ecf20Sopenharmony_ci u32 data; 22808c2ecf20Sopenharmony_ci struct list_head *pos = NULL, *n; 22818c2ecf20Sopenharmony_ci struct vme_bridge *tsi148_bridge; 22828c2ecf20Sopenharmony_ci struct tsi148_driver *tsi148_device; 22838c2ecf20Sopenharmony_ci struct vme_master_resource *master_image; 22848c2ecf20Sopenharmony_ci struct vme_slave_resource *slave_image; 22858c2ecf20Sopenharmony_ci struct vme_dma_resource *dma_ctrlr; 22868c2ecf20Sopenharmony_ci struct vme_lm_resource *lm; 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_ci /* If we want to support more than one of each bridge, we need to 22898c2ecf20Sopenharmony_ci * dynamically generate this so we get one per device 22908c2ecf20Sopenharmony_ci */ 22918c2ecf20Sopenharmony_ci tsi148_bridge = kzalloc(sizeof(*tsi148_bridge), GFP_KERNEL); 22928c2ecf20Sopenharmony_ci if (!tsi148_bridge) { 22938c2ecf20Sopenharmony_ci retval = -ENOMEM; 22948c2ecf20Sopenharmony_ci goto err_struct; 22958c2ecf20Sopenharmony_ci } 22968c2ecf20Sopenharmony_ci vme_init_bridge(tsi148_bridge); 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci tsi148_device = kzalloc(sizeof(*tsi148_device), GFP_KERNEL); 22998c2ecf20Sopenharmony_ci if (!tsi148_device) { 23008c2ecf20Sopenharmony_ci retval = -ENOMEM; 23018c2ecf20Sopenharmony_ci goto err_driver; 23028c2ecf20Sopenharmony_ci } 23038c2ecf20Sopenharmony_ci 23048c2ecf20Sopenharmony_ci tsi148_bridge->driver_priv = tsi148_device; 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ci /* Enable the device */ 23078c2ecf20Sopenharmony_ci retval = pci_enable_device(pdev); 23088c2ecf20Sopenharmony_ci if (retval) { 23098c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to enable device\n"); 23108c2ecf20Sopenharmony_ci goto err_enable; 23118c2ecf20Sopenharmony_ci } 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci /* Map Registers */ 23148c2ecf20Sopenharmony_ci retval = pci_request_regions(pdev, driver_name); 23158c2ecf20Sopenharmony_ci if (retval) { 23168c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to reserve resources\n"); 23178c2ecf20Sopenharmony_ci goto err_resource; 23188c2ecf20Sopenharmony_ci } 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci /* map registers in BAR 0 */ 23218c2ecf20Sopenharmony_ci tsi148_device->base = ioremap(pci_resource_start(pdev, 0), 23228c2ecf20Sopenharmony_ci 4096); 23238c2ecf20Sopenharmony_ci if (!tsi148_device->base) { 23248c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to remap CRG region\n"); 23258c2ecf20Sopenharmony_ci retval = -EIO; 23268c2ecf20Sopenharmony_ci goto err_remap; 23278c2ecf20Sopenharmony_ci } 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci /* Check to see if the mapping worked out */ 23308c2ecf20Sopenharmony_ci data = ioread32(tsi148_device->base + TSI148_PCFS_ID) & 0x0000FFFF; 23318c2ecf20Sopenharmony_ci if (data != PCI_VENDOR_ID_TUNDRA) { 23328c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "CRG region check failed\n"); 23338c2ecf20Sopenharmony_ci retval = -EIO; 23348c2ecf20Sopenharmony_ci goto err_test; 23358c2ecf20Sopenharmony_ci } 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci /* Initialize wait queues & mutual exclusion flags */ 23388c2ecf20Sopenharmony_ci init_waitqueue_head(&tsi148_device->dma_queue[0]); 23398c2ecf20Sopenharmony_ci init_waitqueue_head(&tsi148_device->dma_queue[1]); 23408c2ecf20Sopenharmony_ci init_waitqueue_head(&tsi148_device->iack_queue); 23418c2ecf20Sopenharmony_ci mutex_init(&tsi148_device->vme_int); 23428c2ecf20Sopenharmony_ci mutex_init(&tsi148_device->vme_rmw); 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci tsi148_bridge->parent = &pdev->dev; 23458c2ecf20Sopenharmony_ci strcpy(tsi148_bridge->name, driver_name); 23468c2ecf20Sopenharmony_ci 23478c2ecf20Sopenharmony_ci /* Setup IRQ */ 23488c2ecf20Sopenharmony_ci retval = tsi148_irq_init(tsi148_bridge); 23498c2ecf20Sopenharmony_ci if (retval != 0) { 23508c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Chip Initialization failed.\n"); 23518c2ecf20Sopenharmony_ci goto err_irq; 23528c2ecf20Sopenharmony_ci } 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci /* If we are going to flush writes, we need to read from the VME bus. 23558c2ecf20Sopenharmony_ci * We need to do this safely, thus we read the devices own CR/CSR 23568c2ecf20Sopenharmony_ci * register. To do this we must set up a window in CR/CSR space and 23578c2ecf20Sopenharmony_ci * hence have one less master window resource available. 23588c2ecf20Sopenharmony_ci */ 23598c2ecf20Sopenharmony_ci master_num = TSI148_MAX_MASTER; 23608c2ecf20Sopenharmony_ci if (err_chk) { 23618c2ecf20Sopenharmony_ci master_num--; 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_ci tsi148_device->flush_image = 23648c2ecf20Sopenharmony_ci kmalloc(sizeof(*tsi148_device->flush_image), 23658c2ecf20Sopenharmony_ci GFP_KERNEL); 23668c2ecf20Sopenharmony_ci if (!tsi148_device->flush_image) { 23678c2ecf20Sopenharmony_ci retval = -ENOMEM; 23688c2ecf20Sopenharmony_ci goto err_master; 23698c2ecf20Sopenharmony_ci } 23708c2ecf20Sopenharmony_ci tsi148_device->flush_image->parent = tsi148_bridge; 23718c2ecf20Sopenharmony_ci spin_lock_init(&tsi148_device->flush_image->lock); 23728c2ecf20Sopenharmony_ci tsi148_device->flush_image->locked = 1; 23738c2ecf20Sopenharmony_ci tsi148_device->flush_image->number = master_num; 23748c2ecf20Sopenharmony_ci memset(&tsi148_device->flush_image->bus_resource, 0, 23758c2ecf20Sopenharmony_ci sizeof(tsi148_device->flush_image->bus_resource)); 23768c2ecf20Sopenharmony_ci tsi148_device->flush_image->kern_base = NULL; 23778c2ecf20Sopenharmony_ci } 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci /* Add master windows to list */ 23808c2ecf20Sopenharmony_ci for (i = 0; i < master_num; i++) { 23818c2ecf20Sopenharmony_ci master_image = kmalloc(sizeof(*master_image), GFP_KERNEL); 23828c2ecf20Sopenharmony_ci if (!master_image) { 23838c2ecf20Sopenharmony_ci retval = -ENOMEM; 23848c2ecf20Sopenharmony_ci goto err_master; 23858c2ecf20Sopenharmony_ci } 23868c2ecf20Sopenharmony_ci master_image->parent = tsi148_bridge; 23878c2ecf20Sopenharmony_ci spin_lock_init(&master_image->lock); 23888c2ecf20Sopenharmony_ci master_image->locked = 0; 23898c2ecf20Sopenharmony_ci master_image->number = i; 23908c2ecf20Sopenharmony_ci master_image->address_attr = VME_A16 | VME_A24 | VME_A32 | 23918c2ecf20Sopenharmony_ci VME_A64 | VME_CRCSR | VME_USER1 | VME_USER2 | 23928c2ecf20Sopenharmony_ci VME_USER3 | VME_USER4; 23938c2ecf20Sopenharmony_ci master_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT | 23948c2ecf20Sopenharmony_ci VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 | 23958c2ecf20Sopenharmony_ci VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER | 23968c2ecf20Sopenharmony_ci VME_PROG | VME_DATA; 23978c2ecf20Sopenharmony_ci master_image->width_attr = VME_D16 | VME_D32; 23988c2ecf20Sopenharmony_ci memset(&master_image->bus_resource, 0, 23998c2ecf20Sopenharmony_ci sizeof(master_image->bus_resource)); 24008c2ecf20Sopenharmony_ci master_image->kern_base = NULL; 24018c2ecf20Sopenharmony_ci list_add_tail(&master_image->list, 24028c2ecf20Sopenharmony_ci &tsi148_bridge->master_resources); 24038c2ecf20Sopenharmony_ci } 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci /* Add slave windows to list */ 24068c2ecf20Sopenharmony_ci for (i = 0; i < TSI148_MAX_SLAVE; i++) { 24078c2ecf20Sopenharmony_ci slave_image = kmalloc(sizeof(*slave_image), GFP_KERNEL); 24088c2ecf20Sopenharmony_ci if (!slave_image) { 24098c2ecf20Sopenharmony_ci retval = -ENOMEM; 24108c2ecf20Sopenharmony_ci goto err_slave; 24118c2ecf20Sopenharmony_ci } 24128c2ecf20Sopenharmony_ci slave_image->parent = tsi148_bridge; 24138c2ecf20Sopenharmony_ci mutex_init(&slave_image->mtx); 24148c2ecf20Sopenharmony_ci slave_image->locked = 0; 24158c2ecf20Sopenharmony_ci slave_image->number = i; 24168c2ecf20Sopenharmony_ci slave_image->address_attr = VME_A16 | VME_A24 | VME_A32 | 24178c2ecf20Sopenharmony_ci VME_A64; 24188c2ecf20Sopenharmony_ci slave_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT | 24198c2ecf20Sopenharmony_ci VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 | 24208c2ecf20Sopenharmony_ci VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER | 24218c2ecf20Sopenharmony_ci VME_PROG | VME_DATA; 24228c2ecf20Sopenharmony_ci list_add_tail(&slave_image->list, 24238c2ecf20Sopenharmony_ci &tsi148_bridge->slave_resources); 24248c2ecf20Sopenharmony_ci } 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_ci /* Add dma engines to list */ 24278c2ecf20Sopenharmony_ci for (i = 0; i < TSI148_MAX_DMA; i++) { 24288c2ecf20Sopenharmony_ci dma_ctrlr = kmalloc(sizeof(*dma_ctrlr), GFP_KERNEL); 24298c2ecf20Sopenharmony_ci if (!dma_ctrlr) { 24308c2ecf20Sopenharmony_ci retval = -ENOMEM; 24318c2ecf20Sopenharmony_ci goto err_dma; 24328c2ecf20Sopenharmony_ci } 24338c2ecf20Sopenharmony_ci dma_ctrlr->parent = tsi148_bridge; 24348c2ecf20Sopenharmony_ci mutex_init(&dma_ctrlr->mtx); 24358c2ecf20Sopenharmony_ci dma_ctrlr->locked = 0; 24368c2ecf20Sopenharmony_ci dma_ctrlr->number = i; 24378c2ecf20Sopenharmony_ci dma_ctrlr->route_attr = VME_DMA_VME_TO_MEM | 24388c2ecf20Sopenharmony_ci VME_DMA_MEM_TO_VME | VME_DMA_VME_TO_VME | 24398c2ecf20Sopenharmony_ci VME_DMA_MEM_TO_MEM | VME_DMA_PATTERN_TO_VME | 24408c2ecf20Sopenharmony_ci VME_DMA_PATTERN_TO_MEM; 24418c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dma_ctrlr->pending); 24428c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dma_ctrlr->running); 24438c2ecf20Sopenharmony_ci list_add_tail(&dma_ctrlr->list, 24448c2ecf20Sopenharmony_ci &tsi148_bridge->dma_resources); 24458c2ecf20Sopenharmony_ci } 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci /* Add location monitor to list */ 24488c2ecf20Sopenharmony_ci lm = kmalloc(sizeof(*lm), GFP_KERNEL); 24498c2ecf20Sopenharmony_ci if (!lm) { 24508c2ecf20Sopenharmony_ci retval = -ENOMEM; 24518c2ecf20Sopenharmony_ci goto err_lm; 24528c2ecf20Sopenharmony_ci } 24538c2ecf20Sopenharmony_ci lm->parent = tsi148_bridge; 24548c2ecf20Sopenharmony_ci mutex_init(&lm->mtx); 24558c2ecf20Sopenharmony_ci lm->locked = 0; 24568c2ecf20Sopenharmony_ci lm->number = 1; 24578c2ecf20Sopenharmony_ci lm->monitors = 4; 24588c2ecf20Sopenharmony_ci list_add_tail(&lm->list, &tsi148_bridge->lm_resources); 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci tsi148_bridge->slave_get = tsi148_slave_get; 24618c2ecf20Sopenharmony_ci tsi148_bridge->slave_set = tsi148_slave_set; 24628c2ecf20Sopenharmony_ci tsi148_bridge->master_get = tsi148_master_get; 24638c2ecf20Sopenharmony_ci tsi148_bridge->master_set = tsi148_master_set; 24648c2ecf20Sopenharmony_ci tsi148_bridge->master_read = tsi148_master_read; 24658c2ecf20Sopenharmony_ci tsi148_bridge->master_write = tsi148_master_write; 24668c2ecf20Sopenharmony_ci tsi148_bridge->master_rmw = tsi148_master_rmw; 24678c2ecf20Sopenharmony_ci tsi148_bridge->dma_list_add = tsi148_dma_list_add; 24688c2ecf20Sopenharmony_ci tsi148_bridge->dma_list_exec = tsi148_dma_list_exec; 24698c2ecf20Sopenharmony_ci tsi148_bridge->dma_list_empty = tsi148_dma_list_empty; 24708c2ecf20Sopenharmony_ci tsi148_bridge->irq_set = tsi148_irq_set; 24718c2ecf20Sopenharmony_ci tsi148_bridge->irq_generate = tsi148_irq_generate; 24728c2ecf20Sopenharmony_ci tsi148_bridge->lm_set = tsi148_lm_set; 24738c2ecf20Sopenharmony_ci tsi148_bridge->lm_get = tsi148_lm_get; 24748c2ecf20Sopenharmony_ci tsi148_bridge->lm_attach = tsi148_lm_attach; 24758c2ecf20Sopenharmony_ci tsi148_bridge->lm_detach = tsi148_lm_detach; 24768c2ecf20Sopenharmony_ci tsi148_bridge->slot_get = tsi148_slot_get; 24778c2ecf20Sopenharmony_ci tsi148_bridge->alloc_consistent = tsi148_alloc_consistent; 24788c2ecf20Sopenharmony_ci tsi148_bridge->free_consistent = tsi148_free_consistent; 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_ci data = ioread32be(tsi148_device->base + TSI148_LCSR_VSTAT); 24818c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Board is%s the VME system controller\n", 24828c2ecf20Sopenharmony_ci (data & TSI148_LCSR_VSTAT_SCONS) ? "" : " not"); 24838c2ecf20Sopenharmony_ci if (!geoid) 24848c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "VME geographical address is %d\n", 24858c2ecf20Sopenharmony_ci data & TSI148_LCSR_VSTAT_GA_M); 24868c2ecf20Sopenharmony_ci else 24878c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "VME geographical address is set to %d\n", 24888c2ecf20Sopenharmony_ci geoid); 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "VME Write and flush and error check is %s\n", 24918c2ecf20Sopenharmony_ci err_chk ? "enabled" : "disabled"); 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci retval = tsi148_crcsr_init(tsi148_bridge, pdev); 24948c2ecf20Sopenharmony_ci if (retval) { 24958c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "CR/CSR configuration failed.\n"); 24968c2ecf20Sopenharmony_ci goto err_crcsr; 24978c2ecf20Sopenharmony_ci } 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_ci retval = vme_register_bridge(tsi148_bridge); 25008c2ecf20Sopenharmony_ci if (retval != 0) { 25018c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Chip Registration failed.\n"); 25028c2ecf20Sopenharmony_ci goto err_reg; 25038c2ecf20Sopenharmony_ci } 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, tsi148_bridge); 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_ci /* Clear VME bus "board fail", and "power-up reset" lines */ 25088c2ecf20Sopenharmony_ci data = ioread32be(tsi148_device->base + TSI148_LCSR_VSTAT); 25098c2ecf20Sopenharmony_ci data &= ~TSI148_LCSR_VSTAT_BRDFL; 25108c2ecf20Sopenharmony_ci data |= TSI148_LCSR_VSTAT_CPURST; 25118c2ecf20Sopenharmony_ci iowrite32be(data, tsi148_device->base + TSI148_LCSR_VSTAT); 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_ci return 0; 25148c2ecf20Sopenharmony_ci 25158c2ecf20Sopenharmony_cierr_reg: 25168c2ecf20Sopenharmony_ci tsi148_crcsr_exit(tsi148_bridge, pdev); 25178c2ecf20Sopenharmony_cierr_crcsr: 25188c2ecf20Sopenharmony_cierr_lm: 25198c2ecf20Sopenharmony_ci /* resources are stored in link list */ 25208c2ecf20Sopenharmony_ci list_for_each_safe(pos, n, &tsi148_bridge->lm_resources) { 25218c2ecf20Sopenharmony_ci lm = list_entry(pos, struct vme_lm_resource, list); 25228c2ecf20Sopenharmony_ci list_del(pos); 25238c2ecf20Sopenharmony_ci kfree(lm); 25248c2ecf20Sopenharmony_ci } 25258c2ecf20Sopenharmony_cierr_dma: 25268c2ecf20Sopenharmony_ci /* resources are stored in link list */ 25278c2ecf20Sopenharmony_ci list_for_each_safe(pos, n, &tsi148_bridge->dma_resources) { 25288c2ecf20Sopenharmony_ci dma_ctrlr = list_entry(pos, struct vme_dma_resource, list); 25298c2ecf20Sopenharmony_ci list_del(pos); 25308c2ecf20Sopenharmony_ci kfree(dma_ctrlr); 25318c2ecf20Sopenharmony_ci } 25328c2ecf20Sopenharmony_cierr_slave: 25338c2ecf20Sopenharmony_ci /* resources are stored in link list */ 25348c2ecf20Sopenharmony_ci list_for_each_safe(pos, n, &tsi148_bridge->slave_resources) { 25358c2ecf20Sopenharmony_ci slave_image = list_entry(pos, struct vme_slave_resource, list); 25368c2ecf20Sopenharmony_ci list_del(pos); 25378c2ecf20Sopenharmony_ci kfree(slave_image); 25388c2ecf20Sopenharmony_ci } 25398c2ecf20Sopenharmony_cierr_master: 25408c2ecf20Sopenharmony_ci /* resources are stored in link list */ 25418c2ecf20Sopenharmony_ci list_for_each_safe(pos, n, &tsi148_bridge->master_resources) { 25428c2ecf20Sopenharmony_ci master_image = list_entry(pos, struct vme_master_resource, 25438c2ecf20Sopenharmony_ci list); 25448c2ecf20Sopenharmony_ci list_del(pos); 25458c2ecf20Sopenharmony_ci kfree(master_image); 25468c2ecf20Sopenharmony_ci } 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_ci tsi148_irq_exit(tsi148_bridge, pdev); 25498c2ecf20Sopenharmony_cierr_irq: 25508c2ecf20Sopenharmony_cierr_test: 25518c2ecf20Sopenharmony_ci iounmap(tsi148_device->base); 25528c2ecf20Sopenharmony_cierr_remap: 25538c2ecf20Sopenharmony_ci pci_release_regions(pdev); 25548c2ecf20Sopenharmony_cierr_resource: 25558c2ecf20Sopenharmony_ci pci_disable_device(pdev); 25568c2ecf20Sopenharmony_cierr_enable: 25578c2ecf20Sopenharmony_ci kfree(tsi148_device); 25588c2ecf20Sopenharmony_cierr_driver: 25598c2ecf20Sopenharmony_ci kfree(tsi148_bridge); 25608c2ecf20Sopenharmony_cierr_struct: 25618c2ecf20Sopenharmony_ci return retval; 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci} 25648c2ecf20Sopenharmony_ci 25658c2ecf20Sopenharmony_cistatic void tsi148_remove(struct pci_dev *pdev) 25668c2ecf20Sopenharmony_ci{ 25678c2ecf20Sopenharmony_ci struct list_head *pos = NULL; 25688c2ecf20Sopenharmony_ci struct list_head *tmplist; 25698c2ecf20Sopenharmony_ci struct vme_master_resource *master_image; 25708c2ecf20Sopenharmony_ci struct vme_slave_resource *slave_image; 25718c2ecf20Sopenharmony_ci struct vme_dma_resource *dma_ctrlr; 25728c2ecf20Sopenharmony_ci int i; 25738c2ecf20Sopenharmony_ci struct tsi148_driver *bridge; 25748c2ecf20Sopenharmony_ci struct vme_bridge *tsi148_bridge = pci_get_drvdata(pdev); 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_ci bridge = tsi148_bridge->driver_priv; 25778c2ecf20Sopenharmony_ci 25788c2ecf20Sopenharmony_ci 25798c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Driver is being unloaded.\n"); 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_ci /* 25828c2ecf20Sopenharmony_ci * Shutdown all inbound and outbound windows. 25838c2ecf20Sopenharmony_ci */ 25848c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 25858c2ecf20Sopenharmony_ci iowrite32be(0, bridge->base + TSI148_LCSR_IT[i] + 25868c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_ITAT); 25878c2ecf20Sopenharmony_ci iowrite32be(0, bridge->base + TSI148_LCSR_OT[i] + 25888c2ecf20Sopenharmony_ci TSI148_LCSR_OFFSET_OTAT); 25898c2ecf20Sopenharmony_ci } 25908c2ecf20Sopenharmony_ci 25918c2ecf20Sopenharmony_ci /* 25928c2ecf20Sopenharmony_ci * Shutdown Location monitor. 25938c2ecf20Sopenharmony_ci */ 25948c2ecf20Sopenharmony_ci iowrite32be(0, bridge->base + TSI148_LCSR_LMAT); 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci /* 25978c2ecf20Sopenharmony_ci * Shutdown CRG map. 25988c2ecf20Sopenharmony_ci */ 25998c2ecf20Sopenharmony_ci iowrite32be(0, bridge->base + TSI148_LCSR_CSRAT); 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci /* 26028c2ecf20Sopenharmony_ci * Clear error status. 26038c2ecf20Sopenharmony_ci */ 26048c2ecf20Sopenharmony_ci iowrite32be(0xFFFFFFFF, bridge->base + TSI148_LCSR_EDPAT); 26058c2ecf20Sopenharmony_ci iowrite32be(0xFFFFFFFF, bridge->base + TSI148_LCSR_VEAT); 26068c2ecf20Sopenharmony_ci iowrite32be(0x07000700, bridge->base + TSI148_LCSR_PSTAT); 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_ci /* 26098c2ecf20Sopenharmony_ci * Remove VIRQ interrupt (if any) 26108c2ecf20Sopenharmony_ci */ 26118c2ecf20Sopenharmony_ci if (ioread32be(bridge->base + TSI148_LCSR_VICR) & 0x800) 26128c2ecf20Sopenharmony_ci iowrite32be(0x8000, bridge->base + TSI148_LCSR_VICR); 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci /* 26158c2ecf20Sopenharmony_ci * Map all Interrupts to PCI INTA 26168c2ecf20Sopenharmony_ci */ 26178c2ecf20Sopenharmony_ci iowrite32be(0x0, bridge->base + TSI148_LCSR_INTM1); 26188c2ecf20Sopenharmony_ci iowrite32be(0x0, bridge->base + TSI148_LCSR_INTM2); 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci tsi148_irq_exit(tsi148_bridge, pdev); 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci vme_unregister_bridge(tsi148_bridge); 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_ci tsi148_crcsr_exit(tsi148_bridge, pdev); 26258c2ecf20Sopenharmony_ci 26268c2ecf20Sopenharmony_ci /* resources are stored in link list */ 26278c2ecf20Sopenharmony_ci list_for_each_safe(pos, tmplist, &tsi148_bridge->dma_resources) { 26288c2ecf20Sopenharmony_ci dma_ctrlr = list_entry(pos, struct vme_dma_resource, list); 26298c2ecf20Sopenharmony_ci list_del(pos); 26308c2ecf20Sopenharmony_ci kfree(dma_ctrlr); 26318c2ecf20Sopenharmony_ci } 26328c2ecf20Sopenharmony_ci 26338c2ecf20Sopenharmony_ci /* resources are stored in link list */ 26348c2ecf20Sopenharmony_ci list_for_each_safe(pos, tmplist, &tsi148_bridge->slave_resources) { 26358c2ecf20Sopenharmony_ci slave_image = list_entry(pos, struct vme_slave_resource, list); 26368c2ecf20Sopenharmony_ci list_del(pos); 26378c2ecf20Sopenharmony_ci kfree(slave_image); 26388c2ecf20Sopenharmony_ci } 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci /* resources are stored in link list */ 26418c2ecf20Sopenharmony_ci list_for_each_safe(pos, tmplist, &tsi148_bridge->master_resources) { 26428c2ecf20Sopenharmony_ci master_image = list_entry(pos, struct vme_master_resource, 26438c2ecf20Sopenharmony_ci list); 26448c2ecf20Sopenharmony_ci list_del(pos); 26458c2ecf20Sopenharmony_ci kfree(master_image); 26468c2ecf20Sopenharmony_ci } 26478c2ecf20Sopenharmony_ci 26488c2ecf20Sopenharmony_ci iounmap(bridge->base); 26498c2ecf20Sopenharmony_ci 26508c2ecf20Sopenharmony_ci pci_release_regions(pdev); 26518c2ecf20Sopenharmony_ci 26528c2ecf20Sopenharmony_ci pci_disable_device(pdev); 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ci kfree(tsi148_bridge->driver_priv); 26558c2ecf20Sopenharmony_ci 26568c2ecf20Sopenharmony_ci kfree(tsi148_bridge); 26578c2ecf20Sopenharmony_ci} 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_cimodule_pci_driver(tsi148_driver); 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_ciMODULE_PARM_DESC(err_chk, "Check for VME errors on reads and writes"); 26628c2ecf20Sopenharmony_cimodule_param(err_chk, bool, 0); 26638c2ecf20Sopenharmony_ci 26648c2ecf20Sopenharmony_ciMODULE_PARM_DESC(geoid, "Override geographical addressing"); 26658c2ecf20Sopenharmony_cimodule_param(geoid, int, 0); 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("VME driver for the Tundra Tempe VME bridge"); 26688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2669