135ba63b8SArnd Bergmann // SPDX-License-Identifier: GPL-2.0-or-later 235ba63b8SArnd Bergmann /* 335ba63b8SArnd Bergmann * Fake VME bridge support. 435ba63b8SArnd Bergmann * 535ba63b8SArnd Bergmann * This drive provides a fake VME bridge chip, this enables debugging of the 635ba63b8SArnd Bergmann * VME framework in the absence of a VME system. 735ba63b8SArnd Bergmann * 835ba63b8SArnd Bergmann * This driver has to do a number of things in software that would be driven 935ba63b8SArnd Bergmann * by hardware if it was available, it will also result in extra overhead at 1035ba63b8SArnd Bergmann * times when compared with driving actual hardware. 1135ba63b8SArnd Bergmann * 1235ba63b8SArnd Bergmann * Author: Martyn Welch <martyn@welches.me.uk> 1335ba63b8SArnd Bergmann * Copyright (c) 2014 Martyn Welch 1435ba63b8SArnd Bergmann * 1535ba63b8SArnd Bergmann * Based on vme_tsi148.c: 1635ba63b8SArnd Bergmann * 1735ba63b8SArnd Bergmann * Author: Martyn Welch <martyn.welch@ge.com> 1835ba63b8SArnd Bergmann * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. 1935ba63b8SArnd Bergmann * 2035ba63b8SArnd Bergmann * Based on work by Tom Armistead and Ajit Prem 2135ba63b8SArnd Bergmann * Copyright 2004 Motorola Inc. 2235ba63b8SArnd Bergmann */ 2335ba63b8SArnd Bergmann 2435ba63b8SArnd Bergmann #include <linux/device.h> 2535ba63b8SArnd Bergmann #include <linux/errno.h> 2635ba63b8SArnd Bergmann #include <linux/interrupt.h> 2735ba63b8SArnd Bergmann #include <linux/module.h> 2835ba63b8SArnd Bergmann #include <linux/moduleparam.h> 2935ba63b8SArnd Bergmann #include <linux/slab.h> 3035ba63b8SArnd Bergmann #include <linux/spinlock.h> 3135ba63b8SArnd Bergmann #include <linux/types.h> 3235ba63b8SArnd Bergmann 3335ba63b8SArnd Bergmann #include "vme.h" 3435ba63b8SArnd Bergmann #include "vme_bridge.h" 3535ba63b8SArnd Bergmann 3635ba63b8SArnd Bergmann /* 3735ba63b8SArnd Bergmann * Define the number of each that the fake driver supports. 3835ba63b8SArnd Bergmann */ 3935ba63b8SArnd Bergmann #define FAKE_MAX_MASTER 8 /* Max Master Windows */ 4035ba63b8SArnd Bergmann #define FAKE_MAX_SLAVE 8 /* Max Slave Windows */ 4135ba63b8SArnd Bergmann 4235ba63b8SArnd Bergmann /* Structures to hold information normally held in device registers */ 4335ba63b8SArnd Bergmann struct fake_slave_window { 4435ba63b8SArnd Bergmann int enabled; 4535ba63b8SArnd Bergmann unsigned long long vme_base; 4635ba63b8SArnd Bergmann unsigned long long size; 4735ba63b8SArnd Bergmann void *buf_base; 4835ba63b8SArnd Bergmann u32 aspace; 4935ba63b8SArnd Bergmann u32 cycle; 5035ba63b8SArnd Bergmann }; 5135ba63b8SArnd Bergmann 5235ba63b8SArnd Bergmann struct fake_master_window { 5335ba63b8SArnd Bergmann int enabled; 5435ba63b8SArnd Bergmann unsigned long long vme_base; 5535ba63b8SArnd Bergmann unsigned long long size; 5635ba63b8SArnd Bergmann u32 aspace; 5735ba63b8SArnd Bergmann u32 cycle; 5835ba63b8SArnd Bergmann u32 dwidth; 5935ba63b8SArnd Bergmann }; 6035ba63b8SArnd Bergmann 6135ba63b8SArnd Bergmann /* Structure used to hold driver specific information */ 6235ba63b8SArnd Bergmann struct fake_driver { 6335ba63b8SArnd Bergmann struct vme_bridge *parent; 6435ba63b8SArnd Bergmann struct fake_slave_window slaves[FAKE_MAX_SLAVE]; 6535ba63b8SArnd Bergmann struct fake_master_window masters[FAKE_MAX_MASTER]; 6635ba63b8SArnd Bergmann u32 lm_enabled; 6735ba63b8SArnd Bergmann unsigned long long lm_base; 6835ba63b8SArnd Bergmann u32 lm_aspace; 6935ba63b8SArnd Bergmann u32 lm_cycle; 7035ba63b8SArnd Bergmann void (*lm_callback[4])(void *); 7135ba63b8SArnd Bergmann void *lm_data[4]; 7235ba63b8SArnd Bergmann struct tasklet_struct int_tasklet; 7335ba63b8SArnd Bergmann int int_level; 7435ba63b8SArnd Bergmann int int_statid; 7535ba63b8SArnd Bergmann void *crcsr_kernel; 7635ba63b8SArnd Bergmann dma_addr_t crcsr_bus; 7735ba63b8SArnd Bergmann /* Only one VME interrupt can be generated at a time, provide locking */ 7835ba63b8SArnd Bergmann struct mutex vme_int; 7935ba63b8SArnd Bergmann }; 8035ba63b8SArnd Bergmann 8135ba63b8SArnd Bergmann /* Module parameter */ 8235ba63b8SArnd Bergmann static int geoid; 8335ba63b8SArnd Bergmann 8435ba63b8SArnd Bergmann static const char driver_name[] = "vme_fake"; 8535ba63b8SArnd Bergmann 8635ba63b8SArnd Bergmann static struct vme_bridge *exit_pointer; 8735ba63b8SArnd Bergmann 8835ba63b8SArnd Bergmann static struct device *vme_root; 8935ba63b8SArnd Bergmann 9035ba63b8SArnd Bergmann /* 9135ba63b8SArnd Bergmann * Calling VME bus interrupt callback if provided. 9235ba63b8SArnd Bergmann */ 9335ba63b8SArnd Bergmann static void fake_VIRQ_tasklet(unsigned long data) 9435ba63b8SArnd Bergmann { 9535ba63b8SArnd Bergmann struct vme_bridge *fake_bridge; 9635ba63b8SArnd Bergmann struct fake_driver *bridge; 9735ba63b8SArnd Bergmann 9835ba63b8SArnd Bergmann fake_bridge = (struct vme_bridge *) data; 9935ba63b8SArnd Bergmann bridge = fake_bridge->driver_priv; 10035ba63b8SArnd Bergmann 10135ba63b8SArnd Bergmann vme_irq_handler(fake_bridge, bridge->int_level, bridge->int_statid); 10235ba63b8SArnd Bergmann } 10335ba63b8SArnd Bergmann 10435ba63b8SArnd Bergmann /* 10535ba63b8SArnd Bergmann * Configure VME interrupt 10635ba63b8SArnd Bergmann */ 10735ba63b8SArnd Bergmann static void fake_irq_set(struct vme_bridge *fake_bridge, int level, 10835ba63b8SArnd Bergmann int state, int sync) 10935ba63b8SArnd Bergmann { 11035ba63b8SArnd Bergmann /* Nothing to do */ 11135ba63b8SArnd Bergmann } 11235ba63b8SArnd Bergmann 11335ba63b8SArnd Bergmann static void *fake_pci_to_ptr(dma_addr_t addr) 11435ba63b8SArnd Bergmann { 11535ba63b8SArnd Bergmann return (void *)(uintptr_t)addr; 11635ba63b8SArnd Bergmann } 11735ba63b8SArnd Bergmann 11835ba63b8SArnd Bergmann static dma_addr_t fake_ptr_to_pci(void *addr) 11935ba63b8SArnd Bergmann { 12035ba63b8SArnd Bergmann return (dma_addr_t)(uintptr_t)addr; 12135ba63b8SArnd Bergmann } 12235ba63b8SArnd Bergmann 12335ba63b8SArnd Bergmann /* 12435ba63b8SArnd Bergmann * Generate a VME bus interrupt at the requested level & vector. Wait for 12535ba63b8SArnd Bergmann * interrupt to be acked. 12635ba63b8SArnd Bergmann */ 12735ba63b8SArnd Bergmann static int fake_irq_generate(struct vme_bridge *fake_bridge, int level, 12835ba63b8SArnd Bergmann int statid) 12935ba63b8SArnd Bergmann { 13035ba63b8SArnd Bergmann struct fake_driver *bridge; 13135ba63b8SArnd Bergmann 13235ba63b8SArnd Bergmann bridge = fake_bridge->driver_priv; 13335ba63b8SArnd Bergmann 13435ba63b8SArnd Bergmann mutex_lock(&bridge->vme_int); 13535ba63b8SArnd Bergmann 13635ba63b8SArnd Bergmann bridge->int_level = level; 13735ba63b8SArnd Bergmann 13835ba63b8SArnd Bergmann bridge->int_statid = statid; 13935ba63b8SArnd Bergmann 14035ba63b8SArnd Bergmann /* 14135ba63b8SArnd Bergmann * Schedule tasklet to run VME handler to emulate normal VME interrupt 14235ba63b8SArnd Bergmann * handler behaviour. 14335ba63b8SArnd Bergmann */ 14435ba63b8SArnd Bergmann tasklet_schedule(&bridge->int_tasklet); 14535ba63b8SArnd Bergmann 14635ba63b8SArnd Bergmann mutex_unlock(&bridge->vme_int); 14735ba63b8SArnd Bergmann 14835ba63b8SArnd Bergmann return 0; 14935ba63b8SArnd Bergmann } 15035ba63b8SArnd Bergmann 15135ba63b8SArnd Bergmann /* 15235ba63b8SArnd Bergmann * Initialize a slave window with the requested attributes. 15335ba63b8SArnd Bergmann */ 15435ba63b8SArnd Bergmann static int fake_slave_set(struct vme_slave_resource *image, int enabled, 15535ba63b8SArnd Bergmann unsigned long long vme_base, unsigned long long size, 15635ba63b8SArnd Bergmann dma_addr_t buf_base, u32 aspace, u32 cycle) 15735ba63b8SArnd Bergmann { 15835ba63b8SArnd Bergmann unsigned int i, granularity = 0; 15935ba63b8SArnd Bergmann unsigned long long vme_bound; 16035ba63b8SArnd Bergmann struct vme_bridge *fake_bridge; 16135ba63b8SArnd Bergmann struct fake_driver *bridge; 16235ba63b8SArnd Bergmann 16335ba63b8SArnd Bergmann fake_bridge = image->parent; 16435ba63b8SArnd Bergmann bridge = fake_bridge->driver_priv; 16535ba63b8SArnd Bergmann 16635ba63b8SArnd Bergmann i = image->number; 16735ba63b8SArnd Bergmann 16835ba63b8SArnd Bergmann switch (aspace) { 16935ba63b8SArnd Bergmann case VME_A16: 17035ba63b8SArnd Bergmann granularity = 0x10; 17135ba63b8SArnd Bergmann break; 17235ba63b8SArnd Bergmann case VME_A24: 17335ba63b8SArnd Bergmann granularity = 0x1000; 17435ba63b8SArnd Bergmann break; 17535ba63b8SArnd Bergmann case VME_A32: 17635ba63b8SArnd Bergmann granularity = 0x10000; 17735ba63b8SArnd Bergmann break; 17835ba63b8SArnd Bergmann case VME_A64: 17935ba63b8SArnd Bergmann granularity = 0x10000; 18035ba63b8SArnd Bergmann break; 18135ba63b8SArnd Bergmann case VME_CRCSR: 18235ba63b8SArnd Bergmann case VME_USER1: 18335ba63b8SArnd Bergmann case VME_USER2: 18435ba63b8SArnd Bergmann case VME_USER3: 18535ba63b8SArnd Bergmann case VME_USER4: 18635ba63b8SArnd Bergmann default: 18735ba63b8SArnd Bergmann pr_err("Invalid address space\n"); 18835ba63b8SArnd Bergmann return -EINVAL; 18935ba63b8SArnd Bergmann } 19035ba63b8SArnd Bergmann 19135ba63b8SArnd Bergmann /* 19235ba63b8SArnd Bergmann * Bound address is a valid address for the window, adjust 19335ba63b8SArnd Bergmann * accordingly 19435ba63b8SArnd Bergmann */ 19535ba63b8SArnd Bergmann vme_bound = vme_base + size - granularity; 19635ba63b8SArnd Bergmann 19735ba63b8SArnd Bergmann if (vme_base & (granularity - 1)) { 19835ba63b8SArnd Bergmann pr_err("Invalid VME base alignment\n"); 19935ba63b8SArnd Bergmann return -EINVAL; 20035ba63b8SArnd Bergmann } 20135ba63b8SArnd Bergmann if (vme_bound & (granularity - 1)) { 20235ba63b8SArnd Bergmann pr_err("Invalid VME bound alignment\n"); 20335ba63b8SArnd Bergmann return -EINVAL; 20435ba63b8SArnd Bergmann } 20535ba63b8SArnd Bergmann 20635ba63b8SArnd Bergmann mutex_lock(&image->mtx); 20735ba63b8SArnd Bergmann 20835ba63b8SArnd Bergmann bridge->slaves[i].enabled = enabled; 20935ba63b8SArnd Bergmann bridge->slaves[i].vme_base = vme_base; 21035ba63b8SArnd Bergmann bridge->slaves[i].size = size; 21135ba63b8SArnd Bergmann bridge->slaves[i].buf_base = fake_pci_to_ptr(buf_base); 21235ba63b8SArnd Bergmann bridge->slaves[i].aspace = aspace; 21335ba63b8SArnd Bergmann bridge->slaves[i].cycle = cycle; 21435ba63b8SArnd Bergmann 21535ba63b8SArnd Bergmann mutex_unlock(&image->mtx); 21635ba63b8SArnd Bergmann 21735ba63b8SArnd Bergmann return 0; 21835ba63b8SArnd Bergmann } 21935ba63b8SArnd Bergmann 22035ba63b8SArnd Bergmann /* 22135ba63b8SArnd Bergmann * Get slave window configuration. 22235ba63b8SArnd Bergmann */ 22335ba63b8SArnd Bergmann static int fake_slave_get(struct vme_slave_resource *image, int *enabled, 22435ba63b8SArnd Bergmann unsigned long long *vme_base, unsigned long long *size, 22535ba63b8SArnd Bergmann dma_addr_t *buf_base, u32 *aspace, u32 *cycle) 22635ba63b8SArnd Bergmann { 22735ba63b8SArnd Bergmann unsigned int i; 22835ba63b8SArnd Bergmann struct fake_driver *bridge; 22935ba63b8SArnd Bergmann 23035ba63b8SArnd Bergmann bridge = image->parent->driver_priv; 23135ba63b8SArnd Bergmann 23235ba63b8SArnd Bergmann i = image->number; 23335ba63b8SArnd Bergmann 23435ba63b8SArnd Bergmann mutex_lock(&image->mtx); 23535ba63b8SArnd Bergmann 23635ba63b8SArnd Bergmann *enabled = bridge->slaves[i].enabled; 23735ba63b8SArnd Bergmann *vme_base = bridge->slaves[i].vme_base; 23835ba63b8SArnd Bergmann *size = bridge->slaves[i].size; 23935ba63b8SArnd Bergmann *buf_base = fake_ptr_to_pci(bridge->slaves[i].buf_base); 24035ba63b8SArnd Bergmann *aspace = bridge->slaves[i].aspace; 24135ba63b8SArnd Bergmann *cycle = bridge->slaves[i].cycle; 24235ba63b8SArnd Bergmann 24335ba63b8SArnd Bergmann mutex_unlock(&image->mtx); 24435ba63b8SArnd Bergmann 24535ba63b8SArnd Bergmann return 0; 24635ba63b8SArnd Bergmann } 24735ba63b8SArnd Bergmann 24835ba63b8SArnd Bergmann /* 24935ba63b8SArnd Bergmann * Set the attributes of an outbound window. 25035ba63b8SArnd Bergmann */ 25135ba63b8SArnd Bergmann static int fake_master_set(struct vme_master_resource *image, int enabled, 25235ba63b8SArnd Bergmann unsigned long long vme_base, unsigned long long size, 25335ba63b8SArnd Bergmann u32 aspace, u32 cycle, u32 dwidth) 25435ba63b8SArnd Bergmann { 25535ba63b8SArnd Bergmann int retval = 0; 25635ba63b8SArnd Bergmann unsigned int i; 25735ba63b8SArnd Bergmann struct vme_bridge *fake_bridge; 25835ba63b8SArnd Bergmann struct fake_driver *bridge; 25935ba63b8SArnd Bergmann 26035ba63b8SArnd Bergmann fake_bridge = image->parent; 26135ba63b8SArnd Bergmann 26235ba63b8SArnd Bergmann bridge = fake_bridge->driver_priv; 26335ba63b8SArnd Bergmann 26435ba63b8SArnd Bergmann /* Verify input data */ 26535ba63b8SArnd Bergmann if (vme_base & 0xFFFF) { 26635ba63b8SArnd Bergmann pr_err("Invalid VME Window alignment\n"); 26735ba63b8SArnd Bergmann retval = -EINVAL; 26835ba63b8SArnd Bergmann goto err_window; 26935ba63b8SArnd Bergmann } 27035ba63b8SArnd Bergmann 27135ba63b8SArnd Bergmann if (size & 0xFFFF) { 27235ba63b8SArnd Bergmann pr_err("Invalid size alignment\n"); 27335ba63b8SArnd Bergmann retval = -EINVAL; 27435ba63b8SArnd Bergmann goto err_window; 27535ba63b8SArnd Bergmann } 27635ba63b8SArnd Bergmann 27735ba63b8SArnd Bergmann if ((size == 0) && (enabled != 0)) { 27835ba63b8SArnd Bergmann pr_err("Size must be non-zero for enabled windows\n"); 27935ba63b8SArnd Bergmann retval = -EINVAL; 28035ba63b8SArnd Bergmann goto err_window; 28135ba63b8SArnd Bergmann } 28235ba63b8SArnd Bergmann 28335ba63b8SArnd Bergmann /* Setup data width */ 28435ba63b8SArnd Bergmann switch (dwidth) { 28535ba63b8SArnd Bergmann case VME_D8: 28635ba63b8SArnd Bergmann case VME_D16: 28735ba63b8SArnd Bergmann case VME_D32: 28835ba63b8SArnd Bergmann break; 28935ba63b8SArnd Bergmann default: 29035ba63b8SArnd Bergmann pr_err("Invalid data width\n"); 29135ba63b8SArnd Bergmann retval = -EINVAL; 29235ba63b8SArnd Bergmann goto err_dwidth; 29335ba63b8SArnd Bergmann } 29435ba63b8SArnd Bergmann 29535ba63b8SArnd Bergmann /* Setup address space */ 29635ba63b8SArnd Bergmann switch (aspace) { 29735ba63b8SArnd Bergmann case VME_A16: 29835ba63b8SArnd Bergmann case VME_A24: 29935ba63b8SArnd Bergmann case VME_A32: 30035ba63b8SArnd Bergmann case VME_A64: 30135ba63b8SArnd Bergmann case VME_CRCSR: 30235ba63b8SArnd Bergmann case VME_USER1: 30335ba63b8SArnd Bergmann case VME_USER2: 30435ba63b8SArnd Bergmann case VME_USER3: 30535ba63b8SArnd Bergmann case VME_USER4: 30635ba63b8SArnd Bergmann break; 30735ba63b8SArnd Bergmann default: 30835ba63b8SArnd Bergmann pr_err("Invalid address space\n"); 30935ba63b8SArnd Bergmann retval = -EINVAL; 31035ba63b8SArnd Bergmann goto err_aspace; 31135ba63b8SArnd Bergmann } 31235ba63b8SArnd Bergmann 31335ba63b8SArnd Bergmann spin_lock(&image->lock); 31435ba63b8SArnd Bergmann 31535ba63b8SArnd Bergmann i = image->number; 31635ba63b8SArnd Bergmann 31735ba63b8SArnd Bergmann bridge->masters[i].enabled = enabled; 31835ba63b8SArnd Bergmann bridge->masters[i].vme_base = vme_base; 31935ba63b8SArnd Bergmann bridge->masters[i].size = size; 32035ba63b8SArnd Bergmann bridge->masters[i].aspace = aspace; 32135ba63b8SArnd Bergmann bridge->masters[i].cycle = cycle; 32235ba63b8SArnd Bergmann bridge->masters[i].dwidth = dwidth; 32335ba63b8SArnd Bergmann 32435ba63b8SArnd Bergmann spin_unlock(&image->lock); 32535ba63b8SArnd Bergmann 32635ba63b8SArnd Bergmann return 0; 32735ba63b8SArnd Bergmann 32835ba63b8SArnd Bergmann err_aspace: 32935ba63b8SArnd Bergmann err_dwidth: 33035ba63b8SArnd Bergmann err_window: 33135ba63b8SArnd Bergmann return retval; 33235ba63b8SArnd Bergmann 33335ba63b8SArnd Bergmann } 33435ba63b8SArnd Bergmann 33535ba63b8SArnd Bergmann /* 33635ba63b8SArnd Bergmann * Set the attributes of an outbound window. 33735ba63b8SArnd Bergmann */ 33835ba63b8SArnd Bergmann static int __fake_master_get(struct vme_master_resource *image, int *enabled, 33935ba63b8SArnd Bergmann unsigned long long *vme_base, unsigned long long *size, 34035ba63b8SArnd Bergmann u32 *aspace, u32 *cycle, u32 *dwidth) 34135ba63b8SArnd Bergmann { 34235ba63b8SArnd Bergmann unsigned int i; 34335ba63b8SArnd Bergmann struct fake_driver *bridge; 34435ba63b8SArnd Bergmann 34535ba63b8SArnd Bergmann bridge = image->parent->driver_priv; 34635ba63b8SArnd Bergmann 34735ba63b8SArnd Bergmann i = image->number; 34835ba63b8SArnd Bergmann 34935ba63b8SArnd Bergmann *enabled = bridge->masters[i].enabled; 35035ba63b8SArnd Bergmann *vme_base = bridge->masters[i].vme_base; 35135ba63b8SArnd Bergmann *size = bridge->masters[i].size; 35235ba63b8SArnd Bergmann *aspace = bridge->masters[i].aspace; 35335ba63b8SArnd Bergmann *cycle = bridge->masters[i].cycle; 35435ba63b8SArnd Bergmann *dwidth = bridge->masters[i].dwidth; 35535ba63b8SArnd Bergmann 35635ba63b8SArnd Bergmann return 0; 35735ba63b8SArnd Bergmann } 35835ba63b8SArnd Bergmann 35935ba63b8SArnd Bergmann static int fake_master_get(struct vme_master_resource *image, int *enabled, 36035ba63b8SArnd Bergmann unsigned long long *vme_base, unsigned long long *size, 36135ba63b8SArnd Bergmann u32 *aspace, u32 *cycle, u32 *dwidth) 36235ba63b8SArnd Bergmann { 36335ba63b8SArnd Bergmann int retval; 36435ba63b8SArnd Bergmann 36535ba63b8SArnd Bergmann spin_lock(&image->lock); 36635ba63b8SArnd Bergmann 36735ba63b8SArnd Bergmann retval = __fake_master_get(image, enabled, vme_base, size, aspace, 36835ba63b8SArnd Bergmann cycle, dwidth); 36935ba63b8SArnd Bergmann 37035ba63b8SArnd Bergmann spin_unlock(&image->lock); 37135ba63b8SArnd Bergmann 37235ba63b8SArnd Bergmann return retval; 37335ba63b8SArnd Bergmann } 37435ba63b8SArnd Bergmann 37535ba63b8SArnd Bergmann static void fake_lm_check(struct fake_driver *bridge, unsigned long long addr, 37635ba63b8SArnd Bergmann u32 aspace, u32 cycle) 37735ba63b8SArnd Bergmann { 37835ba63b8SArnd Bergmann struct vme_bridge *fake_bridge; 37935ba63b8SArnd Bergmann unsigned long long lm_base; 38035ba63b8SArnd Bergmann u32 lm_aspace, lm_cycle; 38135ba63b8SArnd Bergmann int i; 38235ba63b8SArnd Bergmann struct vme_lm_resource *lm; 38335ba63b8SArnd Bergmann struct list_head *pos = NULL, *n; 38435ba63b8SArnd Bergmann 38535ba63b8SArnd Bergmann /* Get vme_bridge */ 38635ba63b8SArnd Bergmann fake_bridge = bridge->parent; 38735ba63b8SArnd Bergmann 38835ba63b8SArnd Bergmann /* Loop through each location monitor resource */ 38935ba63b8SArnd Bergmann list_for_each_safe(pos, n, &fake_bridge->lm_resources) { 39035ba63b8SArnd Bergmann lm = list_entry(pos, struct vme_lm_resource, list); 39135ba63b8SArnd Bergmann 39235ba63b8SArnd Bergmann /* If disabled, we're done */ 39335ba63b8SArnd Bergmann if (bridge->lm_enabled == 0) 39435ba63b8SArnd Bergmann return; 39535ba63b8SArnd Bergmann 39635ba63b8SArnd Bergmann lm_base = bridge->lm_base; 39735ba63b8SArnd Bergmann lm_aspace = bridge->lm_aspace; 39835ba63b8SArnd Bergmann lm_cycle = bridge->lm_cycle; 39935ba63b8SArnd Bergmann 40035ba63b8SArnd Bergmann /* First make sure that the cycle and address space match */ 40135ba63b8SArnd Bergmann if ((lm_aspace == aspace) && (lm_cycle == cycle)) { 40235ba63b8SArnd Bergmann for (i = 0; i < lm->monitors; i++) { 40335ba63b8SArnd Bergmann /* Each location monitor covers 8 bytes */ 40435ba63b8SArnd Bergmann if (((lm_base + (8 * i)) <= addr) && 40535ba63b8SArnd Bergmann ((lm_base + (8 * i) + 8) > addr)) { 40635ba63b8SArnd Bergmann if (bridge->lm_callback[i]) 40735ba63b8SArnd Bergmann bridge->lm_callback[i]( 40835ba63b8SArnd Bergmann bridge->lm_data[i]); 40935ba63b8SArnd Bergmann } 41035ba63b8SArnd Bergmann } 41135ba63b8SArnd Bergmann } 41235ba63b8SArnd Bergmann } 41335ba63b8SArnd Bergmann } 41435ba63b8SArnd Bergmann 41535ba63b8SArnd Bergmann static noinline_for_stack u8 fake_vmeread8(struct fake_driver *bridge, 41635ba63b8SArnd Bergmann unsigned long long addr, 41735ba63b8SArnd Bergmann u32 aspace, u32 cycle) 41835ba63b8SArnd Bergmann { 41935ba63b8SArnd Bergmann u8 retval = 0xff; 42035ba63b8SArnd Bergmann int i; 42135ba63b8SArnd Bergmann unsigned long long start, end, offset; 42235ba63b8SArnd Bergmann u8 *loc; 42335ba63b8SArnd Bergmann 42435ba63b8SArnd Bergmann for (i = 0; i < FAKE_MAX_SLAVE; i++) { 42535ba63b8SArnd Bergmann start = bridge->slaves[i].vme_base; 42635ba63b8SArnd Bergmann end = bridge->slaves[i].vme_base + bridge->slaves[i].size; 42735ba63b8SArnd Bergmann 42835ba63b8SArnd Bergmann if (aspace != bridge->slaves[i].aspace) 42935ba63b8SArnd Bergmann continue; 43035ba63b8SArnd Bergmann 43135ba63b8SArnd Bergmann if (cycle != bridge->slaves[i].cycle) 43235ba63b8SArnd Bergmann continue; 43335ba63b8SArnd Bergmann 43435ba63b8SArnd Bergmann if ((addr >= start) && (addr < end)) { 43535ba63b8SArnd Bergmann offset = addr - bridge->slaves[i].vme_base; 43635ba63b8SArnd Bergmann loc = (u8 *)(bridge->slaves[i].buf_base + offset); 43735ba63b8SArnd Bergmann retval = *loc; 43835ba63b8SArnd Bergmann 43935ba63b8SArnd Bergmann break; 44035ba63b8SArnd Bergmann } 44135ba63b8SArnd Bergmann } 44235ba63b8SArnd Bergmann 44335ba63b8SArnd Bergmann fake_lm_check(bridge, addr, aspace, cycle); 44435ba63b8SArnd Bergmann 44535ba63b8SArnd Bergmann return retval; 44635ba63b8SArnd Bergmann } 44735ba63b8SArnd Bergmann 44835ba63b8SArnd Bergmann static noinline_for_stack u16 fake_vmeread16(struct fake_driver *bridge, 44935ba63b8SArnd Bergmann unsigned long long addr, 45035ba63b8SArnd Bergmann u32 aspace, u32 cycle) 45135ba63b8SArnd Bergmann { 45235ba63b8SArnd Bergmann u16 retval = 0xffff; 45335ba63b8SArnd Bergmann int i; 45435ba63b8SArnd Bergmann unsigned long long start, end, offset; 45535ba63b8SArnd Bergmann u16 *loc; 45635ba63b8SArnd Bergmann 45735ba63b8SArnd Bergmann for (i = 0; i < FAKE_MAX_SLAVE; i++) { 45835ba63b8SArnd Bergmann if (aspace != bridge->slaves[i].aspace) 45935ba63b8SArnd Bergmann continue; 46035ba63b8SArnd Bergmann 46135ba63b8SArnd Bergmann if (cycle != bridge->slaves[i].cycle) 46235ba63b8SArnd Bergmann continue; 46335ba63b8SArnd Bergmann 46435ba63b8SArnd Bergmann start = bridge->slaves[i].vme_base; 46535ba63b8SArnd Bergmann end = bridge->slaves[i].vme_base + bridge->slaves[i].size; 46635ba63b8SArnd Bergmann 46735ba63b8SArnd Bergmann if ((addr >= start) && ((addr + 1) < end)) { 46835ba63b8SArnd Bergmann offset = addr - bridge->slaves[i].vme_base; 46935ba63b8SArnd Bergmann loc = (u16 *)(bridge->slaves[i].buf_base + offset); 47035ba63b8SArnd Bergmann retval = *loc; 47135ba63b8SArnd Bergmann 47235ba63b8SArnd Bergmann break; 47335ba63b8SArnd Bergmann } 47435ba63b8SArnd Bergmann } 47535ba63b8SArnd Bergmann 47635ba63b8SArnd Bergmann fake_lm_check(bridge, addr, aspace, cycle); 47735ba63b8SArnd Bergmann 47835ba63b8SArnd Bergmann return retval; 47935ba63b8SArnd Bergmann } 48035ba63b8SArnd Bergmann 48135ba63b8SArnd Bergmann static noinline_for_stack u32 fake_vmeread32(struct fake_driver *bridge, 48235ba63b8SArnd Bergmann unsigned long long addr, 48335ba63b8SArnd Bergmann u32 aspace, u32 cycle) 48435ba63b8SArnd Bergmann { 48535ba63b8SArnd Bergmann u32 retval = 0xffffffff; 48635ba63b8SArnd Bergmann int i; 48735ba63b8SArnd Bergmann unsigned long long start, end, offset; 48835ba63b8SArnd Bergmann u32 *loc; 48935ba63b8SArnd Bergmann 49035ba63b8SArnd Bergmann for (i = 0; i < FAKE_MAX_SLAVE; i++) { 49135ba63b8SArnd Bergmann if (aspace != bridge->slaves[i].aspace) 49235ba63b8SArnd Bergmann continue; 49335ba63b8SArnd Bergmann 49435ba63b8SArnd Bergmann if (cycle != bridge->slaves[i].cycle) 49535ba63b8SArnd Bergmann continue; 49635ba63b8SArnd Bergmann 49735ba63b8SArnd Bergmann start = bridge->slaves[i].vme_base; 49835ba63b8SArnd Bergmann end = bridge->slaves[i].vme_base + bridge->slaves[i].size; 49935ba63b8SArnd Bergmann 50035ba63b8SArnd Bergmann if ((addr >= start) && ((addr + 3) < end)) { 50135ba63b8SArnd Bergmann offset = addr - bridge->slaves[i].vme_base; 50235ba63b8SArnd Bergmann loc = (u32 *)(bridge->slaves[i].buf_base + offset); 50335ba63b8SArnd Bergmann retval = *loc; 50435ba63b8SArnd Bergmann 50535ba63b8SArnd Bergmann break; 50635ba63b8SArnd Bergmann } 50735ba63b8SArnd Bergmann } 50835ba63b8SArnd Bergmann 50935ba63b8SArnd Bergmann fake_lm_check(bridge, addr, aspace, cycle); 51035ba63b8SArnd Bergmann 51135ba63b8SArnd Bergmann return retval; 51235ba63b8SArnd Bergmann } 51335ba63b8SArnd Bergmann 51435ba63b8SArnd Bergmann static ssize_t fake_master_read(struct vme_master_resource *image, void *buf, 51535ba63b8SArnd Bergmann size_t count, loff_t offset) 51635ba63b8SArnd Bergmann { 51735ba63b8SArnd Bergmann int retval; 51835ba63b8SArnd Bergmann u32 aspace, cycle, dwidth; 51935ba63b8SArnd Bergmann struct vme_bridge *fake_bridge; 52035ba63b8SArnd Bergmann struct fake_driver *priv; 52135ba63b8SArnd Bergmann int i; 52235ba63b8SArnd Bergmann unsigned long long addr; 52335ba63b8SArnd Bergmann unsigned int done = 0; 52435ba63b8SArnd Bergmann unsigned int count32; 52535ba63b8SArnd Bergmann 52635ba63b8SArnd Bergmann fake_bridge = image->parent; 52735ba63b8SArnd Bergmann 52835ba63b8SArnd Bergmann priv = fake_bridge->driver_priv; 52935ba63b8SArnd Bergmann 53035ba63b8SArnd Bergmann i = image->number; 53135ba63b8SArnd Bergmann 53235ba63b8SArnd Bergmann addr = (unsigned long long)priv->masters[i].vme_base + offset; 53335ba63b8SArnd Bergmann aspace = priv->masters[i].aspace; 53435ba63b8SArnd Bergmann cycle = priv->masters[i].cycle; 53535ba63b8SArnd Bergmann dwidth = priv->masters[i].dwidth; 53635ba63b8SArnd Bergmann 53735ba63b8SArnd Bergmann spin_lock(&image->lock); 53835ba63b8SArnd Bergmann 53935ba63b8SArnd Bergmann /* The following code handles VME address alignment. We cannot use 54035ba63b8SArnd Bergmann * memcpy_xxx here because it may cut data transfers in to 8-bit 54135ba63b8SArnd Bergmann * cycles when D16 or D32 cycles are required on the VME bus. 54235ba63b8SArnd Bergmann * On the other hand, the bridge itself assures that the maximum data 54335ba63b8SArnd Bergmann * cycle configured for the transfer is used and splits it 54435ba63b8SArnd Bergmann * automatically for non-aligned addresses, so we don't want the 54535ba63b8SArnd Bergmann * overhead of needlessly forcing small transfers for the entire cycle. 54635ba63b8SArnd Bergmann */ 54735ba63b8SArnd Bergmann if (addr & 0x1) { 54835ba63b8SArnd Bergmann *(u8 *)buf = fake_vmeread8(priv, addr, aspace, cycle); 54935ba63b8SArnd Bergmann done += 1; 55035ba63b8SArnd Bergmann if (done == count) 55135ba63b8SArnd Bergmann goto out; 55235ba63b8SArnd Bergmann } 55335ba63b8SArnd Bergmann if ((dwidth == VME_D16) || (dwidth == VME_D32)) { 55435ba63b8SArnd Bergmann if ((addr + done) & 0x2) { 55535ba63b8SArnd Bergmann if ((count - done) < 2) { 55635ba63b8SArnd Bergmann *(u8 *)(buf + done) = fake_vmeread8(priv, 55735ba63b8SArnd Bergmann addr + done, aspace, cycle); 55835ba63b8SArnd Bergmann done += 1; 55935ba63b8SArnd Bergmann goto out; 56035ba63b8SArnd Bergmann } else { 56135ba63b8SArnd Bergmann *(u16 *)(buf + done) = fake_vmeread16(priv, 56235ba63b8SArnd Bergmann addr + done, aspace, cycle); 56335ba63b8SArnd Bergmann done += 2; 56435ba63b8SArnd Bergmann } 56535ba63b8SArnd Bergmann } 56635ba63b8SArnd Bergmann } 56735ba63b8SArnd Bergmann 56835ba63b8SArnd Bergmann if (dwidth == VME_D32) { 56935ba63b8SArnd Bergmann count32 = (count - done) & ~0x3; 57035ba63b8SArnd Bergmann while (done < count32) { 57135ba63b8SArnd Bergmann *(u32 *)(buf + done) = fake_vmeread32(priv, addr + done, 57235ba63b8SArnd Bergmann aspace, cycle); 57335ba63b8SArnd Bergmann done += 4; 57435ba63b8SArnd Bergmann } 57535ba63b8SArnd Bergmann } else if (dwidth == VME_D16) { 57635ba63b8SArnd Bergmann count32 = (count - done) & ~0x3; 57735ba63b8SArnd Bergmann while (done < count32) { 57835ba63b8SArnd Bergmann *(u16 *)(buf + done) = fake_vmeread16(priv, addr + done, 57935ba63b8SArnd Bergmann aspace, cycle); 58035ba63b8SArnd Bergmann done += 2; 58135ba63b8SArnd Bergmann } 58235ba63b8SArnd Bergmann } else if (dwidth == VME_D8) { 58335ba63b8SArnd Bergmann count32 = (count - done); 58435ba63b8SArnd Bergmann while (done < count32) { 58535ba63b8SArnd Bergmann *(u8 *)(buf + done) = fake_vmeread8(priv, addr + done, 58635ba63b8SArnd Bergmann aspace, cycle); 58735ba63b8SArnd Bergmann done += 1; 58835ba63b8SArnd Bergmann } 58935ba63b8SArnd Bergmann 59035ba63b8SArnd Bergmann } 59135ba63b8SArnd Bergmann 59235ba63b8SArnd Bergmann if ((dwidth == VME_D16) || (dwidth == VME_D32)) { 59335ba63b8SArnd Bergmann if ((count - done) & 0x2) { 59435ba63b8SArnd Bergmann *(u16 *)(buf + done) = fake_vmeread16(priv, addr + done, 59535ba63b8SArnd Bergmann aspace, cycle); 59635ba63b8SArnd Bergmann done += 2; 59735ba63b8SArnd Bergmann } 59835ba63b8SArnd Bergmann } 59935ba63b8SArnd Bergmann if ((count - done) & 0x1) { 60035ba63b8SArnd Bergmann *(u8 *)(buf + done) = fake_vmeread8(priv, addr + done, aspace, 60135ba63b8SArnd Bergmann cycle); 60235ba63b8SArnd Bergmann done += 1; 60335ba63b8SArnd Bergmann } 60435ba63b8SArnd Bergmann 60535ba63b8SArnd Bergmann out: 60635ba63b8SArnd Bergmann retval = count; 60735ba63b8SArnd Bergmann 60835ba63b8SArnd Bergmann spin_unlock(&image->lock); 60935ba63b8SArnd Bergmann 61035ba63b8SArnd Bergmann return retval; 61135ba63b8SArnd Bergmann } 61235ba63b8SArnd Bergmann 61335ba63b8SArnd Bergmann static noinline_for_stack void fake_vmewrite8(struct fake_driver *bridge, 61435ba63b8SArnd Bergmann u8 *buf, unsigned long long addr, 61535ba63b8SArnd Bergmann u32 aspace, u32 cycle) 61635ba63b8SArnd Bergmann { 61735ba63b8SArnd Bergmann int i; 61835ba63b8SArnd Bergmann unsigned long long start, end, offset; 61935ba63b8SArnd Bergmann u8 *loc; 62035ba63b8SArnd Bergmann 62135ba63b8SArnd Bergmann for (i = 0; i < FAKE_MAX_SLAVE; i++) { 62235ba63b8SArnd Bergmann if (aspace != bridge->slaves[i].aspace) 62335ba63b8SArnd Bergmann continue; 62435ba63b8SArnd Bergmann 62535ba63b8SArnd Bergmann if (cycle != bridge->slaves[i].cycle) 62635ba63b8SArnd Bergmann continue; 62735ba63b8SArnd Bergmann 62835ba63b8SArnd Bergmann start = bridge->slaves[i].vme_base; 62935ba63b8SArnd Bergmann end = bridge->slaves[i].vme_base + bridge->slaves[i].size; 63035ba63b8SArnd Bergmann 63135ba63b8SArnd Bergmann if ((addr >= start) && (addr < end)) { 63235ba63b8SArnd Bergmann offset = addr - bridge->slaves[i].vme_base; 63335ba63b8SArnd Bergmann loc = (u8 *)((void *)bridge->slaves[i].buf_base + offset); 63435ba63b8SArnd Bergmann *loc = *buf; 63535ba63b8SArnd Bergmann 63635ba63b8SArnd Bergmann break; 63735ba63b8SArnd Bergmann } 63835ba63b8SArnd Bergmann } 63935ba63b8SArnd Bergmann 64035ba63b8SArnd Bergmann fake_lm_check(bridge, addr, aspace, cycle); 64135ba63b8SArnd Bergmann 64235ba63b8SArnd Bergmann } 64335ba63b8SArnd Bergmann 64435ba63b8SArnd Bergmann static noinline_for_stack void fake_vmewrite16(struct fake_driver *bridge, 64535ba63b8SArnd Bergmann u16 *buf, unsigned long long addr, 64635ba63b8SArnd Bergmann u32 aspace, u32 cycle) 64735ba63b8SArnd Bergmann { 64835ba63b8SArnd Bergmann int i; 64935ba63b8SArnd Bergmann unsigned long long start, end, offset; 65035ba63b8SArnd Bergmann u16 *loc; 65135ba63b8SArnd Bergmann 65235ba63b8SArnd Bergmann for (i = 0; i < FAKE_MAX_SLAVE; i++) { 65335ba63b8SArnd Bergmann if (aspace != bridge->slaves[i].aspace) 65435ba63b8SArnd Bergmann continue; 65535ba63b8SArnd Bergmann 65635ba63b8SArnd Bergmann if (cycle != bridge->slaves[i].cycle) 65735ba63b8SArnd Bergmann continue; 65835ba63b8SArnd Bergmann 65935ba63b8SArnd Bergmann start = bridge->slaves[i].vme_base; 66035ba63b8SArnd Bergmann end = bridge->slaves[i].vme_base + bridge->slaves[i].size; 66135ba63b8SArnd Bergmann 66235ba63b8SArnd Bergmann if ((addr >= start) && ((addr + 1) < end)) { 66335ba63b8SArnd Bergmann offset = addr - bridge->slaves[i].vme_base; 66435ba63b8SArnd Bergmann loc = (u16 *)((void *)bridge->slaves[i].buf_base + offset); 66535ba63b8SArnd Bergmann *loc = *buf; 66635ba63b8SArnd Bergmann 66735ba63b8SArnd Bergmann break; 66835ba63b8SArnd Bergmann } 66935ba63b8SArnd Bergmann } 67035ba63b8SArnd Bergmann 67135ba63b8SArnd Bergmann fake_lm_check(bridge, addr, aspace, cycle); 67235ba63b8SArnd Bergmann 67335ba63b8SArnd Bergmann } 67435ba63b8SArnd Bergmann 67535ba63b8SArnd Bergmann static noinline_for_stack void fake_vmewrite32(struct fake_driver *bridge, 67635ba63b8SArnd Bergmann u32 *buf, unsigned long long addr, 67735ba63b8SArnd Bergmann u32 aspace, u32 cycle) 67835ba63b8SArnd Bergmann { 67935ba63b8SArnd Bergmann int i; 68035ba63b8SArnd Bergmann unsigned long long start, end, offset; 68135ba63b8SArnd Bergmann u32 *loc; 68235ba63b8SArnd Bergmann 68335ba63b8SArnd Bergmann for (i = 0; i < FAKE_MAX_SLAVE; i++) { 68435ba63b8SArnd Bergmann if (aspace != bridge->slaves[i].aspace) 68535ba63b8SArnd Bergmann continue; 68635ba63b8SArnd Bergmann 68735ba63b8SArnd Bergmann if (cycle != bridge->slaves[i].cycle) 68835ba63b8SArnd Bergmann continue; 68935ba63b8SArnd Bergmann 69035ba63b8SArnd Bergmann start = bridge->slaves[i].vme_base; 69135ba63b8SArnd Bergmann end = bridge->slaves[i].vme_base + bridge->slaves[i].size; 69235ba63b8SArnd Bergmann 69335ba63b8SArnd Bergmann if ((addr >= start) && ((addr + 3) < end)) { 69435ba63b8SArnd Bergmann offset = addr - bridge->slaves[i].vme_base; 69535ba63b8SArnd Bergmann loc = (u32 *)((void *)bridge->slaves[i].buf_base + offset); 69635ba63b8SArnd Bergmann *loc = *buf; 69735ba63b8SArnd Bergmann 69835ba63b8SArnd Bergmann break; 69935ba63b8SArnd Bergmann } 70035ba63b8SArnd Bergmann } 70135ba63b8SArnd Bergmann 70235ba63b8SArnd Bergmann fake_lm_check(bridge, addr, aspace, cycle); 70335ba63b8SArnd Bergmann 70435ba63b8SArnd Bergmann } 70535ba63b8SArnd Bergmann 70635ba63b8SArnd Bergmann static ssize_t fake_master_write(struct vme_master_resource *image, void *buf, 70735ba63b8SArnd Bergmann size_t count, loff_t offset) 70835ba63b8SArnd Bergmann { 70935ba63b8SArnd Bergmann int retval = 0; 71035ba63b8SArnd Bergmann u32 aspace, cycle, dwidth; 71135ba63b8SArnd Bergmann unsigned long long addr; 71235ba63b8SArnd Bergmann int i; 71335ba63b8SArnd Bergmann unsigned int done = 0; 71435ba63b8SArnd Bergmann unsigned int count32; 71535ba63b8SArnd Bergmann 71635ba63b8SArnd Bergmann struct vme_bridge *fake_bridge; 71735ba63b8SArnd Bergmann struct fake_driver *bridge; 71835ba63b8SArnd Bergmann 71935ba63b8SArnd Bergmann fake_bridge = image->parent; 72035ba63b8SArnd Bergmann 72135ba63b8SArnd Bergmann bridge = fake_bridge->driver_priv; 72235ba63b8SArnd Bergmann 72335ba63b8SArnd Bergmann i = image->number; 72435ba63b8SArnd Bergmann 72535ba63b8SArnd Bergmann addr = bridge->masters[i].vme_base + offset; 72635ba63b8SArnd Bergmann aspace = bridge->masters[i].aspace; 72735ba63b8SArnd Bergmann cycle = bridge->masters[i].cycle; 72835ba63b8SArnd Bergmann dwidth = bridge->masters[i].dwidth; 72935ba63b8SArnd Bergmann 73035ba63b8SArnd Bergmann spin_lock(&image->lock); 73135ba63b8SArnd Bergmann 73235ba63b8SArnd Bergmann /* Here we apply for the same strategy we do in master_read 73335ba63b8SArnd Bergmann * function in order to assure the correct cycles. 73435ba63b8SArnd Bergmann */ 73535ba63b8SArnd Bergmann if (addr & 0x1) { 73635ba63b8SArnd Bergmann fake_vmewrite8(bridge, (u8 *)buf, addr, aspace, cycle); 73735ba63b8SArnd Bergmann done += 1; 73835ba63b8SArnd Bergmann if (done == count) 73935ba63b8SArnd Bergmann goto out; 74035ba63b8SArnd Bergmann } 74135ba63b8SArnd Bergmann 74235ba63b8SArnd Bergmann if ((dwidth == VME_D16) || (dwidth == VME_D32)) { 74335ba63b8SArnd Bergmann if ((addr + done) & 0x2) { 74435ba63b8SArnd Bergmann if ((count - done) < 2) { 74535ba63b8SArnd Bergmann fake_vmewrite8(bridge, (u8 *)(buf + done), 74635ba63b8SArnd Bergmann addr + done, aspace, cycle); 74735ba63b8SArnd Bergmann done += 1; 74835ba63b8SArnd Bergmann goto out; 74935ba63b8SArnd Bergmann } else { 75035ba63b8SArnd Bergmann fake_vmewrite16(bridge, (u16 *)(buf + done), 75135ba63b8SArnd Bergmann addr + done, aspace, cycle); 75235ba63b8SArnd Bergmann done += 2; 75335ba63b8SArnd Bergmann } 75435ba63b8SArnd Bergmann } 75535ba63b8SArnd Bergmann } 75635ba63b8SArnd Bergmann 75735ba63b8SArnd Bergmann if (dwidth == VME_D32) { 75835ba63b8SArnd Bergmann count32 = (count - done) & ~0x3; 75935ba63b8SArnd Bergmann while (done < count32) { 76035ba63b8SArnd Bergmann fake_vmewrite32(bridge, (u32 *)(buf + done), 76135ba63b8SArnd Bergmann addr + done, aspace, cycle); 76235ba63b8SArnd Bergmann done += 4; 76335ba63b8SArnd Bergmann } 76435ba63b8SArnd Bergmann } else if (dwidth == VME_D16) { 76535ba63b8SArnd Bergmann count32 = (count - done) & ~0x3; 76635ba63b8SArnd Bergmann while (done < count32) { 76735ba63b8SArnd Bergmann fake_vmewrite16(bridge, (u16 *)(buf + done), 76835ba63b8SArnd Bergmann addr + done, aspace, cycle); 76935ba63b8SArnd Bergmann done += 2; 77035ba63b8SArnd Bergmann } 77135ba63b8SArnd Bergmann } else if (dwidth == VME_D8) { 77235ba63b8SArnd Bergmann count32 = (count - done); 77335ba63b8SArnd Bergmann while (done < count32) { 77435ba63b8SArnd Bergmann fake_vmewrite8(bridge, (u8 *)(buf + done), addr + done, 77535ba63b8SArnd Bergmann aspace, cycle); 77635ba63b8SArnd Bergmann done += 1; 77735ba63b8SArnd Bergmann } 77835ba63b8SArnd Bergmann 77935ba63b8SArnd Bergmann } 78035ba63b8SArnd Bergmann 78135ba63b8SArnd Bergmann if ((dwidth == VME_D16) || (dwidth == VME_D32)) { 78235ba63b8SArnd Bergmann if ((count - done) & 0x2) { 78335ba63b8SArnd Bergmann fake_vmewrite16(bridge, (u16 *)(buf + done), 78435ba63b8SArnd Bergmann addr + done, aspace, cycle); 78535ba63b8SArnd Bergmann done += 2; 78635ba63b8SArnd Bergmann } 78735ba63b8SArnd Bergmann } 78835ba63b8SArnd Bergmann 78935ba63b8SArnd Bergmann if ((count - done) & 0x1) { 79035ba63b8SArnd Bergmann fake_vmewrite8(bridge, (u8 *)(buf + done), addr + done, aspace, 79135ba63b8SArnd Bergmann cycle); 79235ba63b8SArnd Bergmann done += 1; 79335ba63b8SArnd Bergmann } 79435ba63b8SArnd Bergmann 79535ba63b8SArnd Bergmann out: 79635ba63b8SArnd Bergmann retval = count; 79735ba63b8SArnd Bergmann 79835ba63b8SArnd Bergmann spin_unlock(&image->lock); 79935ba63b8SArnd Bergmann 80035ba63b8SArnd Bergmann return retval; 80135ba63b8SArnd Bergmann } 80235ba63b8SArnd Bergmann 80335ba63b8SArnd Bergmann /* 80435ba63b8SArnd Bergmann * Perform an RMW cycle on the VME bus. 80535ba63b8SArnd Bergmann * 80635ba63b8SArnd Bergmann * Requires a previously configured master window, returns final value. 80735ba63b8SArnd Bergmann */ 80835ba63b8SArnd Bergmann static unsigned int fake_master_rmw(struct vme_master_resource *image, 80935ba63b8SArnd Bergmann unsigned int mask, unsigned int compare, unsigned int swap, 81035ba63b8SArnd Bergmann loff_t offset) 81135ba63b8SArnd Bergmann { 81235ba63b8SArnd Bergmann u32 tmp, base; 81335ba63b8SArnd Bergmann u32 aspace, cycle; 81435ba63b8SArnd Bergmann int i; 81535ba63b8SArnd Bergmann struct fake_driver *bridge; 81635ba63b8SArnd Bergmann 81735ba63b8SArnd Bergmann bridge = image->parent->driver_priv; 81835ba63b8SArnd Bergmann 81935ba63b8SArnd Bergmann /* Find the PCI address that maps to the desired VME address */ 82035ba63b8SArnd Bergmann i = image->number; 82135ba63b8SArnd Bergmann 82235ba63b8SArnd Bergmann base = bridge->masters[i].vme_base; 82335ba63b8SArnd Bergmann aspace = bridge->masters[i].aspace; 82435ba63b8SArnd Bergmann cycle = bridge->masters[i].cycle; 82535ba63b8SArnd Bergmann 82635ba63b8SArnd Bergmann /* Lock image */ 82735ba63b8SArnd Bergmann spin_lock(&image->lock); 82835ba63b8SArnd Bergmann 82935ba63b8SArnd Bergmann /* Read existing value */ 83035ba63b8SArnd Bergmann tmp = fake_vmeread32(bridge, base + offset, aspace, cycle); 83135ba63b8SArnd Bergmann 83235ba63b8SArnd Bergmann /* Perform check */ 83335ba63b8SArnd Bergmann if ((tmp && mask) == (compare && mask)) { 83435ba63b8SArnd Bergmann tmp = tmp | (mask | swap); 83535ba63b8SArnd Bergmann tmp = tmp & (~mask | swap); 83635ba63b8SArnd Bergmann 83735ba63b8SArnd Bergmann /* Write back */ 83835ba63b8SArnd Bergmann fake_vmewrite32(bridge, &tmp, base + offset, aspace, cycle); 83935ba63b8SArnd Bergmann } 84035ba63b8SArnd Bergmann 84135ba63b8SArnd Bergmann /* Unlock image */ 84235ba63b8SArnd Bergmann spin_unlock(&image->lock); 84335ba63b8SArnd Bergmann 84435ba63b8SArnd Bergmann return tmp; 84535ba63b8SArnd Bergmann } 84635ba63b8SArnd Bergmann 84735ba63b8SArnd Bergmann /* 84835ba63b8SArnd Bergmann * All 4 location monitors reside at the same base - this is therefore a 84935ba63b8SArnd Bergmann * system wide configuration. 85035ba63b8SArnd Bergmann * 85135ba63b8SArnd Bergmann * This does not enable the LM monitor - that should be done when the first 85235ba63b8SArnd Bergmann * callback is attached and disabled when the last callback is removed. 85335ba63b8SArnd Bergmann */ 85435ba63b8SArnd Bergmann static int fake_lm_set(struct vme_lm_resource *lm, unsigned long long lm_base, 85535ba63b8SArnd Bergmann u32 aspace, u32 cycle) 85635ba63b8SArnd Bergmann { 85735ba63b8SArnd Bergmann int i; 85835ba63b8SArnd Bergmann struct vme_bridge *fake_bridge; 85935ba63b8SArnd Bergmann struct fake_driver *bridge; 86035ba63b8SArnd Bergmann 86135ba63b8SArnd Bergmann fake_bridge = lm->parent; 86235ba63b8SArnd Bergmann 86335ba63b8SArnd Bergmann bridge = fake_bridge->driver_priv; 86435ba63b8SArnd Bergmann 86535ba63b8SArnd Bergmann mutex_lock(&lm->mtx); 86635ba63b8SArnd Bergmann 86735ba63b8SArnd Bergmann /* If we already have a callback attached, we can't move it! */ 86835ba63b8SArnd Bergmann for (i = 0; i < lm->monitors; i++) { 86935ba63b8SArnd Bergmann if (bridge->lm_callback[i]) { 87035ba63b8SArnd Bergmann mutex_unlock(&lm->mtx); 87135ba63b8SArnd Bergmann pr_err("Location monitor callback attached, can't reset\n"); 87235ba63b8SArnd Bergmann return -EBUSY; 87335ba63b8SArnd Bergmann } 87435ba63b8SArnd Bergmann } 87535ba63b8SArnd Bergmann 87635ba63b8SArnd Bergmann switch (aspace) { 87735ba63b8SArnd Bergmann case VME_A16: 87835ba63b8SArnd Bergmann case VME_A24: 87935ba63b8SArnd Bergmann case VME_A32: 88035ba63b8SArnd Bergmann case VME_A64: 88135ba63b8SArnd Bergmann break; 88235ba63b8SArnd Bergmann default: 88335ba63b8SArnd Bergmann mutex_unlock(&lm->mtx); 88435ba63b8SArnd Bergmann pr_err("Invalid address space\n"); 88535ba63b8SArnd Bergmann return -EINVAL; 88635ba63b8SArnd Bergmann } 88735ba63b8SArnd Bergmann 88835ba63b8SArnd Bergmann bridge->lm_base = lm_base; 88935ba63b8SArnd Bergmann bridge->lm_aspace = aspace; 89035ba63b8SArnd Bergmann bridge->lm_cycle = cycle; 89135ba63b8SArnd Bergmann 89235ba63b8SArnd Bergmann mutex_unlock(&lm->mtx); 89335ba63b8SArnd Bergmann 89435ba63b8SArnd Bergmann return 0; 89535ba63b8SArnd Bergmann } 89635ba63b8SArnd Bergmann 89735ba63b8SArnd Bergmann /* Get configuration of the callback monitor and return whether it is enabled 89835ba63b8SArnd Bergmann * or disabled. 89935ba63b8SArnd Bergmann */ 90035ba63b8SArnd Bergmann static int fake_lm_get(struct vme_lm_resource *lm, 90135ba63b8SArnd Bergmann unsigned long long *lm_base, u32 *aspace, u32 *cycle) 90235ba63b8SArnd Bergmann { 90335ba63b8SArnd Bergmann struct fake_driver *bridge; 90435ba63b8SArnd Bergmann 90535ba63b8SArnd Bergmann bridge = lm->parent->driver_priv; 90635ba63b8SArnd Bergmann 90735ba63b8SArnd Bergmann mutex_lock(&lm->mtx); 90835ba63b8SArnd Bergmann 90935ba63b8SArnd Bergmann *lm_base = bridge->lm_base; 91035ba63b8SArnd Bergmann *aspace = bridge->lm_aspace; 91135ba63b8SArnd Bergmann *cycle = bridge->lm_cycle; 91235ba63b8SArnd Bergmann 91335ba63b8SArnd Bergmann mutex_unlock(&lm->mtx); 91435ba63b8SArnd Bergmann 91535ba63b8SArnd Bergmann return bridge->lm_enabled; 91635ba63b8SArnd Bergmann } 91735ba63b8SArnd Bergmann 91835ba63b8SArnd Bergmann /* 91935ba63b8SArnd Bergmann * Attach a callback to a specific location monitor. 92035ba63b8SArnd Bergmann * 92135ba63b8SArnd Bergmann * Callback will be passed the monitor triggered. 92235ba63b8SArnd Bergmann */ 92335ba63b8SArnd Bergmann static int fake_lm_attach(struct vme_lm_resource *lm, int monitor, 92435ba63b8SArnd Bergmann void (*callback)(void *), void *data) 92535ba63b8SArnd Bergmann { 92635ba63b8SArnd Bergmann struct vme_bridge *fake_bridge; 92735ba63b8SArnd Bergmann struct fake_driver *bridge; 92835ba63b8SArnd Bergmann 92935ba63b8SArnd Bergmann fake_bridge = lm->parent; 93035ba63b8SArnd Bergmann 93135ba63b8SArnd Bergmann bridge = fake_bridge->driver_priv; 93235ba63b8SArnd Bergmann 93335ba63b8SArnd Bergmann mutex_lock(&lm->mtx); 93435ba63b8SArnd Bergmann 93535ba63b8SArnd Bergmann /* Ensure that the location monitor is configured - need PGM or DATA */ 93635ba63b8SArnd Bergmann if (bridge->lm_cycle == 0) { 93735ba63b8SArnd Bergmann mutex_unlock(&lm->mtx); 93835ba63b8SArnd Bergmann pr_err("Location monitor not properly configured\n"); 93935ba63b8SArnd Bergmann return -EINVAL; 94035ba63b8SArnd Bergmann } 94135ba63b8SArnd Bergmann 94235ba63b8SArnd Bergmann /* Check that a callback isn't already attached */ 94335ba63b8SArnd Bergmann if (bridge->lm_callback[monitor]) { 94435ba63b8SArnd Bergmann mutex_unlock(&lm->mtx); 94535ba63b8SArnd Bergmann pr_err("Existing callback attached\n"); 94635ba63b8SArnd Bergmann return -EBUSY; 94735ba63b8SArnd Bergmann } 94835ba63b8SArnd Bergmann 94935ba63b8SArnd Bergmann /* Attach callback */ 95035ba63b8SArnd Bergmann bridge->lm_callback[monitor] = callback; 95135ba63b8SArnd Bergmann bridge->lm_data[monitor] = data; 95235ba63b8SArnd Bergmann 95335ba63b8SArnd Bergmann /* Ensure that global Location Monitor Enable set */ 95435ba63b8SArnd Bergmann bridge->lm_enabled = 1; 95535ba63b8SArnd Bergmann 95635ba63b8SArnd Bergmann mutex_unlock(&lm->mtx); 95735ba63b8SArnd Bergmann 95835ba63b8SArnd Bergmann return 0; 95935ba63b8SArnd Bergmann } 96035ba63b8SArnd Bergmann 96135ba63b8SArnd Bergmann /* 96235ba63b8SArnd Bergmann * Detach a callback function forn a specific location monitor. 96335ba63b8SArnd Bergmann */ 96435ba63b8SArnd Bergmann static int fake_lm_detach(struct vme_lm_resource *lm, int monitor) 96535ba63b8SArnd Bergmann { 96635ba63b8SArnd Bergmann u32 tmp; 96735ba63b8SArnd Bergmann int i; 96835ba63b8SArnd Bergmann struct fake_driver *bridge; 96935ba63b8SArnd Bergmann 97035ba63b8SArnd Bergmann bridge = lm->parent->driver_priv; 97135ba63b8SArnd Bergmann 97235ba63b8SArnd Bergmann mutex_lock(&lm->mtx); 97335ba63b8SArnd Bergmann 97435ba63b8SArnd Bergmann /* Detach callback */ 97535ba63b8SArnd Bergmann bridge->lm_callback[monitor] = NULL; 97635ba63b8SArnd Bergmann bridge->lm_data[monitor] = NULL; 97735ba63b8SArnd Bergmann 97835ba63b8SArnd Bergmann /* If all location monitors disabled, disable global Location Monitor */ 97935ba63b8SArnd Bergmann tmp = 0; 98035ba63b8SArnd Bergmann for (i = 0; i < lm->monitors; i++) { 98135ba63b8SArnd Bergmann if (bridge->lm_callback[i]) 98235ba63b8SArnd Bergmann tmp = 1; 98335ba63b8SArnd Bergmann } 98435ba63b8SArnd Bergmann 98535ba63b8SArnd Bergmann if (tmp == 0) 98635ba63b8SArnd Bergmann bridge->lm_enabled = 0; 98735ba63b8SArnd Bergmann 98835ba63b8SArnd Bergmann mutex_unlock(&lm->mtx); 98935ba63b8SArnd Bergmann 99035ba63b8SArnd Bergmann return 0; 99135ba63b8SArnd Bergmann } 99235ba63b8SArnd Bergmann 99335ba63b8SArnd Bergmann /* 99435ba63b8SArnd Bergmann * Determine Geographical Addressing 99535ba63b8SArnd Bergmann */ 99635ba63b8SArnd Bergmann static int fake_slot_get(struct vme_bridge *fake_bridge) 99735ba63b8SArnd Bergmann { 99835ba63b8SArnd Bergmann return geoid; 99935ba63b8SArnd Bergmann } 100035ba63b8SArnd Bergmann 100135ba63b8SArnd Bergmann static void *fake_alloc_consistent(struct device *parent, size_t size, 100235ba63b8SArnd Bergmann dma_addr_t *dma) 100335ba63b8SArnd Bergmann { 100435ba63b8SArnd Bergmann void *alloc = kmalloc(size, GFP_KERNEL); 100535ba63b8SArnd Bergmann 100635ba63b8SArnd Bergmann if (alloc) 100735ba63b8SArnd Bergmann *dma = fake_ptr_to_pci(alloc); 100835ba63b8SArnd Bergmann 100935ba63b8SArnd Bergmann return alloc; 101035ba63b8SArnd Bergmann } 101135ba63b8SArnd Bergmann 101235ba63b8SArnd Bergmann static void fake_free_consistent(struct device *parent, size_t size, 101335ba63b8SArnd Bergmann void *vaddr, dma_addr_t dma) 101435ba63b8SArnd Bergmann { 101535ba63b8SArnd Bergmann kfree(vaddr); 101635ba63b8SArnd Bergmann /* 101735ba63b8SArnd Bergmann dma_free_coherent(parent, size, vaddr, dma); 101835ba63b8SArnd Bergmann */ 101935ba63b8SArnd Bergmann } 102035ba63b8SArnd Bergmann 102135ba63b8SArnd Bergmann /* 102235ba63b8SArnd Bergmann * Configure CR/CSR space 102335ba63b8SArnd Bergmann * 102435ba63b8SArnd Bergmann * Access to the CR/CSR can be configured at power-up. The location of the 102535ba63b8SArnd Bergmann * CR/CSR registers in the CR/CSR address space is determined by the boards 102635ba63b8SArnd Bergmann * Geographic address. 102735ba63b8SArnd Bergmann * 102835ba63b8SArnd Bergmann * Each board has a 512kB window, with the highest 4kB being used for the 102935ba63b8SArnd Bergmann * boards registers, this means there is a fix length 508kB window which must 103035ba63b8SArnd Bergmann * be mapped onto PCI memory. 103135ba63b8SArnd Bergmann */ 103235ba63b8SArnd Bergmann static int fake_crcsr_init(struct vme_bridge *fake_bridge) 103335ba63b8SArnd Bergmann { 103435ba63b8SArnd Bergmann u32 vstat; 103535ba63b8SArnd Bergmann struct fake_driver *bridge; 103635ba63b8SArnd Bergmann 103735ba63b8SArnd Bergmann bridge = fake_bridge->driver_priv; 103835ba63b8SArnd Bergmann 103935ba63b8SArnd Bergmann /* Allocate mem for CR/CSR image */ 104035ba63b8SArnd Bergmann bridge->crcsr_kernel = kzalloc(VME_CRCSR_BUF_SIZE, GFP_KERNEL); 104135ba63b8SArnd Bergmann bridge->crcsr_bus = fake_ptr_to_pci(bridge->crcsr_kernel); 104235ba63b8SArnd Bergmann if (!bridge->crcsr_kernel) 104335ba63b8SArnd Bergmann return -ENOMEM; 104435ba63b8SArnd Bergmann 104535ba63b8SArnd Bergmann vstat = fake_slot_get(fake_bridge); 104635ba63b8SArnd Bergmann 104735ba63b8SArnd Bergmann pr_info("CR/CSR Offset: %d\n", vstat); 104835ba63b8SArnd Bergmann 104935ba63b8SArnd Bergmann return 0; 105035ba63b8SArnd Bergmann } 105135ba63b8SArnd Bergmann 105235ba63b8SArnd Bergmann static void fake_crcsr_exit(struct vme_bridge *fake_bridge) 105335ba63b8SArnd Bergmann { 105435ba63b8SArnd Bergmann struct fake_driver *bridge; 105535ba63b8SArnd Bergmann 105635ba63b8SArnd Bergmann bridge = fake_bridge->driver_priv; 105735ba63b8SArnd Bergmann 105835ba63b8SArnd Bergmann kfree(bridge->crcsr_kernel); 105935ba63b8SArnd Bergmann } 106035ba63b8SArnd Bergmann 106135ba63b8SArnd Bergmann static int __init fake_init(void) 106235ba63b8SArnd Bergmann { 106335ba63b8SArnd Bergmann int retval, i; 106435ba63b8SArnd Bergmann struct list_head *pos = NULL, *n; 106535ba63b8SArnd Bergmann struct vme_bridge *fake_bridge; 106635ba63b8SArnd Bergmann struct fake_driver *fake_device; 106735ba63b8SArnd Bergmann struct vme_master_resource *master_image; 106835ba63b8SArnd Bergmann struct vme_slave_resource *slave_image; 106935ba63b8SArnd Bergmann struct vme_lm_resource *lm; 107035ba63b8SArnd Bergmann 107135ba63b8SArnd Bergmann /* We need a fake parent device */ 107235ba63b8SArnd Bergmann vme_root = __root_device_register("vme", THIS_MODULE); 1073*7bef797dSChen Zhongjin if (IS_ERR(vme_root)) 1074*7bef797dSChen Zhongjin return PTR_ERR(vme_root); 107535ba63b8SArnd Bergmann 107635ba63b8SArnd Bergmann /* If we want to support more than one bridge at some point, we need to 107735ba63b8SArnd Bergmann * dynamically allocate this so we get one per device. 107835ba63b8SArnd Bergmann */ 107935ba63b8SArnd Bergmann fake_bridge = kzalloc(sizeof(*fake_bridge), GFP_KERNEL); 108035ba63b8SArnd Bergmann if (!fake_bridge) { 108135ba63b8SArnd Bergmann retval = -ENOMEM; 108235ba63b8SArnd Bergmann goto err_struct; 108335ba63b8SArnd Bergmann } 108435ba63b8SArnd Bergmann 108535ba63b8SArnd Bergmann fake_device = kzalloc(sizeof(*fake_device), GFP_KERNEL); 108635ba63b8SArnd Bergmann if (!fake_device) { 108735ba63b8SArnd Bergmann retval = -ENOMEM; 108835ba63b8SArnd Bergmann goto err_driver; 108935ba63b8SArnd Bergmann } 109035ba63b8SArnd Bergmann 109135ba63b8SArnd Bergmann fake_bridge->driver_priv = fake_device; 109235ba63b8SArnd Bergmann 109335ba63b8SArnd Bergmann fake_bridge->parent = vme_root; 109435ba63b8SArnd Bergmann 109535ba63b8SArnd Bergmann fake_device->parent = fake_bridge; 109635ba63b8SArnd Bergmann 109735ba63b8SArnd Bergmann /* Initialize wait queues & mutual exclusion flags */ 109835ba63b8SArnd Bergmann mutex_init(&fake_device->vme_int); 109935ba63b8SArnd Bergmann mutex_init(&fake_bridge->irq_mtx); 110035ba63b8SArnd Bergmann tasklet_init(&fake_device->int_tasklet, fake_VIRQ_tasklet, 110135ba63b8SArnd Bergmann (unsigned long) fake_bridge); 110235ba63b8SArnd Bergmann 110335ba63b8SArnd Bergmann strcpy(fake_bridge->name, driver_name); 110435ba63b8SArnd Bergmann 110535ba63b8SArnd Bergmann /* Add master windows to list */ 110635ba63b8SArnd Bergmann INIT_LIST_HEAD(&fake_bridge->master_resources); 110735ba63b8SArnd Bergmann for (i = 0; i < FAKE_MAX_MASTER; i++) { 110835ba63b8SArnd Bergmann master_image = kmalloc(sizeof(*master_image), GFP_KERNEL); 110935ba63b8SArnd Bergmann if (!master_image) { 111035ba63b8SArnd Bergmann retval = -ENOMEM; 111135ba63b8SArnd Bergmann goto err_master; 111235ba63b8SArnd Bergmann } 111335ba63b8SArnd Bergmann master_image->parent = fake_bridge; 111435ba63b8SArnd Bergmann spin_lock_init(&master_image->lock); 111535ba63b8SArnd Bergmann master_image->locked = 0; 111635ba63b8SArnd Bergmann master_image->number = i; 111735ba63b8SArnd Bergmann master_image->address_attr = VME_A16 | VME_A24 | VME_A32 | 111835ba63b8SArnd Bergmann VME_A64; 111935ba63b8SArnd Bergmann master_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT | 112035ba63b8SArnd Bergmann VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 | 112135ba63b8SArnd Bergmann VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER | 112235ba63b8SArnd Bergmann VME_PROG | VME_DATA; 112335ba63b8SArnd Bergmann master_image->width_attr = VME_D16 | VME_D32; 112435ba63b8SArnd Bergmann memset(&master_image->bus_resource, 0, 112535ba63b8SArnd Bergmann sizeof(struct resource)); 112635ba63b8SArnd Bergmann master_image->kern_base = NULL; 112735ba63b8SArnd Bergmann list_add_tail(&master_image->list, 112835ba63b8SArnd Bergmann &fake_bridge->master_resources); 112935ba63b8SArnd Bergmann } 113035ba63b8SArnd Bergmann 113135ba63b8SArnd Bergmann /* Add slave windows to list */ 113235ba63b8SArnd Bergmann INIT_LIST_HEAD(&fake_bridge->slave_resources); 113335ba63b8SArnd Bergmann for (i = 0; i < FAKE_MAX_SLAVE; i++) { 113435ba63b8SArnd Bergmann slave_image = kmalloc(sizeof(*slave_image), GFP_KERNEL); 113535ba63b8SArnd Bergmann if (!slave_image) { 113635ba63b8SArnd Bergmann retval = -ENOMEM; 113735ba63b8SArnd Bergmann goto err_slave; 113835ba63b8SArnd Bergmann } 113935ba63b8SArnd Bergmann slave_image->parent = fake_bridge; 114035ba63b8SArnd Bergmann mutex_init(&slave_image->mtx); 114135ba63b8SArnd Bergmann slave_image->locked = 0; 114235ba63b8SArnd Bergmann slave_image->number = i; 114335ba63b8SArnd Bergmann slave_image->address_attr = VME_A16 | VME_A24 | VME_A32 | 114435ba63b8SArnd Bergmann VME_A64 | VME_CRCSR | VME_USER1 | VME_USER2 | 114535ba63b8SArnd Bergmann VME_USER3 | VME_USER4; 114635ba63b8SArnd Bergmann slave_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT | 114735ba63b8SArnd Bergmann VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 | 114835ba63b8SArnd Bergmann VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER | 114935ba63b8SArnd Bergmann VME_PROG | VME_DATA; 115035ba63b8SArnd Bergmann list_add_tail(&slave_image->list, 115135ba63b8SArnd Bergmann &fake_bridge->slave_resources); 115235ba63b8SArnd Bergmann } 115335ba63b8SArnd Bergmann 115435ba63b8SArnd Bergmann /* Add location monitor to list */ 115535ba63b8SArnd Bergmann INIT_LIST_HEAD(&fake_bridge->lm_resources); 115635ba63b8SArnd Bergmann lm = kmalloc(sizeof(*lm), GFP_KERNEL); 115735ba63b8SArnd Bergmann if (!lm) { 115835ba63b8SArnd Bergmann retval = -ENOMEM; 115935ba63b8SArnd Bergmann goto err_lm; 116035ba63b8SArnd Bergmann } 116135ba63b8SArnd Bergmann lm->parent = fake_bridge; 116235ba63b8SArnd Bergmann mutex_init(&lm->mtx); 116335ba63b8SArnd Bergmann lm->locked = 0; 116435ba63b8SArnd Bergmann lm->number = 1; 116535ba63b8SArnd Bergmann lm->monitors = 4; 116635ba63b8SArnd Bergmann list_add_tail(&lm->list, &fake_bridge->lm_resources); 116735ba63b8SArnd Bergmann 116835ba63b8SArnd Bergmann fake_bridge->slave_get = fake_slave_get; 116935ba63b8SArnd Bergmann fake_bridge->slave_set = fake_slave_set; 117035ba63b8SArnd Bergmann fake_bridge->master_get = fake_master_get; 117135ba63b8SArnd Bergmann fake_bridge->master_set = fake_master_set; 117235ba63b8SArnd Bergmann fake_bridge->master_read = fake_master_read; 117335ba63b8SArnd Bergmann fake_bridge->master_write = fake_master_write; 117435ba63b8SArnd Bergmann fake_bridge->master_rmw = fake_master_rmw; 117535ba63b8SArnd Bergmann fake_bridge->irq_set = fake_irq_set; 117635ba63b8SArnd Bergmann fake_bridge->irq_generate = fake_irq_generate; 117735ba63b8SArnd Bergmann fake_bridge->lm_set = fake_lm_set; 117835ba63b8SArnd Bergmann fake_bridge->lm_get = fake_lm_get; 117935ba63b8SArnd Bergmann fake_bridge->lm_attach = fake_lm_attach; 118035ba63b8SArnd Bergmann fake_bridge->lm_detach = fake_lm_detach; 118135ba63b8SArnd Bergmann fake_bridge->slot_get = fake_slot_get; 118235ba63b8SArnd Bergmann fake_bridge->alloc_consistent = fake_alloc_consistent; 118335ba63b8SArnd Bergmann fake_bridge->free_consistent = fake_free_consistent; 118435ba63b8SArnd Bergmann 118535ba63b8SArnd Bergmann pr_info("Board is%s the VME system controller\n", 118635ba63b8SArnd Bergmann (geoid == 1) ? "" : " not"); 118735ba63b8SArnd Bergmann 118835ba63b8SArnd Bergmann pr_info("VME geographical address is set to %d\n", geoid); 118935ba63b8SArnd Bergmann 119035ba63b8SArnd Bergmann retval = fake_crcsr_init(fake_bridge); 119135ba63b8SArnd Bergmann if (retval) { 119235ba63b8SArnd Bergmann pr_err("CR/CSR configuration failed.\n"); 119335ba63b8SArnd Bergmann goto err_crcsr; 119435ba63b8SArnd Bergmann } 119535ba63b8SArnd Bergmann 119635ba63b8SArnd Bergmann retval = vme_register_bridge(fake_bridge); 119735ba63b8SArnd Bergmann if (retval != 0) { 119835ba63b8SArnd Bergmann pr_err("Chip Registration failed.\n"); 119935ba63b8SArnd Bergmann goto err_reg; 120035ba63b8SArnd Bergmann } 120135ba63b8SArnd Bergmann 120235ba63b8SArnd Bergmann exit_pointer = fake_bridge; 120335ba63b8SArnd Bergmann 120435ba63b8SArnd Bergmann return 0; 120535ba63b8SArnd Bergmann 120635ba63b8SArnd Bergmann err_reg: 120735ba63b8SArnd Bergmann fake_crcsr_exit(fake_bridge); 120835ba63b8SArnd Bergmann err_crcsr: 120935ba63b8SArnd Bergmann err_lm: 121035ba63b8SArnd Bergmann /* resources are stored in link list */ 121135ba63b8SArnd Bergmann list_for_each_safe(pos, n, &fake_bridge->lm_resources) { 121235ba63b8SArnd Bergmann lm = list_entry(pos, struct vme_lm_resource, list); 121335ba63b8SArnd Bergmann list_del(pos); 121435ba63b8SArnd Bergmann kfree(lm); 121535ba63b8SArnd Bergmann } 121635ba63b8SArnd Bergmann err_slave: 121735ba63b8SArnd Bergmann /* resources are stored in link list */ 121835ba63b8SArnd Bergmann list_for_each_safe(pos, n, &fake_bridge->slave_resources) { 121935ba63b8SArnd Bergmann slave_image = list_entry(pos, struct vme_slave_resource, list); 122035ba63b8SArnd Bergmann list_del(pos); 122135ba63b8SArnd Bergmann kfree(slave_image); 122235ba63b8SArnd Bergmann } 122335ba63b8SArnd Bergmann err_master: 122435ba63b8SArnd Bergmann /* resources are stored in link list */ 122535ba63b8SArnd Bergmann list_for_each_safe(pos, n, &fake_bridge->master_resources) { 122635ba63b8SArnd Bergmann master_image = list_entry(pos, struct vme_master_resource, 122735ba63b8SArnd Bergmann list); 122835ba63b8SArnd Bergmann list_del(pos); 122935ba63b8SArnd Bergmann kfree(master_image); 123035ba63b8SArnd Bergmann } 123135ba63b8SArnd Bergmann 123235ba63b8SArnd Bergmann kfree(fake_device); 123335ba63b8SArnd Bergmann err_driver: 123435ba63b8SArnd Bergmann kfree(fake_bridge); 123535ba63b8SArnd Bergmann err_struct: 123635ba63b8SArnd Bergmann return retval; 123735ba63b8SArnd Bergmann 123835ba63b8SArnd Bergmann } 123935ba63b8SArnd Bergmann 124035ba63b8SArnd Bergmann static void __exit fake_exit(void) 124135ba63b8SArnd Bergmann { 124235ba63b8SArnd Bergmann struct list_head *pos = NULL; 124335ba63b8SArnd Bergmann struct list_head *tmplist; 124435ba63b8SArnd Bergmann struct vme_master_resource *master_image; 124535ba63b8SArnd Bergmann struct vme_slave_resource *slave_image; 124635ba63b8SArnd Bergmann int i; 124735ba63b8SArnd Bergmann struct vme_bridge *fake_bridge; 124835ba63b8SArnd Bergmann struct fake_driver *bridge; 124935ba63b8SArnd Bergmann 125035ba63b8SArnd Bergmann fake_bridge = exit_pointer; 125135ba63b8SArnd Bergmann 125235ba63b8SArnd Bergmann bridge = fake_bridge->driver_priv; 125335ba63b8SArnd Bergmann 125435ba63b8SArnd Bergmann pr_debug("Driver is being unloaded.\n"); 125535ba63b8SArnd Bergmann 125635ba63b8SArnd Bergmann /* 125735ba63b8SArnd Bergmann * Shutdown all inbound and outbound windows. 125835ba63b8SArnd Bergmann */ 125935ba63b8SArnd Bergmann for (i = 0; i < FAKE_MAX_MASTER; i++) 126035ba63b8SArnd Bergmann bridge->masters[i].enabled = 0; 126135ba63b8SArnd Bergmann 126235ba63b8SArnd Bergmann for (i = 0; i < FAKE_MAX_SLAVE; i++) 126335ba63b8SArnd Bergmann bridge->slaves[i].enabled = 0; 126435ba63b8SArnd Bergmann 126535ba63b8SArnd Bergmann /* 126635ba63b8SArnd Bergmann * Shutdown Location monitor. 126735ba63b8SArnd Bergmann */ 126835ba63b8SArnd Bergmann bridge->lm_enabled = 0; 126935ba63b8SArnd Bergmann 127035ba63b8SArnd Bergmann vme_unregister_bridge(fake_bridge); 127135ba63b8SArnd Bergmann 127235ba63b8SArnd Bergmann fake_crcsr_exit(fake_bridge); 127335ba63b8SArnd Bergmann /* resources are stored in link list */ 127435ba63b8SArnd Bergmann list_for_each_safe(pos, tmplist, &fake_bridge->slave_resources) { 127535ba63b8SArnd Bergmann slave_image = list_entry(pos, struct vme_slave_resource, list); 127635ba63b8SArnd Bergmann list_del(pos); 127735ba63b8SArnd Bergmann kfree(slave_image); 127835ba63b8SArnd Bergmann } 127935ba63b8SArnd Bergmann 128035ba63b8SArnd Bergmann /* resources are stored in link list */ 128135ba63b8SArnd Bergmann list_for_each_safe(pos, tmplist, &fake_bridge->master_resources) { 128235ba63b8SArnd Bergmann master_image = list_entry(pos, struct vme_master_resource, 128335ba63b8SArnd Bergmann list); 128435ba63b8SArnd Bergmann list_del(pos); 128535ba63b8SArnd Bergmann kfree(master_image); 128635ba63b8SArnd Bergmann } 128735ba63b8SArnd Bergmann 128835ba63b8SArnd Bergmann kfree(fake_bridge->driver_priv); 128935ba63b8SArnd Bergmann 129035ba63b8SArnd Bergmann kfree(fake_bridge); 129135ba63b8SArnd Bergmann 129235ba63b8SArnd Bergmann root_device_unregister(vme_root); 129335ba63b8SArnd Bergmann } 129435ba63b8SArnd Bergmann 129535ba63b8SArnd Bergmann MODULE_PARM_DESC(geoid, "Set geographical addressing"); 129635ba63b8SArnd Bergmann module_param(geoid, int, 0); 129735ba63b8SArnd Bergmann 129835ba63b8SArnd Bergmann MODULE_DESCRIPTION("Fake VME bridge driver"); 129935ba63b8SArnd Bergmann MODULE_LICENSE("GPL"); 130035ba63b8SArnd Bergmann 130135ba63b8SArnd Bergmann module_init(fake_init); 130235ba63b8SArnd Bergmann module_exit(fake_exit); 1303