18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Fake VME bridge support.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This drive provides a fake VME bridge chip, this enables debugging of the
68c2ecf20Sopenharmony_ci * VME framework in the absence of a VME system.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This driver has to do a number of things in software that would be driven
98c2ecf20Sopenharmony_ci * by hardware if it was available, it will also result in extra overhead at
108c2ecf20Sopenharmony_ci * times when compared with driving actual hardware.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Author: Martyn Welch <martyn@welches.me.uk>
138c2ecf20Sopenharmony_ci * Copyright (c) 2014 Martyn Welch
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * Based on vme_tsi148.c:
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Author: Martyn Welch <martyn.welch@ge.com>
188c2ecf20Sopenharmony_ci * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * Based on work by Tom Armistead and Ajit Prem
218c2ecf20Sopenharmony_ci * Copyright 2004 Motorola Inc.
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <linux/device.h>
258c2ecf20Sopenharmony_ci#include <linux/errno.h>
268c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
278c2ecf20Sopenharmony_ci#include <linux/module.h>
288c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
298c2ecf20Sopenharmony_ci#include <linux/slab.h>
308c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
318c2ecf20Sopenharmony_ci#include <linux/types.h>
328c2ecf20Sopenharmony_ci#include <linux/vme.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include "../vme_bridge.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/*
378c2ecf20Sopenharmony_ci *  Define the number of each that the fake driver supports.
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_ci#define FAKE_MAX_MASTER		8	/* Max Master Windows */
408c2ecf20Sopenharmony_ci#define FAKE_MAX_SLAVE		8	/* Max Slave Windows */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* Structures to hold information normally held in device registers */
438c2ecf20Sopenharmony_cistruct fake_slave_window {
448c2ecf20Sopenharmony_ci	int enabled;
458c2ecf20Sopenharmony_ci	unsigned long long vme_base;
468c2ecf20Sopenharmony_ci	unsigned long long size;
478c2ecf20Sopenharmony_ci	void *buf_base;
488c2ecf20Sopenharmony_ci	u32 aspace;
498c2ecf20Sopenharmony_ci	u32 cycle;
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistruct fake_master_window {
538c2ecf20Sopenharmony_ci	int enabled;
548c2ecf20Sopenharmony_ci	unsigned long long vme_base;
558c2ecf20Sopenharmony_ci	unsigned long long size;
568c2ecf20Sopenharmony_ci	u32 aspace;
578c2ecf20Sopenharmony_ci	u32 cycle;
588c2ecf20Sopenharmony_ci	u32 dwidth;
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* Structure used to hold driver specific information */
628c2ecf20Sopenharmony_cistruct fake_driver {
638c2ecf20Sopenharmony_ci	struct vme_bridge *parent;
648c2ecf20Sopenharmony_ci	struct fake_slave_window slaves[FAKE_MAX_SLAVE];
658c2ecf20Sopenharmony_ci	struct fake_master_window masters[FAKE_MAX_MASTER];
668c2ecf20Sopenharmony_ci	u32 lm_enabled;
678c2ecf20Sopenharmony_ci	unsigned long long lm_base;
688c2ecf20Sopenharmony_ci	u32 lm_aspace;
698c2ecf20Sopenharmony_ci	u32 lm_cycle;
708c2ecf20Sopenharmony_ci	void (*lm_callback[4])(void *);
718c2ecf20Sopenharmony_ci	void *lm_data[4];
728c2ecf20Sopenharmony_ci	struct tasklet_struct int_tasklet;
738c2ecf20Sopenharmony_ci	int int_level;
748c2ecf20Sopenharmony_ci	int int_statid;
758c2ecf20Sopenharmony_ci	void *crcsr_kernel;
768c2ecf20Sopenharmony_ci	dma_addr_t crcsr_bus;
778c2ecf20Sopenharmony_ci	/* Only one VME interrupt can be generated at a time, provide locking */
788c2ecf20Sopenharmony_ci	struct mutex vme_int;
798c2ecf20Sopenharmony_ci};
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* Module parameter */
828c2ecf20Sopenharmony_cistatic int geoid;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic const char driver_name[] = "vme_fake";
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic struct vme_bridge *exit_pointer;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic struct device *vme_root;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/*
918c2ecf20Sopenharmony_ci * Calling VME bus interrupt callback if provided.
928c2ecf20Sopenharmony_ci */
938c2ecf20Sopenharmony_cistatic void fake_VIRQ_tasklet(unsigned long data)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct vme_bridge *fake_bridge;
968c2ecf20Sopenharmony_ci	struct fake_driver *bridge;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	fake_bridge = (struct vme_bridge *) data;
998c2ecf20Sopenharmony_ci	bridge = fake_bridge->driver_priv;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	vme_irq_handler(fake_bridge, bridge->int_level, bridge->int_statid);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/*
1058c2ecf20Sopenharmony_ci * Configure VME interrupt
1068c2ecf20Sopenharmony_ci */
1078c2ecf20Sopenharmony_cistatic void fake_irq_set(struct vme_bridge *fake_bridge, int level,
1088c2ecf20Sopenharmony_ci		int state, int sync)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	/* Nothing to do */
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic void *fake_pci_to_ptr(dma_addr_t addr)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	return (void *)(uintptr_t)addr;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic dma_addr_t fake_ptr_to_pci(void *addr)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	return (dma_addr_t)(uintptr_t)addr;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/*
1248c2ecf20Sopenharmony_ci * Generate a VME bus interrupt at the requested level & vector. Wait for
1258c2ecf20Sopenharmony_ci * interrupt to be acked.
1268c2ecf20Sopenharmony_ci */
1278c2ecf20Sopenharmony_cistatic int fake_irq_generate(struct vme_bridge *fake_bridge, int level,
1288c2ecf20Sopenharmony_ci		int statid)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct fake_driver *bridge;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	bridge = fake_bridge->driver_priv;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	mutex_lock(&bridge->vme_int);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	bridge->int_level = level;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	bridge->int_statid = statid;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/*
1418c2ecf20Sopenharmony_ci	 * Schedule tasklet to run VME handler to emulate normal VME interrupt
1428c2ecf20Sopenharmony_ci	 * handler behaviour.
1438c2ecf20Sopenharmony_ci	 */
1448c2ecf20Sopenharmony_ci	tasklet_schedule(&bridge->int_tasklet);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	mutex_unlock(&bridge->vme_int);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return 0;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/*
1528c2ecf20Sopenharmony_ci * Initialize a slave window with the requested attributes.
1538c2ecf20Sopenharmony_ci */
1548c2ecf20Sopenharmony_cistatic int fake_slave_set(struct vme_slave_resource *image, int enabled,
1558c2ecf20Sopenharmony_ci		unsigned long long vme_base, unsigned long long size,
1568c2ecf20Sopenharmony_ci		dma_addr_t buf_base, u32 aspace, u32 cycle)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	unsigned int i, granularity = 0;
1598c2ecf20Sopenharmony_ci	unsigned long long vme_bound;
1608c2ecf20Sopenharmony_ci	struct vme_bridge *fake_bridge;
1618c2ecf20Sopenharmony_ci	struct fake_driver *bridge;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	fake_bridge = image->parent;
1648c2ecf20Sopenharmony_ci	bridge = fake_bridge->driver_priv;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	i = image->number;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	switch (aspace) {
1698c2ecf20Sopenharmony_ci	case VME_A16:
1708c2ecf20Sopenharmony_ci		granularity = 0x10;
1718c2ecf20Sopenharmony_ci		break;
1728c2ecf20Sopenharmony_ci	case VME_A24:
1738c2ecf20Sopenharmony_ci		granularity = 0x1000;
1748c2ecf20Sopenharmony_ci		break;
1758c2ecf20Sopenharmony_ci	case VME_A32:
1768c2ecf20Sopenharmony_ci		granularity = 0x10000;
1778c2ecf20Sopenharmony_ci		break;
1788c2ecf20Sopenharmony_ci	case VME_A64:
1798c2ecf20Sopenharmony_ci		granularity = 0x10000;
1808c2ecf20Sopenharmony_ci		break;
1818c2ecf20Sopenharmony_ci	case VME_CRCSR:
1828c2ecf20Sopenharmony_ci	case VME_USER1:
1838c2ecf20Sopenharmony_ci	case VME_USER2:
1848c2ecf20Sopenharmony_ci	case VME_USER3:
1858c2ecf20Sopenharmony_ci	case VME_USER4:
1868c2ecf20Sopenharmony_ci	default:
1878c2ecf20Sopenharmony_ci		pr_err("Invalid address space\n");
1888c2ecf20Sopenharmony_ci		return -EINVAL;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	/*
1928c2ecf20Sopenharmony_ci	 * Bound address is a valid address for the window, adjust
1938c2ecf20Sopenharmony_ci	 * accordingly
1948c2ecf20Sopenharmony_ci	 */
1958c2ecf20Sopenharmony_ci	vme_bound = vme_base + size - granularity;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (vme_base & (granularity - 1)) {
1988c2ecf20Sopenharmony_ci		pr_err("Invalid VME base alignment\n");
1998c2ecf20Sopenharmony_ci		return -EINVAL;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci	if (vme_bound & (granularity - 1)) {
2028c2ecf20Sopenharmony_ci		pr_err("Invalid VME bound alignment\n");
2038c2ecf20Sopenharmony_ci		return -EINVAL;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	mutex_lock(&image->mtx);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	bridge->slaves[i].enabled = enabled;
2098c2ecf20Sopenharmony_ci	bridge->slaves[i].vme_base = vme_base;
2108c2ecf20Sopenharmony_ci	bridge->slaves[i].size = size;
2118c2ecf20Sopenharmony_ci	bridge->slaves[i].buf_base = fake_pci_to_ptr(buf_base);
2128c2ecf20Sopenharmony_ci	bridge->slaves[i].aspace = aspace;
2138c2ecf20Sopenharmony_ci	bridge->slaves[i].cycle = cycle;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	mutex_unlock(&image->mtx);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	return 0;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci/*
2218c2ecf20Sopenharmony_ci * Get slave window configuration.
2228c2ecf20Sopenharmony_ci */
2238c2ecf20Sopenharmony_cistatic int fake_slave_get(struct vme_slave_resource *image, int *enabled,
2248c2ecf20Sopenharmony_ci		unsigned long long *vme_base, unsigned long long *size,
2258c2ecf20Sopenharmony_ci		dma_addr_t *buf_base, u32 *aspace, u32 *cycle)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	unsigned int i;
2288c2ecf20Sopenharmony_ci	struct fake_driver *bridge;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	bridge = image->parent->driver_priv;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	i = image->number;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	mutex_lock(&image->mtx);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	*enabled = bridge->slaves[i].enabled;
2378c2ecf20Sopenharmony_ci	*vme_base = bridge->slaves[i].vme_base;
2388c2ecf20Sopenharmony_ci	*size = bridge->slaves[i].size;
2398c2ecf20Sopenharmony_ci	*buf_base = fake_ptr_to_pci(bridge->slaves[i].buf_base);
2408c2ecf20Sopenharmony_ci	*aspace = bridge->slaves[i].aspace;
2418c2ecf20Sopenharmony_ci	*cycle = bridge->slaves[i].cycle;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	mutex_unlock(&image->mtx);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return 0;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci/*
2498c2ecf20Sopenharmony_ci * Set the attributes of an outbound window.
2508c2ecf20Sopenharmony_ci */
2518c2ecf20Sopenharmony_cistatic int fake_master_set(struct vme_master_resource *image, int enabled,
2528c2ecf20Sopenharmony_ci		unsigned long long vme_base, unsigned long long size,
2538c2ecf20Sopenharmony_ci		u32 aspace, u32 cycle, u32 dwidth)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	int retval = 0;
2568c2ecf20Sopenharmony_ci	unsigned int i;
2578c2ecf20Sopenharmony_ci	struct vme_bridge *fake_bridge;
2588c2ecf20Sopenharmony_ci	struct fake_driver *bridge;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	fake_bridge = image->parent;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	bridge = fake_bridge->driver_priv;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	/* Verify input data */
2658c2ecf20Sopenharmony_ci	if (vme_base & 0xFFFF) {
2668c2ecf20Sopenharmony_ci		pr_err("Invalid VME Window alignment\n");
2678c2ecf20Sopenharmony_ci		retval = -EINVAL;
2688c2ecf20Sopenharmony_ci		goto err_window;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (size & 0xFFFF) {
2728c2ecf20Sopenharmony_ci		pr_err("Invalid size alignment\n");
2738c2ecf20Sopenharmony_ci		retval = -EINVAL;
2748c2ecf20Sopenharmony_ci		goto err_window;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if ((size == 0) && (enabled != 0)) {
2788c2ecf20Sopenharmony_ci		pr_err("Size must be non-zero for enabled windows\n");
2798c2ecf20Sopenharmony_ci		retval = -EINVAL;
2808c2ecf20Sopenharmony_ci		goto err_window;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/* Setup data width */
2848c2ecf20Sopenharmony_ci	switch (dwidth) {
2858c2ecf20Sopenharmony_ci	case VME_D8:
2868c2ecf20Sopenharmony_ci	case VME_D16:
2878c2ecf20Sopenharmony_ci	case VME_D32:
2888c2ecf20Sopenharmony_ci		break;
2898c2ecf20Sopenharmony_ci	default:
2908c2ecf20Sopenharmony_ci		pr_err("Invalid data width\n");
2918c2ecf20Sopenharmony_ci		retval = -EINVAL;
2928c2ecf20Sopenharmony_ci		goto err_dwidth;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/* Setup address space */
2968c2ecf20Sopenharmony_ci	switch (aspace) {
2978c2ecf20Sopenharmony_ci	case VME_A16:
2988c2ecf20Sopenharmony_ci	case VME_A24:
2998c2ecf20Sopenharmony_ci	case VME_A32:
3008c2ecf20Sopenharmony_ci	case VME_A64:
3018c2ecf20Sopenharmony_ci	case VME_CRCSR:
3028c2ecf20Sopenharmony_ci	case VME_USER1:
3038c2ecf20Sopenharmony_ci	case VME_USER2:
3048c2ecf20Sopenharmony_ci	case VME_USER3:
3058c2ecf20Sopenharmony_ci	case VME_USER4:
3068c2ecf20Sopenharmony_ci		break;
3078c2ecf20Sopenharmony_ci	default:
3088c2ecf20Sopenharmony_ci		pr_err("Invalid address space\n");
3098c2ecf20Sopenharmony_ci		retval = -EINVAL;
3108c2ecf20Sopenharmony_ci		goto err_aspace;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	spin_lock(&image->lock);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	i = image->number;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	bridge->masters[i].enabled = enabled;
3188c2ecf20Sopenharmony_ci	bridge->masters[i].vme_base = vme_base;
3198c2ecf20Sopenharmony_ci	bridge->masters[i].size = size;
3208c2ecf20Sopenharmony_ci	bridge->masters[i].aspace = aspace;
3218c2ecf20Sopenharmony_ci	bridge->masters[i].cycle = cycle;
3228c2ecf20Sopenharmony_ci	bridge->masters[i].dwidth = dwidth;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	spin_unlock(&image->lock);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	return 0;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cierr_aspace:
3298c2ecf20Sopenharmony_cierr_dwidth:
3308c2ecf20Sopenharmony_cierr_window:
3318c2ecf20Sopenharmony_ci	return retval;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci/*
3368c2ecf20Sopenharmony_ci * Set the attributes of an outbound window.
3378c2ecf20Sopenharmony_ci */
3388c2ecf20Sopenharmony_cistatic int __fake_master_get(struct vme_master_resource *image, int *enabled,
3398c2ecf20Sopenharmony_ci		unsigned long long *vme_base, unsigned long long *size,
3408c2ecf20Sopenharmony_ci		u32 *aspace, u32 *cycle, u32 *dwidth)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	unsigned int i;
3438c2ecf20Sopenharmony_ci	struct fake_driver *bridge;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	bridge = image->parent->driver_priv;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	i = image->number;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	*enabled = bridge->masters[i].enabled;
3508c2ecf20Sopenharmony_ci	*vme_base = bridge->masters[i].vme_base;
3518c2ecf20Sopenharmony_ci	*size = bridge->masters[i].size;
3528c2ecf20Sopenharmony_ci	*aspace = bridge->masters[i].aspace;
3538c2ecf20Sopenharmony_ci	*cycle = bridge->masters[i].cycle;
3548c2ecf20Sopenharmony_ci	*dwidth = bridge->masters[i].dwidth;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	return 0;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int fake_master_get(struct vme_master_resource *image, int *enabled,
3618c2ecf20Sopenharmony_ci		unsigned long long *vme_base, unsigned long long *size,
3628c2ecf20Sopenharmony_ci		u32 *aspace, u32 *cycle, u32 *dwidth)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	int retval;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	spin_lock(&image->lock);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	retval = __fake_master_get(image, enabled, vme_base, size, aspace,
3698c2ecf20Sopenharmony_ci			cycle, dwidth);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	spin_unlock(&image->lock);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	return retval;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic void fake_lm_check(struct fake_driver *bridge, unsigned long long addr,
3788c2ecf20Sopenharmony_ci			  u32 aspace, u32 cycle)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct vme_bridge *fake_bridge;
3818c2ecf20Sopenharmony_ci	unsigned long long lm_base;
3828c2ecf20Sopenharmony_ci	u32 lm_aspace, lm_cycle;
3838c2ecf20Sopenharmony_ci	int i;
3848c2ecf20Sopenharmony_ci	struct vme_lm_resource *lm;
3858c2ecf20Sopenharmony_ci	struct list_head *pos = NULL, *n;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	/* Get vme_bridge */
3888c2ecf20Sopenharmony_ci	fake_bridge = bridge->parent;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	/* Loop through each location monitor resource */
3918c2ecf20Sopenharmony_ci	list_for_each_safe(pos, n, &fake_bridge->lm_resources) {
3928c2ecf20Sopenharmony_ci		lm = list_entry(pos, struct vme_lm_resource, list);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci		/* If disabled, we're done */
3958c2ecf20Sopenharmony_ci		if (bridge->lm_enabled == 0)
3968c2ecf20Sopenharmony_ci			return;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci		lm_base = bridge->lm_base;
3998c2ecf20Sopenharmony_ci		lm_aspace = bridge->lm_aspace;
4008c2ecf20Sopenharmony_ci		lm_cycle = bridge->lm_cycle;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci		/* First make sure that the cycle and address space match */
4038c2ecf20Sopenharmony_ci		if ((lm_aspace == aspace) && (lm_cycle == cycle)) {
4048c2ecf20Sopenharmony_ci			for (i = 0; i < lm->monitors; i++) {
4058c2ecf20Sopenharmony_ci				/* Each location monitor covers 8 bytes */
4068c2ecf20Sopenharmony_ci				if (((lm_base + (8 * i)) <= addr) &&
4078c2ecf20Sopenharmony_ci				    ((lm_base + (8 * i) + 8) > addr)) {
4088c2ecf20Sopenharmony_ci					if (bridge->lm_callback[i])
4098c2ecf20Sopenharmony_ci						bridge->lm_callback[i](
4108c2ecf20Sopenharmony_ci							bridge->lm_data[i]);
4118c2ecf20Sopenharmony_ci				}
4128c2ecf20Sopenharmony_ci			}
4138c2ecf20Sopenharmony_ci		}
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic noinline_for_stack u8 fake_vmeread8(struct fake_driver *bridge,
4188c2ecf20Sopenharmony_ci					   unsigned long long addr,
4198c2ecf20Sopenharmony_ci					   u32 aspace, u32 cycle)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	u8 retval = 0xff;
4228c2ecf20Sopenharmony_ci	int i;
4238c2ecf20Sopenharmony_ci	unsigned long long start, end, offset;
4248c2ecf20Sopenharmony_ci	u8 *loc;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	for (i = 0; i < FAKE_MAX_SLAVE; i++) {
4278c2ecf20Sopenharmony_ci		start = bridge->slaves[i].vme_base;
4288c2ecf20Sopenharmony_ci		end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci		if (aspace != bridge->slaves[i].aspace)
4318c2ecf20Sopenharmony_ci			continue;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci		if (cycle != bridge->slaves[i].cycle)
4348c2ecf20Sopenharmony_ci			continue;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci		if ((addr >= start) && (addr < end)) {
4378c2ecf20Sopenharmony_ci			offset = addr - bridge->slaves[i].vme_base;
4388c2ecf20Sopenharmony_ci			loc = (u8 *)(bridge->slaves[i].buf_base + offset);
4398c2ecf20Sopenharmony_ci			retval = *loc;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci			break;
4428c2ecf20Sopenharmony_ci		}
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	fake_lm_check(bridge, addr, aspace, cycle);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	return retval;
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic noinline_for_stack u16 fake_vmeread16(struct fake_driver *bridge,
4518c2ecf20Sopenharmony_ci					     unsigned long long addr,
4528c2ecf20Sopenharmony_ci					     u32 aspace, u32 cycle)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	u16 retval = 0xffff;
4558c2ecf20Sopenharmony_ci	int i;
4568c2ecf20Sopenharmony_ci	unsigned long long start, end, offset;
4578c2ecf20Sopenharmony_ci	u16 *loc;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	for (i = 0; i < FAKE_MAX_SLAVE; i++) {
4608c2ecf20Sopenharmony_ci		if (aspace != bridge->slaves[i].aspace)
4618c2ecf20Sopenharmony_ci			continue;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci		if (cycle != bridge->slaves[i].cycle)
4648c2ecf20Sopenharmony_ci			continue;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		start = bridge->slaves[i].vme_base;
4678c2ecf20Sopenharmony_ci		end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci		if ((addr >= start) && ((addr + 1) < end)) {
4708c2ecf20Sopenharmony_ci			offset = addr - bridge->slaves[i].vme_base;
4718c2ecf20Sopenharmony_ci			loc = (u16 *)(bridge->slaves[i].buf_base + offset);
4728c2ecf20Sopenharmony_ci			retval = *loc;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci			break;
4758c2ecf20Sopenharmony_ci		}
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	fake_lm_check(bridge, addr, aspace, cycle);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	return retval;
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic noinline_for_stack u32 fake_vmeread32(struct fake_driver *bridge,
4848c2ecf20Sopenharmony_ci					     unsigned long long addr,
4858c2ecf20Sopenharmony_ci					     u32 aspace, u32 cycle)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	u32 retval = 0xffffffff;
4888c2ecf20Sopenharmony_ci	int i;
4898c2ecf20Sopenharmony_ci	unsigned long long start, end, offset;
4908c2ecf20Sopenharmony_ci	u32 *loc;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	for (i = 0; i < FAKE_MAX_SLAVE; i++) {
4938c2ecf20Sopenharmony_ci		if (aspace != bridge->slaves[i].aspace)
4948c2ecf20Sopenharmony_ci			continue;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci		if (cycle != bridge->slaves[i].cycle)
4978c2ecf20Sopenharmony_ci			continue;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci		start = bridge->slaves[i].vme_base;
5008c2ecf20Sopenharmony_ci		end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci		if ((addr >= start) && ((addr + 3) < end)) {
5038c2ecf20Sopenharmony_ci			offset = addr - bridge->slaves[i].vme_base;
5048c2ecf20Sopenharmony_ci			loc = (u32 *)(bridge->slaves[i].buf_base + offset);
5058c2ecf20Sopenharmony_ci			retval = *loc;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci			break;
5088c2ecf20Sopenharmony_ci		}
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	fake_lm_check(bridge, addr, aspace, cycle);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	return retval;
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic ssize_t fake_master_read(struct vme_master_resource *image, void *buf,
5178c2ecf20Sopenharmony_ci		size_t count, loff_t offset)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	int retval;
5208c2ecf20Sopenharmony_ci	u32 aspace, cycle, dwidth;
5218c2ecf20Sopenharmony_ci	struct vme_bridge *fake_bridge;
5228c2ecf20Sopenharmony_ci	struct fake_driver *priv;
5238c2ecf20Sopenharmony_ci	int i;
5248c2ecf20Sopenharmony_ci	unsigned long long addr;
5258c2ecf20Sopenharmony_ci	unsigned int done = 0;
5268c2ecf20Sopenharmony_ci	unsigned int count32;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	fake_bridge = image->parent;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	priv = fake_bridge->driver_priv;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	i = image->number;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	addr = (unsigned long long)priv->masters[i].vme_base + offset;
5358c2ecf20Sopenharmony_ci	aspace = priv->masters[i].aspace;
5368c2ecf20Sopenharmony_ci	cycle = priv->masters[i].cycle;
5378c2ecf20Sopenharmony_ci	dwidth = priv->masters[i].dwidth;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	spin_lock(&image->lock);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	/* The following code handles VME address alignment. We cannot use
5428c2ecf20Sopenharmony_ci	 * memcpy_xxx here because it may cut data transfers in to 8-bit
5438c2ecf20Sopenharmony_ci	 * cycles when D16 or D32 cycles are required on the VME bus.
5448c2ecf20Sopenharmony_ci	 * On the other hand, the bridge itself assures that the maximum data
5458c2ecf20Sopenharmony_ci	 * cycle configured for the transfer is used and splits it
5468c2ecf20Sopenharmony_ci	 * automatically for non-aligned addresses, so we don't want the
5478c2ecf20Sopenharmony_ci	 * overhead of needlessly forcing small transfers for the entire cycle.
5488c2ecf20Sopenharmony_ci	 */
5498c2ecf20Sopenharmony_ci	if (addr & 0x1) {
5508c2ecf20Sopenharmony_ci		*(u8 *)buf = fake_vmeread8(priv, addr, aspace, cycle);
5518c2ecf20Sopenharmony_ci		done += 1;
5528c2ecf20Sopenharmony_ci		if (done == count)
5538c2ecf20Sopenharmony_ci			goto out;
5548c2ecf20Sopenharmony_ci	}
5558c2ecf20Sopenharmony_ci	if ((dwidth == VME_D16) || (dwidth == VME_D32)) {
5568c2ecf20Sopenharmony_ci		if ((addr + done) & 0x2) {
5578c2ecf20Sopenharmony_ci			if ((count - done) < 2) {
5588c2ecf20Sopenharmony_ci				*(u8 *)(buf + done) = fake_vmeread8(priv,
5598c2ecf20Sopenharmony_ci						addr + done, aspace, cycle);
5608c2ecf20Sopenharmony_ci				done += 1;
5618c2ecf20Sopenharmony_ci				goto out;
5628c2ecf20Sopenharmony_ci			} else {
5638c2ecf20Sopenharmony_ci				*(u16 *)(buf + done) = fake_vmeread16(priv,
5648c2ecf20Sopenharmony_ci						addr + done, aspace, cycle);
5658c2ecf20Sopenharmony_ci				done += 2;
5668c2ecf20Sopenharmony_ci			}
5678c2ecf20Sopenharmony_ci		}
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (dwidth == VME_D32) {
5718c2ecf20Sopenharmony_ci		count32 = (count - done) & ~0x3;
5728c2ecf20Sopenharmony_ci		while (done < count32) {
5738c2ecf20Sopenharmony_ci			*(u32 *)(buf + done) = fake_vmeread32(priv, addr + done,
5748c2ecf20Sopenharmony_ci					aspace, cycle);
5758c2ecf20Sopenharmony_ci			done += 4;
5768c2ecf20Sopenharmony_ci		}
5778c2ecf20Sopenharmony_ci	} else if (dwidth == VME_D16) {
5788c2ecf20Sopenharmony_ci		count32 = (count - done) & ~0x3;
5798c2ecf20Sopenharmony_ci		while (done < count32) {
5808c2ecf20Sopenharmony_ci			*(u16 *)(buf + done) = fake_vmeread16(priv, addr + done,
5818c2ecf20Sopenharmony_ci					aspace, cycle);
5828c2ecf20Sopenharmony_ci			done += 2;
5838c2ecf20Sopenharmony_ci		}
5848c2ecf20Sopenharmony_ci	} else if (dwidth == VME_D8) {
5858c2ecf20Sopenharmony_ci		count32 = (count - done);
5868c2ecf20Sopenharmony_ci		while (done < count32) {
5878c2ecf20Sopenharmony_ci			*(u8 *)(buf + done) = fake_vmeread8(priv, addr + done,
5888c2ecf20Sopenharmony_ci					aspace, cycle);
5898c2ecf20Sopenharmony_ci			done += 1;
5908c2ecf20Sopenharmony_ci		}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if ((dwidth == VME_D16) || (dwidth == VME_D32)) {
5958c2ecf20Sopenharmony_ci		if ((count - done) & 0x2) {
5968c2ecf20Sopenharmony_ci			*(u16 *)(buf + done) = fake_vmeread16(priv, addr + done,
5978c2ecf20Sopenharmony_ci					aspace, cycle);
5988c2ecf20Sopenharmony_ci			done += 2;
5998c2ecf20Sopenharmony_ci		}
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci	if ((count - done) & 0x1) {
6028c2ecf20Sopenharmony_ci		*(u8 *)(buf + done) = fake_vmeread8(priv, addr + done, aspace,
6038c2ecf20Sopenharmony_ci				cycle);
6048c2ecf20Sopenharmony_ci		done += 1;
6058c2ecf20Sopenharmony_ci	}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ciout:
6088c2ecf20Sopenharmony_ci	retval = count;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	spin_unlock(&image->lock);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	return retval;
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic noinline_for_stack void fake_vmewrite8(struct fake_driver *bridge,
6168c2ecf20Sopenharmony_ci					      u8 *buf, unsigned long long addr,
6178c2ecf20Sopenharmony_ci					      u32 aspace, u32 cycle)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	int i;
6208c2ecf20Sopenharmony_ci	unsigned long long start, end, offset;
6218c2ecf20Sopenharmony_ci	u8 *loc;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	for (i = 0; i < FAKE_MAX_SLAVE; i++) {
6248c2ecf20Sopenharmony_ci		if (aspace != bridge->slaves[i].aspace)
6258c2ecf20Sopenharmony_ci			continue;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci		if (cycle != bridge->slaves[i].cycle)
6288c2ecf20Sopenharmony_ci			continue;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci		start = bridge->slaves[i].vme_base;
6318c2ecf20Sopenharmony_ci		end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci		if ((addr >= start) && (addr < end)) {
6348c2ecf20Sopenharmony_ci			offset = addr - bridge->slaves[i].vme_base;
6358c2ecf20Sopenharmony_ci			loc = (u8 *)((void *)bridge->slaves[i].buf_base + offset);
6368c2ecf20Sopenharmony_ci			*loc = *buf;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci			break;
6398c2ecf20Sopenharmony_ci		}
6408c2ecf20Sopenharmony_ci	}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	fake_lm_check(bridge, addr, aspace, cycle);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic noinline_for_stack void fake_vmewrite16(struct fake_driver *bridge,
6478c2ecf20Sopenharmony_ci					       u16 *buf, unsigned long long addr,
6488c2ecf20Sopenharmony_ci					       u32 aspace, u32 cycle)
6498c2ecf20Sopenharmony_ci{
6508c2ecf20Sopenharmony_ci	int i;
6518c2ecf20Sopenharmony_ci	unsigned long long start, end, offset;
6528c2ecf20Sopenharmony_ci	u16 *loc;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	for (i = 0; i < FAKE_MAX_SLAVE; i++) {
6558c2ecf20Sopenharmony_ci		if (aspace != bridge->slaves[i].aspace)
6568c2ecf20Sopenharmony_ci			continue;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci		if (cycle != bridge->slaves[i].cycle)
6598c2ecf20Sopenharmony_ci			continue;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci		start = bridge->slaves[i].vme_base;
6628c2ecf20Sopenharmony_ci		end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci		if ((addr >= start) && ((addr + 1) < end)) {
6658c2ecf20Sopenharmony_ci			offset = addr - bridge->slaves[i].vme_base;
6668c2ecf20Sopenharmony_ci			loc = (u16 *)((void *)bridge->slaves[i].buf_base + offset);
6678c2ecf20Sopenharmony_ci			*loc = *buf;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci			break;
6708c2ecf20Sopenharmony_ci		}
6718c2ecf20Sopenharmony_ci	}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	fake_lm_check(bridge, addr, aspace, cycle);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_cistatic noinline_for_stack void fake_vmewrite32(struct fake_driver *bridge,
6788c2ecf20Sopenharmony_ci					       u32 *buf, unsigned long long addr,
6798c2ecf20Sopenharmony_ci					       u32 aspace, u32 cycle)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	int i;
6828c2ecf20Sopenharmony_ci	unsigned long long start, end, offset;
6838c2ecf20Sopenharmony_ci	u32 *loc;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	for (i = 0; i < FAKE_MAX_SLAVE; i++) {
6868c2ecf20Sopenharmony_ci		if (aspace != bridge->slaves[i].aspace)
6878c2ecf20Sopenharmony_ci			continue;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci		if (cycle != bridge->slaves[i].cycle)
6908c2ecf20Sopenharmony_ci			continue;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci		start = bridge->slaves[i].vme_base;
6938c2ecf20Sopenharmony_ci		end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci		if ((addr >= start) && ((addr + 3) < end)) {
6968c2ecf20Sopenharmony_ci			offset = addr - bridge->slaves[i].vme_base;
6978c2ecf20Sopenharmony_ci			loc = (u32 *)((void *)bridge->slaves[i].buf_base + offset);
6988c2ecf20Sopenharmony_ci			*loc = *buf;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci			break;
7018c2ecf20Sopenharmony_ci		}
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	fake_lm_check(bridge, addr, aspace, cycle);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_cistatic ssize_t fake_master_write(struct vme_master_resource *image, void *buf,
7098c2ecf20Sopenharmony_ci		size_t count, loff_t offset)
7108c2ecf20Sopenharmony_ci{
7118c2ecf20Sopenharmony_ci	int retval = 0;
7128c2ecf20Sopenharmony_ci	u32 aspace, cycle, dwidth;
7138c2ecf20Sopenharmony_ci	unsigned long long addr;
7148c2ecf20Sopenharmony_ci	int i;
7158c2ecf20Sopenharmony_ci	unsigned int done = 0;
7168c2ecf20Sopenharmony_ci	unsigned int count32;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	struct vme_bridge *fake_bridge;
7198c2ecf20Sopenharmony_ci	struct fake_driver *bridge;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	fake_bridge = image->parent;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	bridge = fake_bridge->driver_priv;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	i = image->number;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	addr = bridge->masters[i].vme_base + offset;
7288c2ecf20Sopenharmony_ci	aspace = bridge->masters[i].aspace;
7298c2ecf20Sopenharmony_ci	cycle = bridge->masters[i].cycle;
7308c2ecf20Sopenharmony_ci	dwidth = bridge->masters[i].dwidth;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	spin_lock(&image->lock);
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	/* Here we apply for the same strategy we do in master_read
7358c2ecf20Sopenharmony_ci	 * function in order to assure the correct cycles.
7368c2ecf20Sopenharmony_ci	 */
7378c2ecf20Sopenharmony_ci	if (addr & 0x1) {
7388c2ecf20Sopenharmony_ci		fake_vmewrite8(bridge, (u8 *)buf, addr, aspace, cycle);
7398c2ecf20Sopenharmony_ci		done += 1;
7408c2ecf20Sopenharmony_ci		if (done == count)
7418c2ecf20Sopenharmony_ci			goto out;
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	if ((dwidth == VME_D16) || (dwidth == VME_D32)) {
7458c2ecf20Sopenharmony_ci		if ((addr + done) & 0x2) {
7468c2ecf20Sopenharmony_ci			if ((count - done) < 2) {
7478c2ecf20Sopenharmony_ci				fake_vmewrite8(bridge, (u8 *)(buf + done),
7488c2ecf20Sopenharmony_ci						addr + done, aspace, cycle);
7498c2ecf20Sopenharmony_ci				done += 1;
7508c2ecf20Sopenharmony_ci				goto out;
7518c2ecf20Sopenharmony_ci			} else {
7528c2ecf20Sopenharmony_ci				fake_vmewrite16(bridge, (u16 *)(buf + done),
7538c2ecf20Sopenharmony_ci						addr + done, aspace, cycle);
7548c2ecf20Sopenharmony_ci				done += 2;
7558c2ecf20Sopenharmony_ci			}
7568c2ecf20Sopenharmony_ci		}
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (dwidth == VME_D32) {
7608c2ecf20Sopenharmony_ci		count32 = (count - done) & ~0x3;
7618c2ecf20Sopenharmony_ci		while (done < count32) {
7628c2ecf20Sopenharmony_ci			fake_vmewrite32(bridge, (u32 *)(buf + done),
7638c2ecf20Sopenharmony_ci					addr + done, aspace, cycle);
7648c2ecf20Sopenharmony_ci			done += 4;
7658c2ecf20Sopenharmony_ci		}
7668c2ecf20Sopenharmony_ci	} else if (dwidth == VME_D16) {
7678c2ecf20Sopenharmony_ci		count32 = (count - done) & ~0x3;
7688c2ecf20Sopenharmony_ci		while (done < count32) {
7698c2ecf20Sopenharmony_ci			fake_vmewrite16(bridge, (u16 *)(buf + done),
7708c2ecf20Sopenharmony_ci					addr + done, aspace, cycle);
7718c2ecf20Sopenharmony_ci			done += 2;
7728c2ecf20Sopenharmony_ci		}
7738c2ecf20Sopenharmony_ci	} else if (dwidth == VME_D8) {
7748c2ecf20Sopenharmony_ci		count32 = (count - done);
7758c2ecf20Sopenharmony_ci		while (done < count32) {
7768c2ecf20Sopenharmony_ci			fake_vmewrite8(bridge, (u8 *)(buf + done), addr + done,
7778c2ecf20Sopenharmony_ci					aspace, cycle);
7788c2ecf20Sopenharmony_ci			done += 1;
7798c2ecf20Sopenharmony_ci		}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	if ((dwidth == VME_D16) || (dwidth == VME_D32)) {
7848c2ecf20Sopenharmony_ci		if ((count - done) & 0x2) {
7858c2ecf20Sopenharmony_ci			fake_vmewrite16(bridge, (u16 *)(buf + done),
7868c2ecf20Sopenharmony_ci					addr + done, aspace, cycle);
7878c2ecf20Sopenharmony_ci			done += 2;
7888c2ecf20Sopenharmony_ci		}
7898c2ecf20Sopenharmony_ci	}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	if ((count - done) & 0x1) {
7928c2ecf20Sopenharmony_ci		fake_vmewrite8(bridge, (u8 *)(buf + done), addr + done, aspace,
7938c2ecf20Sopenharmony_ci				cycle);
7948c2ecf20Sopenharmony_ci		done += 1;
7958c2ecf20Sopenharmony_ci	}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ciout:
7988c2ecf20Sopenharmony_ci	retval = count;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	spin_unlock(&image->lock);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	return retval;
8038c2ecf20Sopenharmony_ci}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci/*
8068c2ecf20Sopenharmony_ci * Perform an RMW cycle on the VME bus.
8078c2ecf20Sopenharmony_ci *
8088c2ecf20Sopenharmony_ci * Requires a previously configured master window, returns final value.
8098c2ecf20Sopenharmony_ci */
8108c2ecf20Sopenharmony_cistatic unsigned int fake_master_rmw(struct vme_master_resource *image,
8118c2ecf20Sopenharmony_ci		unsigned int mask, unsigned int compare, unsigned int swap,
8128c2ecf20Sopenharmony_ci		loff_t offset)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	u32 tmp, base;
8158c2ecf20Sopenharmony_ci	u32 aspace, cycle;
8168c2ecf20Sopenharmony_ci	int i;
8178c2ecf20Sopenharmony_ci	struct fake_driver *bridge;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	bridge = image->parent->driver_priv;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	/* Find the PCI address that maps to the desired VME address */
8228c2ecf20Sopenharmony_ci	i = image->number;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	base = bridge->masters[i].vme_base;
8258c2ecf20Sopenharmony_ci	aspace = bridge->masters[i].aspace;
8268c2ecf20Sopenharmony_ci	cycle = bridge->masters[i].cycle;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	/* Lock image */
8298c2ecf20Sopenharmony_ci	spin_lock(&image->lock);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	/* Read existing value */
8328c2ecf20Sopenharmony_ci	tmp = fake_vmeread32(bridge, base + offset, aspace, cycle);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	/* Perform check */
8358c2ecf20Sopenharmony_ci	if ((tmp && mask) == (compare && mask)) {
8368c2ecf20Sopenharmony_ci		tmp = tmp | (mask | swap);
8378c2ecf20Sopenharmony_ci		tmp = tmp & (~mask | swap);
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci		/* Write back */
8408c2ecf20Sopenharmony_ci		fake_vmewrite32(bridge, &tmp, base + offset, aspace, cycle);
8418c2ecf20Sopenharmony_ci	}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	/* Unlock image */
8448c2ecf20Sopenharmony_ci	spin_unlock(&image->lock);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	return tmp;
8478c2ecf20Sopenharmony_ci}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci/*
8508c2ecf20Sopenharmony_ci * All 4 location monitors reside at the same base - this is therefore a
8518c2ecf20Sopenharmony_ci * system wide configuration.
8528c2ecf20Sopenharmony_ci *
8538c2ecf20Sopenharmony_ci * This does not enable the LM monitor - that should be done when the first
8548c2ecf20Sopenharmony_ci * callback is attached and disabled when the last callback is removed.
8558c2ecf20Sopenharmony_ci */
8568c2ecf20Sopenharmony_cistatic int fake_lm_set(struct vme_lm_resource *lm, unsigned long long lm_base,
8578c2ecf20Sopenharmony_ci		u32 aspace, u32 cycle)
8588c2ecf20Sopenharmony_ci{
8598c2ecf20Sopenharmony_ci	int i;
8608c2ecf20Sopenharmony_ci	struct vme_bridge *fake_bridge;
8618c2ecf20Sopenharmony_ci	struct fake_driver *bridge;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	fake_bridge = lm->parent;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	bridge = fake_bridge->driver_priv;
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	mutex_lock(&lm->mtx);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	/* If we already have a callback attached, we can't move it! */
8708c2ecf20Sopenharmony_ci	for (i = 0; i < lm->monitors; i++) {
8718c2ecf20Sopenharmony_ci		if (bridge->lm_callback[i]) {
8728c2ecf20Sopenharmony_ci			mutex_unlock(&lm->mtx);
8738c2ecf20Sopenharmony_ci			pr_err("Location monitor callback attached, can't reset\n");
8748c2ecf20Sopenharmony_ci			return -EBUSY;
8758c2ecf20Sopenharmony_ci		}
8768c2ecf20Sopenharmony_ci	}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	switch (aspace) {
8798c2ecf20Sopenharmony_ci	case VME_A16:
8808c2ecf20Sopenharmony_ci	case VME_A24:
8818c2ecf20Sopenharmony_ci	case VME_A32:
8828c2ecf20Sopenharmony_ci	case VME_A64:
8838c2ecf20Sopenharmony_ci		break;
8848c2ecf20Sopenharmony_ci	default:
8858c2ecf20Sopenharmony_ci		mutex_unlock(&lm->mtx);
8868c2ecf20Sopenharmony_ci		pr_err("Invalid address space\n");
8878c2ecf20Sopenharmony_ci		return -EINVAL;
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	bridge->lm_base = lm_base;
8918c2ecf20Sopenharmony_ci	bridge->lm_aspace = aspace;
8928c2ecf20Sopenharmony_ci	bridge->lm_cycle = cycle;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	mutex_unlock(&lm->mtx);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	return 0;
8978c2ecf20Sopenharmony_ci}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci/* Get configuration of the callback monitor and return whether it is enabled
9008c2ecf20Sopenharmony_ci * or disabled.
9018c2ecf20Sopenharmony_ci */
9028c2ecf20Sopenharmony_cistatic int fake_lm_get(struct vme_lm_resource *lm,
9038c2ecf20Sopenharmony_ci		unsigned long long *lm_base, u32 *aspace, u32 *cycle)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	struct fake_driver *bridge;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	bridge = lm->parent->driver_priv;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	mutex_lock(&lm->mtx);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	*lm_base = bridge->lm_base;
9128c2ecf20Sopenharmony_ci	*aspace = bridge->lm_aspace;
9138c2ecf20Sopenharmony_ci	*cycle = bridge->lm_cycle;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	mutex_unlock(&lm->mtx);
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	return bridge->lm_enabled;
9188c2ecf20Sopenharmony_ci}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci/*
9218c2ecf20Sopenharmony_ci * Attach a callback to a specific location monitor.
9228c2ecf20Sopenharmony_ci *
9238c2ecf20Sopenharmony_ci * Callback will be passed the monitor triggered.
9248c2ecf20Sopenharmony_ci */
9258c2ecf20Sopenharmony_cistatic int fake_lm_attach(struct vme_lm_resource *lm, int monitor,
9268c2ecf20Sopenharmony_ci		void (*callback)(void *), void *data)
9278c2ecf20Sopenharmony_ci{
9288c2ecf20Sopenharmony_ci	struct vme_bridge *fake_bridge;
9298c2ecf20Sopenharmony_ci	struct fake_driver *bridge;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	fake_bridge = lm->parent;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	bridge = fake_bridge->driver_priv;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	mutex_lock(&lm->mtx);
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	/* Ensure that the location monitor is configured - need PGM or DATA */
9388c2ecf20Sopenharmony_ci	if (bridge->lm_cycle == 0) {
9398c2ecf20Sopenharmony_ci		mutex_unlock(&lm->mtx);
9408c2ecf20Sopenharmony_ci		pr_err("Location monitor not properly configured\n");
9418c2ecf20Sopenharmony_ci		return -EINVAL;
9428c2ecf20Sopenharmony_ci	}
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	/* Check that a callback isn't already attached */
9458c2ecf20Sopenharmony_ci	if (bridge->lm_callback[monitor]) {
9468c2ecf20Sopenharmony_ci		mutex_unlock(&lm->mtx);
9478c2ecf20Sopenharmony_ci		pr_err("Existing callback attached\n");
9488c2ecf20Sopenharmony_ci		return -EBUSY;
9498c2ecf20Sopenharmony_ci	}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	/* Attach callback */
9528c2ecf20Sopenharmony_ci	bridge->lm_callback[monitor] = callback;
9538c2ecf20Sopenharmony_ci	bridge->lm_data[monitor] = data;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	/* Ensure that global Location Monitor Enable set */
9568c2ecf20Sopenharmony_ci	bridge->lm_enabled = 1;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	mutex_unlock(&lm->mtx);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	return 0;
9618c2ecf20Sopenharmony_ci}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci/*
9648c2ecf20Sopenharmony_ci * Detach a callback function forn a specific location monitor.
9658c2ecf20Sopenharmony_ci */
9668c2ecf20Sopenharmony_cistatic int fake_lm_detach(struct vme_lm_resource *lm, int monitor)
9678c2ecf20Sopenharmony_ci{
9688c2ecf20Sopenharmony_ci	u32 tmp;
9698c2ecf20Sopenharmony_ci	int i;
9708c2ecf20Sopenharmony_ci	struct fake_driver *bridge;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	bridge = lm->parent->driver_priv;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	mutex_lock(&lm->mtx);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	/* Detach callback */
9778c2ecf20Sopenharmony_ci	bridge->lm_callback[monitor] = NULL;
9788c2ecf20Sopenharmony_ci	bridge->lm_data[monitor] = NULL;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	/* If all location monitors disabled, disable global Location Monitor */
9818c2ecf20Sopenharmony_ci	tmp = 0;
9828c2ecf20Sopenharmony_ci	for (i = 0; i < lm->monitors; i++) {
9838c2ecf20Sopenharmony_ci		if (bridge->lm_callback[i])
9848c2ecf20Sopenharmony_ci			tmp = 1;
9858c2ecf20Sopenharmony_ci	}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	if (tmp == 0)
9888c2ecf20Sopenharmony_ci		bridge->lm_enabled = 0;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	mutex_unlock(&lm->mtx);
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	return 0;
9938c2ecf20Sopenharmony_ci}
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci/*
9968c2ecf20Sopenharmony_ci * Determine Geographical Addressing
9978c2ecf20Sopenharmony_ci */
9988c2ecf20Sopenharmony_cistatic int fake_slot_get(struct vme_bridge *fake_bridge)
9998c2ecf20Sopenharmony_ci{
10008c2ecf20Sopenharmony_ci	return geoid;
10018c2ecf20Sopenharmony_ci}
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_cistatic void *fake_alloc_consistent(struct device *parent, size_t size,
10048c2ecf20Sopenharmony_ci		dma_addr_t *dma)
10058c2ecf20Sopenharmony_ci{
10068c2ecf20Sopenharmony_ci	void *alloc = kmalloc(size, GFP_KERNEL);
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	if (alloc)
10098c2ecf20Sopenharmony_ci		*dma = fake_ptr_to_pci(alloc);
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	return alloc;
10128c2ecf20Sopenharmony_ci}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_cistatic void fake_free_consistent(struct device *parent, size_t size,
10158c2ecf20Sopenharmony_ci		void *vaddr, dma_addr_t dma)
10168c2ecf20Sopenharmony_ci{
10178c2ecf20Sopenharmony_ci	kfree(vaddr);
10188c2ecf20Sopenharmony_ci/*
10198c2ecf20Sopenharmony_ci	dma_free_coherent(parent, size, vaddr, dma);
10208c2ecf20Sopenharmony_ci*/
10218c2ecf20Sopenharmony_ci}
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci/*
10248c2ecf20Sopenharmony_ci * Configure CR/CSR space
10258c2ecf20Sopenharmony_ci *
10268c2ecf20Sopenharmony_ci * Access to the CR/CSR can be configured at power-up. The location of the
10278c2ecf20Sopenharmony_ci * CR/CSR registers in the CR/CSR address space is determined by the boards
10288c2ecf20Sopenharmony_ci * Geographic address.
10298c2ecf20Sopenharmony_ci *
10308c2ecf20Sopenharmony_ci * Each board has a 512kB window, with the highest 4kB being used for the
10318c2ecf20Sopenharmony_ci * boards registers, this means there is a fix length 508kB window which must
10328c2ecf20Sopenharmony_ci * be mapped onto PCI memory.
10338c2ecf20Sopenharmony_ci */
10348c2ecf20Sopenharmony_cistatic int fake_crcsr_init(struct vme_bridge *fake_bridge)
10358c2ecf20Sopenharmony_ci{
10368c2ecf20Sopenharmony_ci	u32 vstat;
10378c2ecf20Sopenharmony_ci	struct fake_driver *bridge;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	bridge = fake_bridge->driver_priv;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	/* Allocate mem for CR/CSR image */
10428c2ecf20Sopenharmony_ci	bridge->crcsr_kernel = kzalloc(VME_CRCSR_BUF_SIZE, GFP_KERNEL);
10438c2ecf20Sopenharmony_ci	bridge->crcsr_bus = fake_ptr_to_pci(bridge->crcsr_kernel);
10448c2ecf20Sopenharmony_ci	if (!bridge->crcsr_kernel)
10458c2ecf20Sopenharmony_ci		return -ENOMEM;
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	vstat = fake_slot_get(fake_bridge);
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	pr_info("CR/CSR Offset: %d\n", vstat);
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	return 0;
10528c2ecf20Sopenharmony_ci}
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_cistatic void fake_crcsr_exit(struct vme_bridge *fake_bridge)
10558c2ecf20Sopenharmony_ci{
10568c2ecf20Sopenharmony_ci	struct fake_driver *bridge;
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	bridge = fake_bridge->driver_priv;
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	kfree(bridge->crcsr_kernel);
10618c2ecf20Sopenharmony_ci}
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_cistatic int __init fake_init(void)
10658c2ecf20Sopenharmony_ci{
10668c2ecf20Sopenharmony_ci	int retval, i;
10678c2ecf20Sopenharmony_ci	struct list_head *pos = NULL, *n;
10688c2ecf20Sopenharmony_ci	struct vme_bridge *fake_bridge;
10698c2ecf20Sopenharmony_ci	struct fake_driver *fake_device;
10708c2ecf20Sopenharmony_ci	struct vme_master_resource *master_image;
10718c2ecf20Sopenharmony_ci	struct vme_slave_resource *slave_image;
10728c2ecf20Sopenharmony_ci	struct vme_lm_resource *lm;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	/* We need a fake parent device */
10758c2ecf20Sopenharmony_ci	vme_root = __root_device_register("vme", THIS_MODULE);
10768c2ecf20Sopenharmony_ci	if (IS_ERR(vme_root))
10778c2ecf20Sopenharmony_ci		return PTR_ERR(vme_root);
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	/* If we want to support more than one bridge at some point, we need to
10808c2ecf20Sopenharmony_ci	 * dynamically allocate this so we get one per device.
10818c2ecf20Sopenharmony_ci	 */
10828c2ecf20Sopenharmony_ci	fake_bridge = kzalloc(sizeof(*fake_bridge), GFP_KERNEL);
10838c2ecf20Sopenharmony_ci	if (!fake_bridge) {
10848c2ecf20Sopenharmony_ci		retval = -ENOMEM;
10858c2ecf20Sopenharmony_ci		goto err_struct;
10868c2ecf20Sopenharmony_ci	}
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	fake_device = kzalloc(sizeof(*fake_device), GFP_KERNEL);
10898c2ecf20Sopenharmony_ci	if (!fake_device) {
10908c2ecf20Sopenharmony_ci		retval = -ENOMEM;
10918c2ecf20Sopenharmony_ci		goto err_driver;
10928c2ecf20Sopenharmony_ci	}
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	fake_bridge->driver_priv = fake_device;
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	fake_bridge->parent = vme_root;
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	fake_device->parent = fake_bridge;
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	/* Initialize wait queues & mutual exclusion flags */
11018c2ecf20Sopenharmony_ci	mutex_init(&fake_device->vme_int);
11028c2ecf20Sopenharmony_ci	mutex_init(&fake_bridge->irq_mtx);
11038c2ecf20Sopenharmony_ci	tasklet_init(&fake_device->int_tasklet, fake_VIRQ_tasklet,
11048c2ecf20Sopenharmony_ci			(unsigned long) fake_bridge);
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	strcpy(fake_bridge->name, driver_name);
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	/* Add master windows to list */
11098c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fake_bridge->master_resources);
11108c2ecf20Sopenharmony_ci	for (i = 0; i < FAKE_MAX_MASTER; i++) {
11118c2ecf20Sopenharmony_ci		master_image = kmalloc(sizeof(*master_image), GFP_KERNEL);
11128c2ecf20Sopenharmony_ci		if (!master_image) {
11138c2ecf20Sopenharmony_ci			retval = -ENOMEM;
11148c2ecf20Sopenharmony_ci			goto err_master;
11158c2ecf20Sopenharmony_ci		}
11168c2ecf20Sopenharmony_ci		master_image->parent = fake_bridge;
11178c2ecf20Sopenharmony_ci		spin_lock_init(&master_image->lock);
11188c2ecf20Sopenharmony_ci		master_image->locked = 0;
11198c2ecf20Sopenharmony_ci		master_image->number = i;
11208c2ecf20Sopenharmony_ci		master_image->address_attr = VME_A16 | VME_A24 | VME_A32 |
11218c2ecf20Sopenharmony_ci			VME_A64;
11228c2ecf20Sopenharmony_ci		master_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT |
11238c2ecf20Sopenharmony_ci			VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 |
11248c2ecf20Sopenharmony_ci			VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER |
11258c2ecf20Sopenharmony_ci			VME_PROG | VME_DATA;
11268c2ecf20Sopenharmony_ci		master_image->width_attr = VME_D16 | VME_D32;
11278c2ecf20Sopenharmony_ci		memset(&master_image->bus_resource, 0,
11288c2ecf20Sopenharmony_ci				sizeof(struct resource));
11298c2ecf20Sopenharmony_ci		master_image->kern_base  = NULL;
11308c2ecf20Sopenharmony_ci		list_add_tail(&master_image->list,
11318c2ecf20Sopenharmony_ci				&fake_bridge->master_resources);
11328c2ecf20Sopenharmony_ci	}
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	/* Add slave windows to list */
11358c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fake_bridge->slave_resources);
11368c2ecf20Sopenharmony_ci	for (i = 0; i < FAKE_MAX_SLAVE; i++) {
11378c2ecf20Sopenharmony_ci		slave_image = kmalloc(sizeof(*slave_image), GFP_KERNEL);
11388c2ecf20Sopenharmony_ci		if (!slave_image) {
11398c2ecf20Sopenharmony_ci			retval = -ENOMEM;
11408c2ecf20Sopenharmony_ci			goto err_slave;
11418c2ecf20Sopenharmony_ci		}
11428c2ecf20Sopenharmony_ci		slave_image->parent = fake_bridge;
11438c2ecf20Sopenharmony_ci		mutex_init(&slave_image->mtx);
11448c2ecf20Sopenharmony_ci		slave_image->locked = 0;
11458c2ecf20Sopenharmony_ci		slave_image->number = i;
11468c2ecf20Sopenharmony_ci		slave_image->address_attr = VME_A16 | VME_A24 | VME_A32 |
11478c2ecf20Sopenharmony_ci			VME_A64 | VME_CRCSR | VME_USER1 | VME_USER2 |
11488c2ecf20Sopenharmony_ci			VME_USER3 | VME_USER4;
11498c2ecf20Sopenharmony_ci		slave_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT |
11508c2ecf20Sopenharmony_ci			VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 |
11518c2ecf20Sopenharmony_ci			VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER |
11528c2ecf20Sopenharmony_ci			VME_PROG | VME_DATA;
11538c2ecf20Sopenharmony_ci		list_add_tail(&slave_image->list,
11548c2ecf20Sopenharmony_ci				&fake_bridge->slave_resources);
11558c2ecf20Sopenharmony_ci	}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	/* Add location monitor to list */
11588c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fake_bridge->lm_resources);
11598c2ecf20Sopenharmony_ci	lm = kmalloc(sizeof(*lm), GFP_KERNEL);
11608c2ecf20Sopenharmony_ci	if (!lm) {
11618c2ecf20Sopenharmony_ci		retval = -ENOMEM;
11628c2ecf20Sopenharmony_ci		goto err_lm;
11638c2ecf20Sopenharmony_ci	}
11648c2ecf20Sopenharmony_ci	lm->parent = fake_bridge;
11658c2ecf20Sopenharmony_ci	mutex_init(&lm->mtx);
11668c2ecf20Sopenharmony_ci	lm->locked = 0;
11678c2ecf20Sopenharmony_ci	lm->number = 1;
11688c2ecf20Sopenharmony_ci	lm->monitors = 4;
11698c2ecf20Sopenharmony_ci	list_add_tail(&lm->list, &fake_bridge->lm_resources);
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	fake_bridge->slave_get = fake_slave_get;
11728c2ecf20Sopenharmony_ci	fake_bridge->slave_set = fake_slave_set;
11738c2ecf20Sopenharmony_ci	fake_bridge->master_get = fake_master_get;
11748c2ecf20Sopenharmony_ci	fake_bridge->master_set = fake_master_set;
11758c2ecf20Sopenharmony_ci	fake_bridge->master_read = fake_master_read;
11768c2ecf20Sopenharmony_ci	fake_bridge->master_write = fake_master_write;
11778c2ecf20Sopenharmony_ci	fake_bridge->master_rmw = fake_master_rmw;
11788c2ecf20Sopenharmony_ci	fake_bridge->irq_set = fake_irq_set;
11798c2ecf20Sopenharmony_ci	fake_bridge->irq_generate = fake_irq_generate;
11808c2ecf20Sopenharmony_ci	fake_bridge->lm_set = fake_lm_set;
11818c2ecf20Sopenharmony_ci	fake_bridge->lm_get = fake_lm_get;
11828c2ecf20Sopenharmony_ci	fake_bridge->lm_attach = fake_lm_attach;
11838c2ecf20Sopenharmony_ci	fake_bridge->lm_detach = fake_lm_detach;
11848c2ecf20Sopenharmony_ci	fake_bridge->slot_get = fake_slot_get;
11858c2ecf20Sopenharmony_ci	fake_bridge->alloc_consistent = fake_alloc_consistent;
11868c2ecf20Sopenharmony_ci	fake_bridge->free_consistent = fake_free_consistent;
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	pr_info("Board is%s the VME system controller\n",
11898c2ecf20Sopenharmony_ci			(geoid == 1) ? "" : " not");
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	pr_info("VME geographical address is set to %d\n", geoid);
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	retval = fake_crcsr_init(fake_bridge);
11948c2ecf20Sopenharmony_ci	if (retval) {
11958c2ecf20Sopenharmony_ci		pr_err("CR/CSR configuration failed.\n");
11968c2ecf20Sopenharmony_ci		goto err_crcsr;
11978c2ecf20Sopenharmony_ci	}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	retval = vme_register_bridge(fake_bridge);
12008c2ecf20Sopenharmony_ci	if (retval != 0) {
12018c2ecf20Sopenharmony_ci		pr_err("Chip Registration failed.\n");
12028c2ecf20Sopenharmony_ci		goto err_reg;
12038c2ecf20Sopenharmony_ci	}
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	exit_pointer = fake_bridge;
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	return 0;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_cierr_reg:
12108c2ecf20Sopenharmony_ci	fake_crcsr_exit(fake_bridge);
12118c2ecf20Sopenharmony_cierr_crcsr:
12128c2ecf20Sopenharmony_cierr_lm:
12138c2ecf20Sopenharmony_ci	/* resources are stored in link list */
12148c2ecf20Sopenharmony_ci	list_for_each_safe(pos, n, &fake_bridge->lm_resources) {
12158c2ecf20Sopenharmony_ci		lm = list_entry(pos, struct vme_lm_resource, list);
12168c2ecf20Sopenharmony_ci		list_del(pos);
12178c2ecf20Sopenharmony_ci		kfree(lm);
12188c2ecf20Sopenharmony_ci	}
12198c2ecf20Sopenharmony_cierr_slave:
12208c2ecf20Sopenharmony_ci	/* resources are stored in link list */
12218c2ecf20Sopenharmony_ci	list_for_each_safe(pos, n, &fake_bridge->slave_resources) {
12228c2ecf20Sopenharmony_ci		slave_image = list_entry(pos, struct vme_slave_resource, list);
12238c2ecf20Sopenharmony_ci		list_del(pos);
12248c2ecf20Sopenharmony_ci		kfree(slave_image);
12258c2ecf20Sopenharmony_ci	}
12268c2ecf20Sopenharmony_cierr_master:
12278c2ecf20Sopenharmony_ci	/* resources are stored in link list */
12288c2ecf20Sopenharmony_ci	list_for_each_safe(pos, n, &fake_bridge->master_resources) {
12298c2ecf20Sopenharmony_ci		master_image = list_entry(pos, struct vme_master_resource,
12308c2ecf20Sopenharmony_ci				list);
12318c2ecf20Sopenharmony_ci		list_del(pos);
12328c2ecf20Sopenharmony_ci		kfree(master_image);
12338c2ecf20Sopenharmony_ci	}
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	kfree(fake_device);
12368c2ecf20Sopenharmony_cierr_driver:
12378c2ecf20Sopenharmony_ci	kfree(fake_bridge);
12388c2ecf20Sopenharmony_cierr_struct:
12398c2ecf20Sopenharmony_ci	return retval;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci}
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_cistatic void __exit fake_exit(void)
12458c2ecf20Sopenharmony_ci{
12468c2ecf20Sopenharmony_ci	struct list_head *pos = NULL;
12478c2ecf20Sopenharmony_ci	struct list_head *tmplist;
12488c2ecf20Sopenharmony_ci	struct vme_master_resource *master_image;
12498c2ecf20Sopenharmony_ci	struct vme_slave_resource *slave_image;
12508c2ecf20Sopenharmony_ci	int i;
12518c2ecf20Sopenharmony_ci	struct vme_bridge *fake_bridge;
12528c2ecf20Sopenharmony_ci	struct fake_driver *bridge;
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	fake_bridge = exit_pointer;
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	bridge = fake_bridge->driver_priv;
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	pr_debug("Driver is being unloaded.\n");
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	/*
12618c2ecf20Sopenharmony_ci	 *  Shutdown all inbound and outbound windows.
12628c2ecf20Sopenharmony_ci	 */
12638c2ecf20Sopenharmony_ci	for (i = 0; i < FAKE_MAX_MASTER; i++)
12648c2ecf20Sopenharmony_ci		bridge->masters[i].enabled = 0;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	for (i = 0; i < FAKE_MAX_SLAVE; i++)
12678c2ecf20Sopenharmony_ci		bridge->slaves[i].enabled = 0;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	/*
12708c2ecf20Sopenharmony_ci	 *  Shutdown Location monitor.
12718c2ecf20Sopenharmony_ci	 */
12728c2ecf20Sopenharmony_ci	bridge->lm_enabled = 0;
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	vme_unregister_bridge(fake_bridge);
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	fake_crcsr_exit(fake_bridge);
12778c2ecf20Sopenharmony_ci	/* resources are stored in link list */
12788c2ecf20Sopenharmony_ci	list_for_each_safe(pos, tmplist, &fake_bridge->slave_resources) {
12798c2ecf20Sopenharmony_ci		slave_image = list_entry(pos, struct vme_slave_resource, list);
12808c2ecf20Sopenharmony_ci		list_del(pos);
12818c2ecf20Sopenharmony_ci		kfree(slave_image);
12828c2ecf20Sopenharmony_ci	}
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	/* resources are stored in link list */
12858c2ecf20Sopenharmony_ci	list_for_each_safe(pos, tmplist, &fake_bridge->master_resources) {
12868c2ecf20Sopenharmony_ci		master_image = list_entry(pos, struct vme_master_resource,
12878c2ecf20Sopenharmony_ci				list);
12888c2ecf20Sopenharmony_ci		list_del(pos);
12898c2ecf20Sopenharmony_ci		kfree(master_image);
12908c2ecf20Sopenharmony_ci	}
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	kfree(fake_bridge->driver_priv);
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	kfree(fake_bridge);
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci	root_device_unregister(vme_root);
12978c2ecf20Sopenharmony_ci}
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ciMODULE_PARM_DESC(geoid, "Set geographical addressing");
13018c2ecf20Sopenharmony_cimodule_param(geoid, int, 0);
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Fake VME bridge driver");
13048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_cimodule_init(fake_init);
13078c2ecf20Sopenharmony_cimodule_exit(fake_exit);
1308