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  */
fake_VIRQ_tasklet(unsigned long data)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  */
fake_irq_set(struct vme_bridge * fake_bridge,int level,int state,int sync)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 
fake_pci_to_ptr(dma_addr_t addr)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 
fake_ptr_to_pci(void * addr)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  */
fake_irq_generate(struct vme_bridge * fake_bridge,int level,int statid)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  */
fake_slave_set(struct vme_slave_resource * image,int enabled,unsigned long long vme_base,unsigned long long size,dma_addr_t buf_base,u32 aspace,u32 cycle)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  */
fake_slave_get(struct vme_slave_resource * image,int * enabled,unsigned long long * vme_base,unsigned long long * size,dma_addr_t * buf_base,u32 * aspace,u32 * cycle)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  */
fake_master_set(struct vme_master_resource * image,int enabled,unsigned long long vme_base,unsigned long long size,u32 aspace,u32 cycle,u32 dwidth)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  * Set the attributes of an outbound window.
33635ba63b8SArnd Bergmann  */
__fake_master_get(struct vme_master_resource * image,int * enabled,unsigned long long * vme_base,unsigned long long * size,u32 * aspace,u32 * cycle,u32 * dwidth)33735ba63b8SArnd Bergmann static int __fake_master_get(struct vme_master_resource *image, int *enabled,
33835ba63b8SArnd Bergmann 		unsigned long long *vme_base, unsigned long long *size,
33935ba63b8SArnd Bergmann 		u32 *aspace, u32 *cycle, u32 *dwidth)
34035ba63b8SArnd Bergmann {
34135ba63b8SArnd Bergmann 	unsigned int i;
34235ba63b8SArnd Bergmann 	struct fake_driver *bridge;
34335ba63b8SArnd Bergmann 
34435ba63b8SArnd Bergmann 	bridge = image->parent->driver_priv;
34535ba63b8SArnd Bergmann 
34635ba63b8SArnd Bergmann 	i = image->number;
34735ba63b8SArnd Bergmann 
34835ba63b8SArnd Bergmann 	*enabled = bridge->masters[i].enabled;
34935ba63b8SArnd Bergmann 	*vme_base = bridge->masters[i].vme_base;
35035ba63b8SArnd Bergmann 	*size = bridge->masters[i].size;
35135ba63b8SArnd Bergmann 	*aspace = bridge->masters[i].aspace;
35235ba63b8SArnd Bergmann 	*cycle = bridge->masters[i].cycle;
35335ba63b8SArnd Bergmann 	*dwidth = bridge->masters[i].dwidth;
35435ba63b8SArnd Bergmann 
35535ba63b8SArnd Bergmann 	return 0;
35635ba63b8SArnd Bergmann }
35735ba63b8SArnd Bergmann 
fake_master_get(struct vme_master_resource * image,int * enabled,unsigned long long * vme_base,unsigned long long * size,u32 * aspace,u32 * cycle,u32 * dwidth)35835ba63b8SArnd Bergmann static int fake_master_get(struct vme_master_resource *image, int *enabled,
35935ba63b8SArnd Bergmann 		unsigned long long *vme_base, unsigned long long *size,
36035ba63b8SArnd Bergmann 		u32 *aspace, u32 *cycle, u32 *dwidth)
36135ba63b8SArnd Bergmann {
36235ba63b8SArnd Bergmann 	int retval;
36335ba63b8SArnd Bergmann 
36435ba63b8SArnd Bergmann 	spin_lock(&image->lock);
36535ba63b8SArnd Bergmann 
36635ba63b8SArnd Bergmann 	retval = __fake_master_get(image, enabled, vme_base, size, aspace,
36735ba63b8SArnd Bergmann 			cycle, dwidth);
36835ba63b8SArnd Bergmann 
36935ba63b8SArnd Bergmann 	spin_unlock(&image->lock);
37035ba63b8SArnd Bergmann 
37135ba63b8SArnd Bergmann 	return retval;
37235ba63b8SArnd Bergmann }
37335ba63b8SArnd Bergmann 
fake_lm_check(struct fake_driver * bridge,unsigned long long addr,u32 aspace,u32 cycle)37435ba63b8SArnd Bergmann static void fake_lm_check(struct fake_driver *bridge, unsigned long long addr,
37535ba63b8SArnd Bergmann 			  u32 aspace, u32 cycle)
37635ba63b8SArnd Bergmann {
37735ba63b8SArnd Bergmann 	struct vme_bridge *fake_bridge;
37835ba63b8SArnd Bergmann 	unsigned long long lm_base;
37935ba63b8SArnd Bergmann 	u32 lm_aspace, lm_cycle;
38035ba63b8SArnd Bergmann 	int i;
38135ba63b8SArnd Bergmann 	struct vme_lm_resource *lm;
38235ba63b8SArnd Bergmann 	struct list_head *pos = NULL, *n;
38335ba63b8SArnd Bergmann 
38435ba63b8SArnd Bergmann 	/* Get vme_bridge */
38535ba63b8SArnd Bergmann 	fake_bridge = bridge->parent;
38635ba63b8SArnd Bergmann 
38735ba63b8SArnd Bergmann 	/* Loop through each location monitor resource */
38835ba63b8SArnd Bergmann 	list_for_each_safe(pos, n, &fake_bridge->lm_resources) {
38935ba63b8SArnd Bergmann 		lm = list_entry(pos, struct vme_lm_resource, list);
39035ba63b8SArnd Bergmann 
39135ba63b8SArnd Bergmann 		/* If disabled, we're done */
39235ba63b8SArnd Bergmann 		if (bridge->lm_enabled == 0)
39335ba63b8SArnd Bergmann 			return;
39435ba63b8SArnd Bergmann 
39535ba63b8SArnd Bergmann 		lm_base = bridge->lm_base;
39635ba63b8SArnd Bergmann 		lm_aspace = bridge->lm_aspace;
39735ba63b8SArnd Bergmann 		lm_cycle = bridge->lm_cycle;
39835ba63b8SArnd Bergmann 
39935ba63b8SArnd Bergmann 		/* First make sure that the cycle and address space match */
40035ba63b8SArnd Bergmann 		if ((lm_aspace == aspace) && (lm_cycle == cycle)) {
40135ba63b8SArnd Bergmann 			for (i = 0; i < lm->monitors; i++) {
40235ba63b8SArnd Bergmann 				/* Each location monitor covers 8 bytes */
40335ba63b8SArnd Bergmann 				if (((lm_base + (8 * i)) <= addr) &&
40435ba63b8SArnd Bergmann 				    ((lm_base + (8 * i) + 8) > addr)) {
40535ba63b8SArnd Bergmann 					if (bridge->lm_callback[i])
40635ba63b8SArnd Bergmann 						bridge->lm_callback[i](
40735ba63b8SArnd Bergmann 							bridge->lm_data[i]);
40835ba63b8SArnd Bergmann 				}
40935ba63b8SArnd Bergmann 			}
41035ba63b8SArnd Bergmann 		}
41135ba63b8SArnd Bergmann 	}
41235ba63b8SArnd Bergmann }
41335ba63b8SArnd Bergmann 
fake_vmeread8(struct fake_driver * bridge,unsigned long long addr,u32 aspace,u32 cycle)41435ba63b8SArnd Bergmann static noinline_for_stack u8 fake_vmeread8(struct fake_driver *bridge,
41535ba63b8SArnd Bergmann 					   unsigned long long addr,
41635ba63b8SArnd Bergmann 					   u32 aspace, u32 cycle)
41735ba63b8SArnd Bergmann {
41835ba63b8SArnd Bergmann 	u8 retval = 0xff;
41935ba63b8SArnd Bergmann 	int i;
42035ba63b8SArnd Bergmann 	unsigned long long start, end, offset;
42135ba63b8SArnd Bergmann 	u8 *loc;
42235ba63b8SArnd Bergmann 
42335ba63b8SArnd Bergmann 	for (i = 0; i < FAKE_MAX_SLAVE; i++) {
42435ba63b8SArnd Bergmann 		start = bridge->slaves[i].vme_base;
42535ba63b8SArnd Bergmann 		end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
42635ba63b8SArnd Bergmann 
42735ba63b8SArnd Bergmann 		if (aspace != bridge->slaves[i].aspace)
42835ba63b8SArnd Bergmann 			continue;
42935ba63b8SArnd Bergmann 
43035ba63b8SArnd Bergmann 		if (cycle != bridge->slaves[i].cycle)
43135ba63b8SArnd Bergmann 			continue;
43235ba63b8SArnd Bergmann 
43335ba63b8SArnd Bergmann 		if ((addr >= start) && (addr < end)) {
43435ba63b8SArnd Bergmann 			offset = addr - bridge->slaves[i].vme_base;
43535ba63b8SArnd Bergmann 			loc = (u8 *)(bridge->slaves[i].buf_base + offset);
43635ba63b8SArnd Bergmann 			retval = *loc;
43735ba63b8SArnd Bergmann 
43835ba63b8SArnd Bergmann 			break;
43935ba63b8SArnd Bergmann 		}
44035ba63b8SArnd Bergmann 	}
44135ba63b8SArnd Bergmann 
44235ba63b8SArnd Bergmann 	fake_lm_check(bridge, addr, aspace, cycle);
44335ba63b8SArnd Bergmann 
44435ba63b8SArnd Bergmann 	return retval;
44535ba63b8SArnd Bergmann }
44635ba63b8SArnd Bergmann 
fake_vmeread16(struct fake_driver * bridge,unsigned long long addr,u32 aspace,u32 cycle)44735ba63b8SArnd Bergmann static noinline_for_stack u16 fake_vmeread16(struct fake_driver *bridge,
44835ba63b8SArnd Bergmann 					     unsigned long long addr,
44935ba63b8SArnd Bergmann 					     u32 aspace, u32 cycle)
45035ba63b8SArnd Bergmann {
45135ba63b8SArnd Bergmann 	u16 retval = 0xffff;
45235ba63b8SArnd Bergmann 	int i;
45335ba63b8SArnd Bergmann 	unsigned long long start, end, offset;
45435ba63b8SArnd Bergmann 	u16 *loc;
45535ba63b8SArnd Bergmann 
45635ba63b8SArnd Bergmann 	for (i = 0; i < FAKE_MAX_SLAVE; i++) {
45735ba63b8SArnd Bergmann 		if (aspace != bridge->slaves[i].aspace)
45835ba63b8SArnd Bergmann 			continue;
45935ba63b8SArnd Bergmann 
46035ba63b8SArnd Bergmann 		if (cycle != bridge->slaves[i].cycle)
46135ba63b8SArnd Bergmann 			continue;
46235ba63b8SArnd Bergmann 
46335ba63b8SArnd Bergmann 		start = bridge->slaves[i].vme_base;
46435ba63b8SArnd Bergmann 		end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
46535ba63b8SArnd Bergmann 
46635ba63b8SArnd Bergmann 		if ((addr >= start) && ((addr + 1) < end)) {
46735ba63b8SArnd Bergmann 			offset = addr - bridge->slaves[i].vme_base;
46835ba63b8SArnd Bergmann 			loc = (u16 *)(bridge->slaves[i].buf_base + offset);
46935ba63b8SArnd Bergmann 			retval = *loc;
47035ba63b8SArnd Bergmann 
47135ba63b8SArnd Bergmann 			break;
47235ba63b8SArnd Bergmann 		}
47335ba63b8SArnd Bergmann 	}
47435ba63b8SArnd Bergmann 
47535ba63b8SArnd Bergmann 	fake_lm_check(bridge, addr, aspace, cycle);
47635ba63b8SArnd Bergmann 
47735ba63b8SArnd Bergmann 	return retval;
47835ba63b8SArnd Bergmann }
47935ba63b8SArnd Bergmann 
fake_vmeread32(struct fake_driver * bridge,unsigned long long addr,u32 aspace,u32 cycle)48035ba63b8SArnd Bergmann static noinline_for_stack u32 fake_vmeread32(struct fake_driver *bridge,
48135ba63b8SArnd Bergmann 					     unsigned long long addr,
48235ba63b8SArnd Bergmann 					     u32 aspace, u32 cycle)
48335ba63b8SArnd Bergmann {
48435ba63b8SArnd Bergmann 	u32 retval = 0xffffffff;
48535ba63b8SArnd Bergmann 	int i;
48635ba63b8SArnd Bergmann 	unsigned long long start, end, offset;
48735ba63b8SArnd Bergmann 	u32 *loc;
48835ba63b8SArnd Bergmann 
48935ba63b8SArnd Bergmann 	for (i = 0; i < FAKE_MAX_SLAVE; i++) {
49035ba63b8SArnd Bergmann 		if (aspace != bridge->slaves[i].aspace)
49135ba63b8SArnd Bergmann 			continue;
49235ba63b8SArnd Bergmann 
49335ba63b8SArnd Bergmann 		if (cycle != bridge->slaves[i].cycle)
49435ba63b8SArnd Bergmann 			continue;
49535ba63b8SArnd Bergmann 
49635ba63b8SArnd Bergmann 		start = bridge->slaves[i].vme_base;
49735ba63b8SArnd Bergmann 		end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
49835ba63b8SArnd Bergmann 
49935ba63b8SArnd Bergmann 		if ((addr >= start) && ((addr + 3) < end)) {
50035ba63b8SArnd Bergmann 			offset = addr - bridge->slaves[i].vme_base;
50135ba63b8SArnd Bergmann 			loc = (u32 *)(bridge->slaves[i].buf_base + offset);
50235ba63b8SArnd Bergmann 			retval = *loc;
50335ba63b8SArnd Bergmann 
50435ba63b8SArnd Bergmann 			break;
50535ba63b8SArnd Bergmann 		}
50635ba63b8SArnd Bergmann 	}
50735ba63b8SArnd Bergmann 
50835ba63b8SArnd Bergmann 	fake_lm_check(bridge, addr, aspace, cycle);
50935ba63b8SArnd Bergmann 
51035ba63b8SArnd Bergmann 	return retval;
51135ba63b8SArnd Bergmann }
51235ba63b8SArnd Bergmann 
fake_master_read(struct vme_master_resource * image,void * buf,size_t count,loff_t offset)51335ba63b8SArnd Bergmann static ssize_t fake_master_read(struct vme_master_resource *image, void *buf,
51435ba63b8SArnd Bergmann 		size_t count, loff_t offset)
51535ba63b8SArnd Bergmann {
51635ba63b8SArnd Bergmann 	int retval;
51735ba63b8SArnd Bergmann 	u32 aspace, cycle, dwidth;
51835ba63b8SArnd Bergmann 	struct vme_bridge *fake_bridge;
51935ba63b8SArnd Bergmann 	struct fake_driver *priv;
52035ba63b8SArnd Bergmann 	int i;
52135ba63b8SArnd Bergmann 	unsigned long long addr;
52235ba63b8SArnd Bergmann 	unsigned int done = 0;
52335ba63b8SArnd Bergmann 	unsigned int count32;
52435ba63b8SArnd Bergmann 
52535ba63b8SArnd Bergmann 	fake_bridge = image->parent;
52635ba63b8SArnd Bergmann 
52735ba63b8SArnd Bergmann 	priv = fake_bridge->driver_priv;
52835ba63b8SArnd Bergmann 
52935ba63b8SArnd Bergmann 	i = image->number;
53035ba63b8SArnd Bergmann 
53135ba63b8SArnd Bergmann 	addr = (unsigned long long)priv->masters[i].vme_base + offset;
53235ba63b8SArnd Bergmann 	aspace = priv->masters[i].aspace;
53335ba63b8SArnd Bergmann 	cycle = priv->masters[i].cycle;
53435ba63b8SArnd Bergmann 	dwidth = priv->masters[i].dwidth;
53535ba63b8SArnd Bergmann 
53635ba63b8SArnd Bergmann 	spin_lock(&image->lock);
53735ba63b8SArnd Bergmann 
53835ba63b8SArnd Bergmann 	/* The following code handles VME address alignment. We cannot use
53935ba63b8SArnd Bergmann 	 * memcpy_xxx here because it may cut data transfers in to 8-bit
54035ba63b8SArnd Bergmann 	 * cycles when D16 or D32 cycles are required on the VME bus.
54135ba63b8SArnd Bergmann 	 * On the other hand, the bridge itself assures that the maximum data
54235ba63b8SArnd Bergmann 	 * cycle configured for the transfer is used and splits it
54335ba63b8SArnd Bergmann 	 * automatically for non-aligned addresses, so we don't want the
54435ba63b8SArnd Bergmann 	 * overhead of needlessly forcing small transfers for the entire cycle.
54535ba63b8SArnd Bergmann 	 */
54635ba63b8SArnd Bergmann 	if (addr & 0x1) {
54735ba63b8SArnd Bergmann 		*(u8 *)buf = fake_vmeread8(priv, addr, aspace, cycle);
54835ba63b8SArnd Bergmann 		done += 1;
54935ba63b8SArnd Bergmann 		if (done == count)
55035ba63b8SArnd Bergmann 			goto out;
55135ba63b8SArnd Bergmann 	}
55235ba63b8SArnd Bergmann 	if ((dwidth == VME_D16) || (dwidth == VME_D32)) {
55335ba63b8SArnd Bergmann 		if ((addr + done) & 0x2) {
55435ba63b8SArnd Bergmann 			if ((count - done) < 2) {
55535ba63b8SArnd Bergmann 				*(u8 *)(buf + done) = fake_vmeread8(priv,
55635ba63b8SArnd Bergmann 						addr + done, aspace, cycle);
55735ba63b8SArnd Bergmann 				done += 1;
55835ba63b8SArnd Bergmann 				goto out;
55935ba63b8SArnd Bergmann 			} else {
56035ba63b8SArnd Bergmann 				*(u16 *)(buf + done) = fake_vmeread16(priv,
56135ba63b8SArnd Bergmann 						addr + done, aspace, cycle);
56235ba63b8SArnd Bergmann 				done += 2;
56335ba63b8SArnd Bergmann 			}
56435ba63b8SArnd Bergmann 		}
56535ba63b8SArnd Bergmann 	}
56635ba63b8SArnd Bergmann 
56735ba63b8SArnd Bergmann 	if (dwidth == VME_D32) {
56835ba63b8SArnd Bergmann 		count32 = (count - done) & ~0x3;
56935ba63b8SArnd Bergmann 		while (done < count32) {
57035ba63b8SArnd Bergmann 			*(u32 *)(buf + done) = fake_vmeread32(priv, addr + done,
57135ba63b8SArnd Bergmann 					aspace, cycle);
57235ba63b8SArnd Bergmann 			done += 4;
57335ba63b8SArnd Bergmann 		}
57435ba63b8SArnd Bergmann 	} else if (dwidth == VME_D16) {
57535ba63b8SArnd Bergmann 		count32 = (count - done) & ~0x3;
57635ba63b8SArnd Bergmann 		while (done < count32) {
57735ba63b8SArnd Bergmann 			*(u16 *)(buf + done) = fake_vmeread16(priv, addr + done,
57835ba63b8SArnd Bergmann 					aspace, cycle);
57935ba63b8SArnd Bergmann 			done += 2;
58035ba63b8SArnd Bergmann 		}
58135ba63b8SArnd Bergmann 	} else if (dwidth == VME_D8) {
58235ba63b8SArnd Bergmann 		count32 = (count - done);
58335ba63b8SArnd Bergmann 		while (done < count32) {
58435ba63b8SArnd Bergmann 			*(u8 *)(buf + done) = fake_vmeread8(priv, addr + done,
58535ba63b8SArnd Bergmann 					aspace, cycle);
58635ba63b8SArnd Bergmann 			done += 1;
58735ba63b8SArnd Bergmann 		}
58835ba63b8SArnd Bergmann 
58935ba63b8SArnd Bergmann 	}
59035ba63b8SArnd Bergmann 
59135ba63b8SArnd Bergmann 	if ((dwidth == VME_D16) || (dwidth == VME_D32)) {
59235ba63b8SArnd Bergmann 		if ((count - done) & 0x2) {
59335ba63b8SArnd Bergmann 			*(u16 *)(buf + done) = fake_vmeread16(priv, addr + done,
59435ba63b8SArnd Bergmann 					aspace, cycle);
59535ba63b8SArnd Bergmann 			done += 2;
59635ba63b8SArnd Bergmann 		}
59735ba63b8SArnd Bergmann 	}
59835ba63b8SArnd Bergmann 	if ((count - done) & 0x1) {
59935ba63b8SArnd Bergmann 		*(u8 *)(buf + done) = fake_vmeread8(priv, addr + done, aspace,
60035ba63b8SArnd Bergmann 				cycle);
60135ba63b8SArnd Bergmann 		done += 1;
60235ba63b8SArnd Bergmann 	}
60335ba63b8SArnd Bergmann 
60435ba63b8SArnd Bergmann out:
60535ba63b8SArnd Bergmann 	retval = count;
60635ba63b8SArnd Bergmann 
60735ba63b8SArnd Bergmann 	spin_unlock(&image->lock);
60835ba63b8SArnd Bergmann 
60935ba63b8SArnd Bergmann 	return retval;
61035ba63b8SArnd Bergmann }
61135ba63b8SArnd Bergmann 
fake_vmewrite8(struct fake_driver * bridge,u8 * buf,unsigned long long addr,u32 aspace,u32 cycle)61235ba63b8SArnd Bergmann static noinline_for_stack void fake_vmewrite8(struct fake_driver *bridge,
61335ba63b8SArnd Bergmann 					      u8 *buf, unsigned long long addr,
61435ba63b8SArnd Bergmann 					      u32 aspace, u32 cycle)
61535ba63b8SArnd Bergmann {
61635ba63b8SArnd Bergmann 	int i;
61735ba63b8SArnd Bergmann 	unsigned long long start, end, offset;
61835ba63b8SArnd Bergmann 	u8 *loc;
61935ba63b8SArnd Bergmann 
62035ba63b8SArnd Bergmann 	for (i = 0; i < FAKE_MAX_SLAVE; i++) {
62135ba63b8SArnd Bergmann 		if (aspace != bridge->slaves[i].aspace)
62235ba63b8SArnd Bergmann 			continue;
62335ba63b8SArnd Bergmann 
62435ba63b8SArnd Bergmann 		if (cycle != bridge->slaves[i].cycle)
62535ba63b8SArnd Bergmann 			continue;
62635ba63b8SArnd Bergmann 
62735ba63b8SArnd Bergmann 		start = bridge->slaves[i].vme_base;
62835ba63b8SArnd Bergmann 		end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
62935ba63b8SArnd Bergmann 
63035ba63b8SArnd Bergmann 		if ((addr >= start) && (addr < end)) {
63135ba63b8SArnd Bergmann 			offset = addr - bridge->slaves[i].vme_base;
63235ba63b8SArnd Bergmann 			loc = (u8 *)((void *)bridge->slaves[i].buf_base + offset);
63335ba63b8SArnd Bergmann 			*loc = *buf;
63435ba63b8SArnd Bergmann 
63535ba63b8SArnd Bergmann 			break;
63635ba63b8SArnd Bergmann 		}
63735ba63b8SArnd Bergmann 	}
63835ba63b8SArnd Bergmann 
63935ba63b8SArnd Bergmann 	fake_lm_check(bridge, addr, aspace, cycle);
64035ba63b8SArnd Bergmann }
64135ba63b8SArnd Bergmann 
fake_vmewrite16(struct fake_driver * bridge,u16 * buf,unsigned long long addr,u32 aspace,u32 cycle)64235ba63b8SArnd Bergmann static noinline_for_stack void fake_vmewrite16(struct fake_driver *bridge,
64335ba63b8SArnd Bergmann 					       u16 *buf, unsigned long long addr,
64435ba63b8SArnd Bergmann 					       u32 aspace, u32 cycle)
64535ba63b8SArnd Bergmann {
64635ba63b8SArnd Bergmann 	int i;
64735ba63b8SArnd Bergmann 	unsigned long long start, end, offset;
64835ba63b8SArnd Bergmann 	u16 *loc;
64935ba63b8SArnd Bergmann 
65035ba63b8SArnd Bergmann 	for (i = 0; i < FAKE_MAX_SLAVE; i++) {
65135ba63b8SArnd Bergmann 		if (aspace != bridge->slaves[i].aspace)
65235ba63b8SArnd Bergmann 			continue;
65335ba63b8SArnd Bergmann 
65435ba63b8SArnd Bergmann 		if (cycle != bridge->slaves[i].cycle)
65535ba63b8SArnd Bergmann 			continue;
65635ba63b8SArnd Bergmann 
65735ba63b8SArnd Bergmann 		start = bridge->slaves[i].vme_base;
65835ba63b8SArnd Bergmann 		end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
65935ba63b8SArnd Bergmann 
66035ba63b8SArnd Bergmann 		if ((addr >= start) && ((addr + 1) < end)) {
66135ba63b8SArnd Bergmann 			offset = addr - bridge->slaves[i].vme_base;
66235ba63b8SArnd Bergmann 			loc = (u16 *)((void *)bridge->slaves[i].buf_base + offset);
66335ba63b8SArnd Bergmann 			*loc = *buf;
66435ba63b8SArnd Bergmann 
66535ba63b8SArnd Bergmann 			break;
66635ba63b8SArnd Bergmann 		}
66735ba63b8SArnd Bergmann 	}
66835ba63b8SArnd Bergmann 
66935ba63b8SArnd Bergmann 	fake_lm_check(bridge, addr, aspace, cycle);
67035ba63b8SArnd Bergmann }
67135ba63b8SArnd Bergmann 
fake_vmewrite32(struct fake_driver * bridge,u32 * buf,unsigned long long addr,u32 aspace,u32 cycle)67235ba63b8SArnd Bergmann static noinline_for_stack void fake_vmewrite32(struct fake_driver *bridge,
67335ba63b8SArnd Bergmann 					       u32 *buf, unsigned long long addr,
67435ba63b8SArnd Bergmann 					       u32 aspace, u32 cycle)
67535ba63b8SArnd Bergmann {
67635ba63b8SArnd Bergmann 	int i;
67735ba63b8SArnd Bergmann 	unsigned long long start, end, offset;
67835ba63b8SArnd Bergmann 	u32 *loc;
67935ba63b8SArnd Bergmann 
68035ba63b8SArnd Bergmann 	for (i = 0; i < FAKE_MAX_SLAVE; i++) {
68135ba63b8SArnd Bergmann 		if (aspace != bridge->slaves[i].aspace)
68235ba63b8SArnd Bergmann 			continue;
68335ba63b8SArnd Bergmann 
68435ba63b8SArnd Bergmann 		if (cycle != bridge->slaves[i].cycle)
68535ba63b8SArnd Bergmann 			continue;
68635ba63b8SArnd Bergmann 
68735ba63b8SArnd Bergmann 		start = bridge->slaves[i].vme_base;
68835ba63b8SArnd Bergmann 		end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
68935ba63b8SArnd Bergmann 
69035ba63b8SArnd Bergmann 		if ((addr >= start) && ((addr + 3) < end)) {
69135ba63b8SArnd Bergmann 			offset = addr - bridge->slaves[i].vme_base;
69235ba63b8SArnd Bergmann 			loc = (u32 *)((void *)bridge->slaves[i].buf_base + offset);
69335ba63b8SArnd Bergmann 			*loc = *buf;
69435ba63b8SArnd Bergmann 
69535ba63b8SArnd Bergmann 			break;
69635ba63b8SArnd Bergmann 		}
69735ba63b8SArnd Bergmann 	}
69835ba63b8SArnd Bergmann 
69935ba63b8SArnd Bergmann 	fake_lm_check(bridge, addr, aspace, cycle);
70035ba63b8SArnd Bergmann }
70135ba63b8SArnd Bergmann 
fake_master_write(struct vme_master_resource * image,void * buf,size_t count,loff_t offset)70235ba63b8SArnd Bergmann static ssize_t fake_master_write(struct vme_master_resource *image, void *buf,
70335ba63b8SArnd Bergmann 		size_t count, loff_t offset)
70435ba63b8SArnd Bergmann {
70535ba63b8SArnd Bergmann 	int retval = 0;
70635ba63b8SArnd Bergmann 	u32 aspace, cycle, dwidth;
70735ba63b8SArnd Bergmann 	unsigned long long addr;
70835ba63b8SArnd Bergmann 	int i;
70935ba63b8SArnd Bergmann 	unsigned int done = 0;
71035ba63b8SArnd Bergmann 	unsigned int count32;
71135ba63b8SArnd Bergmann 
71235ba63b8SArnd Bergmann 	struct vme_bridge *fake_bridge;
71335ba63b8SArnd Bergmann 	struct fake_driver *bridge;
71435ba63b8SArnd Bergmann 
71535ba63b8SArnd Bergmann 	fake_bridge = image->parent;
71635ba63b8SArnd Bergmann 
71735ba63b8SArnd Bergmann 	bridge = fake_bridge->driver_priv;
71835ba63b8SArnd Bergmann 
71935ba63b8SArnd Bergmann 	i = image->number;
72035ba63b8SArnd Bergmann 
72135ba63b8SArnd Bergmann 	addr = bridge->masters[i].vme_base + offset;
72235ba63b8SArnd Bergmann 	aspace = bridge->masters[i].aspace;
72335ba63b8SArnd Bergmann 	cycle = bridge->masters[i].cycle;
72435ba63b8SArnd Bergmann 	dwidth = bridge->masters[i].dwidth;
72535ba63b8SArnd Bergmann 
72635ba63b8SArnd Bergmann 	spin_lock(&image->lock);
72735ba63b8SArnd Bergmann 
72835ba63b8SArnd Bergmann 	/* Here we apply for the same strategy we do in master_read
72935ba63b8SArnd Bergmann 	 * function in order to assure the correct cycles.
73035ba63b8SArnd Bergmann 	 */
73135ba63b8SArnd Bergmann 	if (addr & 0x1) {
73235ba63b8SArnd Bergmann 		fake_vmewrite8(bridge, (u8 *)buf, addr, aspace, cycle);
73335ba63b8SArnd Bergmann 		done += 1;
73435ba63b8SArnd Bergmann 		if (done == count)
73535ba63b8SArnd Bergmann 			goto out;
73635ba63b8SArnd Bergmann 	}
73735ba63b8SArnd Bergmann 
73835ba63b8SArnd Bergmann 	if ((dwidth == VME_D16) || (dwidth == VME_D32)) {
73935ba63b8SArnd Bergmann 		if ((addr + done) & 0x2) {
74035ba63b8SArnd Bergmann 			if ((count - done) < 2) {
74135ba63b8SArnd Bergmann 				fake_vmewrite8(bridge, (u8 *)(buf + done),
74235ba63b8SArnd Bergmann 						addr + done, aspace, cycle);
74335ba63b8SArnd Bergmann 				done += 1;
74435ba63b8SArnd Bergmann 				goto out;
74535ba63b8SArnd Bergmann 			} else {
74635ba63b8SArnd Bergmann 				fake_vmewrite16(bridge, (u16 *)(buf + done),
74735ba63b8SArnd Bergmann 						addr + done, aspace, cycle);
74835ba63b8SArnd Bergmann 				done += 2;
74935ba63b8SArnd Bergmann 			}
75035ba63b8SArnd Bergmann 		}
75135ba63b8SArnd Bergmann 	}
75235ba63b8SArnd Bergmann 
75335ba63b8SArnd Bergmann 	if (dwidth == VME_D32) {
75435ba63b8SArnd Bergmann 		count32 = (count - done) & ~0x3;
75535ba63b8SArnd Bergmann 		while (done < count32) {
75635ba63b8SArnd Bergmann 			fake_vmewrite32(bridge, (u32 *)(buf + done),
75735ba63b8SArnd Bergmann 					addr + done, aspace, cycle);
75835ba63b8SArnd Bergmann 			done += 4;
75935ba63b8SArnd Bergmann 		}
76035ba63b8SArnd Bergmann 	} else if (dwidth == VME_D16) {
76135ba63b8SArnd Bergmann 		count32 = (count - done) & ~0x3;
76235ba63b8SArnd Bergmann 		while (done < count32) {
76335ba63b8SArnd Bergmann 			fake_vmewrite16(bridge, (u16 *)(buf + done),
76435ba63b8SArnd Bergmann 					addr + done, aspace, cycle);
76535ba63b8SArnd Bergmann 			done += 2;
76635ba63b8SArnd Bergmann 		}
76735ba63b8SArnd Bergmann 	} else if (dwidth == VME_D8) {
76835ba63b8SArnd Bergmann 		count32 = (count - done);
76935ba63b8SArnd Bergmann 		while (done < count32) {
77035ba63b8SArnd Bergmann 			fake_vmewrite8(bridge, (u8 *)(buf + done), addr + done,
77135ba63b8SArnd Bergmann 					aspace, cycle);
77235ba63b8SArnd Bergmann 			done += 1;
77335ba63b8SArnd Bergmann 		}
77435ba63b8SArnd Bergmann 
77535ba63b8SArnd Bergmann 	}
77635ba63b8SArnd Bergmann 
77735ba63b8SArnd Bergmann 	if ((dwidth == VME_D16) || (dwidth == VME_D32)) {
77835ba63b8SArnd Bergmann 		if ((count - done) & 0x2) {
77935ba63b8SArnd Bergmann 			fake_vmewrite16(bridge, (u16 *)(buf + done),
78035ba63b8SArnd Bergmann 					addr + done, aspace, cycle);
78135ba63b8SArnd Bergmann 			done += 2;
78235ba63b8SArnd Bergmann 		}
78335ba63b8SArnd Bergmann 	}
78435ba63b8SArnd Bergmann 
78535ba63b8SArnd Bergmann 	if ((count - done) & 0x1) {
78635ba63b8SArnd Bergmann 		fake_vmewrite8(bridge, (u8 *)(buf + done), addr + done, aspace,
78735ba63b8SArnd Bergmann 				cycle);
78835ba63b8SArnd Bergmann 		done += 1;
78935ba63b8SArnd Bergmann 	}
79035ba63b8SArnd Bergmann 
79135ba63b8SArnd Bergmann out:
79235ba63b8SArnd Bergmann 	retval = count;
79335ba63b8SArnd Bergmann 
79435ba63b8SArnd Bergmann 	spin_unlock(&image->lock);
79535ba63b8SArnd Bergmann 
79635ba63b8SArnd Bergmann 	return retval;
79735ba63b8SArnd Bergmann }
79835ba63b8SArnd Bergmann 
79935ba63b8SArnd Bergmann /*
80035ba63b8SArnd Bergmann  * Perform an RMW cycle on the VME bus.
80135ba63b8SArnd Bergmann  *
80235ba63b8SArnd Bergmann  * Requires a previously configured master window, returns final value.
80335ba63b8SArnd Bergmann  */
fake_master_rmw(struct vme_master_resource * image,unsigned int mask,unsigned int compare,unsigned int swap,loff_t offset)80435ba63b8SArnd Bergmann static unsigned int fake_master_rmw(struct vme_master_resource *image,
80535ba63b8SArnd Bergmann 		unsigned int mask, unsigned int compare, unsigned int swap,
80635ba63b8SArnd Bergmann 		loff_t offset)
80735ba63b8SArnd Bergmann {
80835ba63b8SArnd Bergmann 	u32 tmp, base;
80935ba63b8SArnd Bergmann 	u32 aspace, cycle;
81035ba63b8SArnd Bergmann 	int i;
81135ba63b8SArnd Bergmann 	struct fake_driver *bridge;
81235ba63b8SArnd Bergmann 
81335ba63b8SArnd Bergmann 	bridge = image->parent->driver_priv;
81435ba63b8SArnd Bergmann 
81535ba63b8SArnd Bergmann 	/* Find the PCI address that maps to the desired VME address */
81635ba63b8SArnd Bergmann 	i = image->number;
81735ba63b8SArnd Bergmann 
81835ba63b8SArnd Bergmann 	base = bridge->masters[i].vme_base;
81935ba63b8SArnd Bergmann 	aspace = bridge->masters[i].aspace;
82035ba63b8SArnd Bergmann 	cycle = bridge->masters[i].cycle;
82135ba63b8SArnd Bergmann 
82235ba63b8SArnd Bergmann 	/* Lock image */
82335ba63b8SArnd Bergmann 	spin_lock(&image->lock);
82435ba63b8SArnd Bergmann 
82535ba63b8SArnd Bergmann 	/* Read existing value */
82635ba63b8SArnd Bergmann 	tmp = fake_vmeread32(bridge, base + offset, aspace, cycle);
82735ba63b8SArnd Bergmann 
82835ba63b8SArnd Bergmann 	/* Perform check */
82935ba63b8SArnd Bergmann 	if ((tmp && mask) == (compare && mask)) {
83035ba63b8SArnd Bergmann 		tmp = tmp | (mask | swap);
83135ba63b8SArnd Bergmann 		tmp = tmp & (~mask | swap);
83235ba63b8SArnd Bergmann 
83335ba63b8SArnd Bergmann 		/* Write back */
83435ba63b8SArnd Bergmann 		fake_vmewrite32(bridge, &tmp, base + offset, aspace, cycle);
83535ba63b8SArnd Bergmann 	}
83635ba63b8SArnd Bergmann 
83735ba63b8SArnd Bergmann 	/* Unlock image */
83835ba63b8SArnd Bergmann 	spin_unlock(&image->lock);
83935ba63b8SArnd Bergmann 
84035ba63b8SArnd Bergmann 	return tmp;
84135ba63b8SArnd Bergmann }
84235ba63b8SArnd Bergmann 
84335ba63b8SArnd Bergmann /*
84435ba63b8SArnd Bergmann  * All 4 location monitors reside at the same base - this is therefore a
84535ba63b8SArnd Bergmann  * system wide configuration.
84635ba63b8SArnd Bergmann  *
84735ba63b8SArnd Bergmann  * This does not enable the LM monitor - that should be done when the first
84835ba63b8SArnd Bergmann  * callback is attached and disabled when the last callback is removed.
84935ba63b8SArnd Bergmann  */
fake_lm_set(struct vme_lm_resource * lm,unsigned long long lm_base,u32 aspace,u32 cycle)85035ba63b8SArnd Bergmann static int fake_lm_set(struct vme_lm_resource *lm, unsigned long long lm_base,
85135ba63b8SArnd Bergmann 		u32 aspace, u32 cycle)
85235ba63b8SArnd Bergmann {
85335ba63b8SArnd Bergmann 	int i;
85435ba63b8SArnd Bergmann 	struct vme_bridge *fake_bridge;
85535ba63b8SArnd Bergmann 	struct fake_driver *bridge;
85635ba63b8SArnd Bergmann 
85735ba63b8SArnd Bergmann 	fake_bridge = lm->parent;
85835ba63b8SArnd Bergmann 
85935ba63b8SArnd Bergmann 	bridge = fake_bridge->driver_priv;
86035ba63b8SArnd Bergmann 
86135ba63b8SArnd Bergmann 	mutex_lock(&lm->mtx);
86235ba63b8SArnd Bergmann 
86335ba63b8SArnd Bergmann 	/* If we already have a callback attached, we can't move it! */
86435ba63b8SArnd Bergmann 	for (i = 0; i < lm->monitors; i++) {
86535ba63b8SArnd Bergmann 		if (bridge->lm_callback[i]) {
86635ba63b8SArnd Bergmann 			mutex_unlock(&lm->mtx);
86735ba63b8SArnd Bergmann 			pr_err("Location monitor callback attached, can't reset\n");
86835ba63b8SArnd Bergmann 			return -EBUSY;
86935ba63b8SArnd Bergmann 		}
87035ba63b8SArnd Bergmann 	}
87135ba63b8SArnd Bergmann 
87235ba63b8SArnd Bergmann 	switch (aspace) {
87335ba63b8SArnd Bergmann 	case VME_A16:
87435ba63b8SArnd Bergmann 	case VME_A24:
87535ba63b8SArnd Bergmann 	case VME_A32:
87635ba63b8SArnd Bergmann 	case VME_A64:
87735ba63b8SArnd Bergmann 		break;
87835ba63b8SArnd Bergmann 	default:
87935ba63b8SArnd Bergmann 		mutex_unlock(&lm->mtx);
88035ba63b8SArnd Bergmann 		pr_err("Invalid address space\n");
88135ba63b8SArnd Bergmann 		return -EINVAL;
88235ba63b8SArnd Bergmann 	}
88335ba63b8SArnd Bergmann 
88435ba63b8SArnd Bergmann 	bridge->lm_base = lm_base;
88535ba63b8SArnd Bergmann 	bridge->lm_aspace = aspace;
88635ba63b8SArnd Bergmann 	bridge->lm_cycle = cycle;
88735ba63b8SArnd Bergmann 
88835ba63b8SArnd Bergmann 	mutex_unlock(&lm->mtx);
88935ba63b8SArnd Bergmann 
89035ba63b8SArnd Bergmann 	return 0;
89135ba63b8SArnd Bergmann }
89235ba63b8SArnd Bergmann 
89335ba63b8SArnd Bergmann /* Get configuration of the callback monitor and return whether it is enabled
89435ba63b8SArnd Bergmann  * or disabled.
89535ba63b8SArnd Bergmann  */
fake_lm_get(struct vme_lm_resource * lm,unsigned long long * lm_base,u32 * aspace,u32 * cycle)89635ba63b8SArnd Bergmann static int fake_lm_get(struct vme_lm_resource *lm,
89735ba63b8SArnd Bergmann 		unsigned long long *lm_base, u32 *aspace, u32 *cycle)
89835ba63b8SArnd Bergmann {
89935ba63b8SArnd Bergmann 	struct fake_driver *bridge;
90035ba63b8SArnd Bergmann 
90135ba63b8SArnd Bergmann 	bridge = lm->parent->driver_priv;
90235ba63b8SArnd Bergmann 
90335ba63b8SArnd Bergmann 	mutex_lock(&lm->mtx);
90435ba63b8SArnd Bergmann 
90535ba63b8SArnd Bergmann 	*lm_base = bridge->lm_base;
90635ba63b8SArnd Bergmann 	*aspace = bridge->lm_aspace;
90735ba63b8SArnd Bergmann 	*cycle = bridge->lm_cycle;
90835ba63b8SArnd Bergmann 
90935ba63b8SArnd Bergmann 	mutex_unlock(&lm->mtx);
91035ba63b8SArnd Bergmann 
91135ba63b8SArnd Bergmann 	return bridge->lm_enabled;
91235ba63b8SArnd Bergmann }
91335ba63b8SArnd Bergmann 
91435ba63b8SArnd Bergmann /*
91535ba63b8SArnd Bergmann  * Attach a callback to a specific location monitor.
91635ba63b8SArnd Bergmann  *
91735ba63b8SArnd Bergmann  * Callback will be passed the monitor triggered.
91835ba63b8SArnd Bergmann  */
fake_lm_attach(struct vme_lm_resource * lm,int monitor,void (* callback)(void *),void * data)91935ba63b8SArnd Bergmann static int fake_lm_attach(struct vme_lm_resource *lm, int monitor,
92035ba63b8SArnd Bergmann 		void (*callback)(void *), void *data)
92135ba63b8SArnd Bergmann {
92235ba63b8SArnd Bergmann 	struct vme_bridge *fake_bridge;
92335ba63b8SArnd Bergmann 	struct fake_driver *bridge;
92435ba63b8SArnd Bergmann 
92535ba63b8SArnd Bergmann 	fake_bridge = lm->parent;
92635ba63b8SArnd Bergmann 
92735ba63b8SArnd Bergmann 	bridge = fake_bridge->driver_priv;
92835ba63b8SArnd Bergmann 
92935ba63b8SArnd Bergmann 	mutex_lock(&lm->mtx);
93035ba63b8SArnd Bergmann 
93135ba63b8SArnd Bergmann 	/* Ensure that the location monitor is configured - need PGM or DATA */
93235ba63b8SArnd Bergmann 	if (bridge->lm_cycle == 0) {
93335ba63b8SArnd Bergmann 		mutex_unlock(&lm->mtx);
93435ba63b8SArnd Bergmann 		pr_err("Location monitor not properly configured\n");
93535ba63b8SArnd Bergmann 		return -EINVAL;
93635ba63b8SArnd Bergmann 	}
93735ba63b8SArnd Bergmann 
93835ba63b8SArnd Bergmann 	/* Check that a callback isn't already attached */
93935ba63b8SArnd Bergmann 	if (bridge->lm_callback[monitor]) {
94035ba63b8SArnd Bergmann 		mutex_unlock(&lm->mtx);
94135ba63b8SArnd Bergmann 		pr_err("Existing callback attached\n");
94235ba63b8SArnd Bergmann 		return -EBUSY;
94335ba63b8SArnd Bergmann 	}
94435ba63b8SArnd Bergmann 
94535ba63b8SArnd Bergmann 	/* Attach callback */
94635ba63b8SArnd Bergmann 	bridge->lm_callback[monitor] = callback;
94735ba63b8SArnd Bergmann 	bridge->lm_data[monitor] = data;
94835ba63b8SArnd Bergmann 
94935ba63b8SArnd Bergmann 	/* Ensure that global Location Monitor Enable set */
95035ba63b8SArnd Bergmann 	bridge->lm_enabled = 1;
95135ba63b8SArnd Bergmann 
95235ba63b8SArnd Bergmann 	mutex_unlock(&lm->mtx);
95335ba63b8SArnd Bergmann 
95435ba63b8SArnd Bergmann 	return 0;
95535ba63b8SArnd Bergmann }
95635ba63b8SArnd Bergmann 
95735ba63b8SArnd Bergmann /*
95835ba63b8SArnd Bergmann  * Detach a callback function forn a specific location monitor.
95935ba63b8SArnd Bergmann  */
fake_lm_detach(struct vme_lm_resource * lm,int monitor)96035ba63b8SArnd Bergmann static int fake_lm_detach(struct vme_lm_resource *lm, int monitor)
96135ba63b8SArnd Bergmann {
96235ba63b8SArnd Bergmann 	u32 tmp;
96335ba63b8SArnd Bergmann 	int i;
96435ba63b8SArnd Bergmann 	struct fake_driver *bridge;
96535ba63b8SArnd Bergmann 
96635ba63b8SArnd Bergmann 	bridge = lm->parent->driver_priv;
96735ba63b8SArnd Bergmann 
96835ba63b8SArnd Bergmann 	mutex_lock(&lm->mtx);
96935ba63b8SArnd Bergmann 
97035ba63b8SArnd Bergmann 	/* Detach callback */
97135ba63b8SArnd Bergmann 	bridge->lm_callback[monitor] = NULL;
97235ba63b8SArnd Bergmann 	bridge->lm_data[monitor] = NULL;
97335ba63b8SArnd Bergmann 
97435ba63b8SArnd Bergmann 	/* If all location monitors disabled, disable global Location Monitor */
97535ba63b8SArnd Bergmann 	tmp = 0;
97635ba63b8SArnd Bergmann 	for (i = 0; i < lm->monitors; i++) {
97735ba63b8SArnd Bergmann 		if (bridge->lm_callback[i])
97835ba63b8SArnd Bergmann 			tmp = 1;
97935ba63b8SArnd Bergmann 	}
98035ba63b8SArnd Bergmann 
98135ba63b8SArnd Bergmann 	if (tmp == 0)
98235ba63b8SArnd Bergmann 		bridge->lm_enabled = 0;
98335ba63b8SArnd Bergmann 
98435ba63b8SArnd Bergmann 	mutex_unlock(&lm->mtx);
98535ba63b8SArnd Bergmann 
98635ba63b8SArnd Bergmann 	return 0;
98735ba63b8SArnd Bergmann }
98835ba63b8SArnd Bergmann 
98935ba63b8SArnd Bergmann /*
99035ba63b8SArnd Bergmann  * Determine Geographical Addressing
99135ba63b8SArnd Bergmann  */
fake_slot_get(struct vme_bridge * fake_bridge)99235ba63b8SArnd Bergmann static int fake_slot_get(struct vme_bridge *fake_bridge)
99335ba63b8SArnd Bergmann {
99435ba63b8SArnd Bergmann 	return geoid;
99535ba63b8SArnd Bergmann }
99635ba63b8SArnd Bergmann 
fake_alloc_consistent(struct device * parent,size_t size,dma_addr_t * dma)99735ba63b8SArnd Bergmann static void *fake_alloc_consistent(struct device *parent, size_t size,
99835ba63b8SArnd Bergmann 		dma_addr_t *dma)
99935ba63b8SArnd Bergmann {
100035ba63b8SArnd Bergmann 	void *alloc = kmalloc(size, GFP_KERNEL);
100135ba63b8SArnd Bergmann 
100235ba63b8SArnd Bergmann 	if (alloc)
100335ba63b8SArnd Bergmann 		*dma = fake_ptr_to_pci(alloc);
100435ba63b8SArnd Bergmann 
100535ba63b8SArnd Bergmann 	return alloc;
100635ba63b8SArnd Bergmann }
100735ba63b8SArnd Bergmann 
fake_free_consistent(struct device * parent,size_t size,void * vaddr,dma_addr_t dma)100835ba63b8SArnd Bergmann static void fake_free_consistent(struct device *parent, size_t size,
100935ba63b8SArnd Bergmann 		void *vaddr, dma_addr_t dma)
101035ba63b8SArnd Bergmann {
101135ba63b8SArnd Bergmann 	kfree(vaddr);
101235ba63b8SArnd Bergmann /*
101335ba63b8SArnd Bergmann 	dma_free_coherent(parent, size, vaddr, dma);
101435ba63b8SArnd Bergmann */
101535ba63b8SArnd Bergmann }
101635ba63b8SArnd Bergmann 
101735ba63b8SArnd Bergmann /*
101835ba63b8SArnd Bergmann  * Configure CR/CSR space
101935ba63b8SArnd Bergmann  *
102035ba63b8SArnd Bergmann  * Access to the CR/CSR can be configured at power-up. The location of the
102135ba63b8SArnd Bergmann  * CR/CSR registers in the CR/CSR address space is determined by the boards
102235ba63b8SArnd Bergmann  * Geographic address.
102335ba63b8SArnd Bergmann  *
102435ba63b8SArnd Bergmann  * Each board has a 512kB window, with the highest 4kB being used for the
102535ba63b8SArnd Bergmann  * boards registers, this means there is a fix length 508kB window which must
102635ba63b8SArnd Bergmann  * be mapped onto PCI memory.
102735ba63b8SArnd Bergmann  */
fake_crcsr_init(struct vme_bridge * fake_bridge)102835ba63b8SArnd Bergmann static int fake_crcsr_init(struct vme_bridge *fake_bridge)
102935ba63b8SArnd Bergmann {
103035ba63b8SArnd Bergmann 	u32 vstat;
103135ba63b8SArnd Bergmann 	struct fake_driver *bridge;
103235ba63b8SArnd Bergmann 
103335ba63b8SArnd Bergmann 	bridge = fake_bridge->driver_priv;
103435ba63b8SArnd Bergmann 
103535ba63b8SArnd Bergmann 	/* Allocate mem for CR/CSR image */
103635ba63b8SArnd Bergmann 	bridge->crcsr_kernel = kzalloc(VME_CRCSR_BUF_SIZE, GFP_KERNEL);
103735ba63b8SArnd Bergmann 	bridge->crcsr_bus = fake_ptr_to_pci(bridge->crcsr_kernel);
103835ba63b8SArnd Bergmann 	if (!bridge->crcsr_kernel)
103935ba63b8SArnd Bergmann 		return -ENOMEM;
104035ba63b8SArnd Bergmann 
104135ba63b8SArnd Bergmann 	vstat = fake_slot_get(fake_bridge);
104235ba63b8SArnd Bergmann 
104335ba63b8SArnd Bergmann 	pr_info("CR/CSR Offset: %d\n", vstat);
104435ba63b8SArnd Bergmann 
104535ba63b8SArnd Bergmann 	return 0;
104635ba63b8SArnd Bergmann }
104735ba63b8SArnd Bergmann 
fake_crcsr_exit(struct vme_bridge * fake_bridge)104835ba63b8SArnd Bergmann static void fake_crcsr_exit(struct vme_bridge *fake_bridge)
104935ba63b8SArnd Bergmann {
105035ba63b8SArnd Bergmann 	struct fake_driver *bridge;
105135ba63b8SArnd Bergmann 
105235ba63b8SArnd Bergmann 	bridge = fake_bridge->driver_priv;
105335ba63b8SArnd Bergmann 
105435ba63b8SArnd Bergmann 	kfree(bridge->crcsr_kernel);
105535ba63b8SArnd Bergmann }
105635ba63b8SArnd Bergmann 
fake_init(void)105735ba63b8SArnd Bergmann static int __init fake_init(void)
105835ba63b8SArnd Bergmann {
105935ba63b8SArnd Bergmann 	int retval, i;
106035ba63b8SArnd Bergmann 	struct list_head *pos = NULL, *n;
106135ba63b8SArnd Bergmann 	struct vme_bridge *fake_bridge;
106235ba63b8SArnd Bergmann 	struct fake_driver *fake_device;
106335ba63b8SArnd Bergmann 	struct vme_master_resource *master_image;
106435ba63b8SArnd Bergmann 	struct vme_slave_resource *slave_image;
106535ba63b8SArnd Bergmann 	struct vme_lm_resource *lm;
106635ba63b8SArnd Bergmann 
106735ba63b8SArnd Bergmann 	/* We need a fake parent device */
1068*37aa6b98SChen Zhongjin 	vme_root = root_device_register("vme");
10697bef797dSChen Zhongjin 	if (IS_ERR(vme_root))
10707bef797dSChen Zhongjin 		return PTR_ERR(vme_root);
107135ba63b8SArnd Bergmann 
107235ba63b8SArnd Bergmann 	/* If we want to support more than one bridge at some point, we need to
107335ba63b8SArnd Bergmann 	 * dynamically allocate this so we get one per device.
107435ba63b8SArnd Bergmann 	 */
107535ba63b8SArnd Bergmann 	fake_bridge = kzalloc(sizeof(*fake_bridge), GFP_KERNEL);
107635ba63b8SArnd Bergmann 	if (!fake_bridge) {
107735ba63b8SArnd Bergmann 		retval = -ENOMEM;
107835ba63b8SArnd Bergmann 		goto err_struct;
107935ba63b8SArnd Bergmann 	}
108035ba63b8SArnd Bergmann 
108135ba63b8SArnd Bergmann 	fake_device = kzalloc(sizeof(*fake_device), GFP_KERNEL);
108235ba63b8SArnd Bergmann 	if (!fake_device) {
108335ba63b8SArnd Bergmann 		retval = -ENOMEM;
108435ba63b8SArnd Bergmann 		goto err_driver;
108535ba63b8SArnd Bergmann 	}
108635ba63b8SArnd Bergmann 
108735ba63b8SArnd Bergmann 	fake_bridge->driver_priv = fake_device;
108835ba63b8SArnd Bergmann 
108935ba63b8SArnd Bergmann 	fake_bridge->parent = vme_root;
109035ba63b8SArnd Bergmann 
109135ba63b8SArnd Bergmann 	fake_device->parent = fake_bridge;
109235ba63b8SArnd Bergmann 
109335ba63b8SArnd Bergmann 	/* Initialize wait queues & mutual exclusion flags */
109435ba63b8SArnd Bergmann 	mutex_init(&fake_device->vme_int);
109535ba63b8SArnd Bergmann 	mutex_init(&fake_bridge->irq_mtx);
109635ba63b8SArnd Bergmann 	tasklet_init(&fake_device->int_tasklet, fake_VIRQ_tasklet,
109735ba63b8SArnd Bergmann 			(unsigned long) fake_bridge);
109835ba63b8SArnd Bergmann 
109935ba63b8SArnd Bergmann 	strcpy(fake_bridge->name, driver_name);
110035ba63b8SArnd Bergmann 
110135ba63b8SArnd Bergmann 	/* Add master windows to list */
110235ba63b8SArnd Bergmann 	INIT_LIST_HEAD(&fake_bridge->master_resources);
110335ba63b8SArnd Bergmann 	for (i = 0; i < FAKE_MAX_MASTER; i++) {
110435ba63b8SArnd Bergmann 		master_image = kmalloc(sizeof(*master_image), GFP_KERNEL);
110535ba63b8SArnd Bergmann 		if (!master_image) {
110635ba63b8SArnd Bergmann 			retval = -ENOMEM;
110735ba63b8SArnd Bergmann 			goto err_master;
110835ba63b8SArnd Bergmann 		}
110935ba63b8SArnd Bergmann 		master_image->parent = fake_bridge;
111035ba63b8SArnd Bergmann 		spin_lock_init(&master_image->lock);
111135ba63b8SArnd Bergmann 		master_image->locked = 0;
111235ba63b8SArnd Bergmann 		master_image->number = i;
111335ba63b8SArnd Bergmann 		master_image->address_attr = VME_A16 | VME_A24 | VME_A32 |
111435ba63b8SArnd Bergmann 			VME_A64;
111535ba63b8SArnd Bergmann 		master_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT |
111635ba63b8SArnd Bergmann 			VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 |
111735ba63b8SArnd Bergmann 			VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER |
111835ba63b8SArnd Bergmann 			VME_PROG | VME_DATA;
111935ba63b8SArnd Bergmann 		master_image->width_attr = VME_D16 | VME_D32;
112035ba63b8SArnd Bergmann 		memset(&master_image->bus_resource, 0,
112135ba63b8SArnd Bergmann 				sizeof(struct resource));
112235ba63b8SArnd Bergmann 		master_image->kern_base  = NULL;
112335ba63b8SArnd Bergmann 		list_add_tail(&master_image->list,
112435ba63b8SArnd Bergmann 				&fake_bridge->master_resources);
112535ba63b8SArnd Bergmann 	}
112635ba63b8SArnd Bergmann 
112735ba63b8SArnd Bergmann 	/* Add slave windows to list */
112835ba63b8SArnd Bergmann 	INIT_LIST_HEAD(&fake_bridge->slave_resources);
112935ba63b8SArnd Bergmann 	for (i = 0; i < FAKE_MAX_SLAVE; i++) {
113035ba63b8SArnd Bergmann 		slave_image = kmalloc(sizeof(*slave_image), GFP_KERNEL);
113135ba63b8SArnd Bergmann 		if (!slave_image) {
113235ba63b8SArnd Bergmann 			retval = -ENOMEM;
113335ba63b8SArnd Bergmann 			goto err_slave;
113435ba63b8SArnd Bergmann 		}
113535ba63b8SArnd Bergmann 		slave_image->parent = fake_bridge;
113635ba63b8SArnd Bergmann 		mutex_init(&slave_image->mtx);
113735ba63b8SArnd Bergmann 		slave_image->locked = 0;
113835ba63b8SArnd Bergmann 		slave_image->number = i;
113935ba63b8SArnd Bergmann 		slave_image->address_attr = VME_A16 | VME_A24 | VME_A32 |
114035ba63b8SArnd Bergmann 			VME_A64 | VME_CRCSR | VME_USER1 | VME_USER2 |
114135ba63b8SArnd Bergmann 			VME_USER3 | VME_USER4;
114235ba63b8SArnd Bergmann 		slave_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT |
114335ba63b8SArnd Bergmann 			VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 |
114435ba63b8SArnd Bergmann 			VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER |
114535ba63b8SArnd Bergmann 			VME_PROG | VME_DATA;
114635ba63b8SArnd Bergmann 		list_add_tail(&slave_image->list,
114735ba63b8SArnd Bergmann 				&fake_bridge->slave_resources);
114835ba63b8SArnd Bergmann 	}
114935ba63b8SArnd Bergmann 
115035ba63b8SArnd Bergmann 	/* Add location monitor to list */
115135ba63b8SArnd Bergmann 	INIT_LIST_HEAD(&fake_bridge->lm_resources);
115235ba63b8SArnd Bergmann 	lm = kmalloc(sizeof(*lm), GFP_KERNEL);
115335ba63b8SArnd Bergmann 	if (!lm) {
115435ba63b8SArnd Bergmann 		retval = -ENOMEM;
115535ba63b8SArnd Bergmann 		goto err_lm;
115635ba63b8SArnd Bergmann 	}
115735ba63b8SArnd Bergmann 	lm->parent = fake_bridge;
115835ba63b8SArnd Bergmann 	mutex_init(&lm->mtx);
115935ba63b8SArnd Bergmann 	lm->locked = 0;
116035ba63b8SArnd Bergmann 	lm->number = 1;
116135ba63b8SArnd Bergmann 	lm->monitors = 4;
116235ba63b8SArnd Bergmann 	list_add_tail(&lm->list, &fake_bridge->lm_resources);
116335ba63b8SArnd Bergmann 
116435ba63b8SArnd Bergmann 	fake_bridge->slave_get = fake_slave_get;
116535ba63b8SArnd Bergmann 	fake_bridge->slave_set = fake_slave_set;
116635ba63b8SArnd Bergmann 	fake_bridge->master_get = fake_master_get;
116735ba63b8SArnd Bergmann 	fake_bridge->master_set = fake_master_set;
116835ba63b8SArnd Bergmann 	fake_bridge->master_read = fake_master_read;
116935ba63b8SArnd Bergmann 	fake_bridge->master_write = fake_master_write;
117035ba63b8SArnd Bergmann 	fake_bridge->master_rmw = fake_master_rmw;
117135ba63b8SArnd Bergmann 	fake_bridge->irq_set = fake_irq_set;
117235ba63b8SArnd Bergmann 	fake_bridge->irq_generate = fake_irq_generate;
117335ba63b8SArnd Bergmann 	fake_bridge->lm_set = fake_lm_set;
117435ba63b8SArnd Bergmann 	fake_bridge->lm_get = fake_lm_get;
117535ba63b8SArnd Bergmann 	fake_bridge->lm_attach = fake_lm_attach;
117635ba63b8SArnd Bergmann 	fake_bridge->lm_detach = fake_lm_detach;
117735ba63b8SArnd Bergmann 	fake_bridge->slot_get = fake_slot_get;
117835ba63b8SArnd Bergmann 	fake_bridge->alloc_consistent = fake_alloc_consistent;
117935ba63b8SArnd Bergmann 	fake_bridge->free_consistent = fake_free_consistent;
118035ba63b8SArnd Bergmann 
118135ba63b8SArnd Bergmann 	pr_info("Board is%s the VME system controller\n",
118235ba63b8SArnd Bergmann 			(geoid == 1) ? "" : " not");
118335ba63b8SArnd Bergmann 
118435ba63b8SArnd Bergmann 	pr_info("VME geographical address is set to %d\n", geoid);
118535ba63b8SArnd Bergmann 
118635ba63b8SArnd Bergmann 	retval = fake_crcsr_init(fake_bridge);
118735ba63b8SArnd Bergmann 	if (retval) {
118835ba63b8SArnd Bergmann 		pr_err("CR/CSR configuration failed.\n");
118935ba63b8SArnd Bergmann 		goto err_crcsr;
119035ba63b8SArnd Bergmann 	}
119135ba63b8SArnd Bergmann 
119235ba63b8SArnd Bergmann 	retval = vme_register_bridge(fake_bridge);
119335ba63b8SArnd Bergmann 	if (retval != 0) {
119435ba63b8SArnd Bergmann 		pr_err("Chip Registration failed.\n");
119535ba63b8SArnd Bergmann 		goto err_reg;
119635ba63b8SArnd Bergmann 	}
119735ba63b8SArnd Bergmann 
119835ba63b8SArnd Bergmann 	exit_pointer = fake_bridge;
119935ba63b8SArnd Bergmann 
120035ba63b8SArnd Bergmann 	return 0;
120135ba63b8SArnd Bergmann 
120235ba63b8SArnd Bergmann err_reg:
120335ba63b8SArnd Bergmann 	fake_crcsr_exit(fake_bridge);
120435ba63b8SArnd Bergmann err_crcsr:
120535ba63b8SArnd Bergmann err_lm:
120635ba63b8SArnd Bergmann 	/* resources are stored in link list */
120735ba63b8SArnd Bergmann 	list_for_each_safe(pos, n, &fake_bridge->lm_resources) {
120835ba63b8SArnd Bergmann 		lm = list_entry(pos, struct vme_lm_resource, list);
120935ba63b8SArnd Bergmann 		list_del(pos);
121035ba63b8SArnd Bergmann 		kfree(lm);
121135ba63b8SArnd Bergmann 	}
121235ba63b8SArnd Bergmann err_slave:
121335ba63b8SArnd Bergmann 	/* resources are stored in link list */
121435ba63b8SArnd Bergmann 	list_for_each_safe(pos, n, &fake_bridge->slave_resources) {
121535ba63b8SArnd Bergmann 		slave_image = list_entry(pos, struct vme_slave_resource, list);
121635ba63b8SArnd Bergmann 		list_del(pos);
121735ba63b8SArnd Bergmann 		kfree(slave_image);
121835ba63b8SArnd Bergmann 	}
121935ba63b8SArnd Bergmann err_master:
122035ba63b8SArnd Bergmann 	/* resources are stored in link list */
122135ba63b8SArnd Bergmann 	list_for_each_safe(pos, n, &fake_bridge->master_resources) {
122235ba63b8SArnd Bergmann 		master_image = list_entry(pos, struct vme_master_resource,
122335ba63b8SArnd Bergmann 				list);
122435ba63b8SArnd Bergmann 		list_del(pos);
122535ba63b8SArnd Bergmann 		kfree(master_image);
122635ba63b8SArnd Bergmann 	}
122735ba63b8SArnd Bergmann 
122835ba63b8SArnd Bergmann 	kfree(fake_device);
122935ba63b8SArnd Bergmann err_driver:
123035ba63b8SArnd Bergmann 	kfree(fake_bridge);
123135ba63b8SArnd Bergmann err_struct:
123235ba63b8SArnd Bergmann 	return retval;
123335ba63b8SArnd Bergmann }
123435ba63b8SArnd Bergmann 
fake_exit(void)123535ba63b8SArnd Bergmann static void __exit fake_exit(void)
123635ba63b8SArnd Bergmann {
123735ba63b8SArnd Bergmann 	struct list_head *pos = NULL;
123835ba63b8SArnd Bergmann 	struct list_head *tmplist;
123935ba63b8SArnd Bergmann 	struct vme_master_resource *master_image;
124035ba63b8SArnd Bergmann 	struct vme_slave_resource *slave_image;
124135ba63b8SArnd Bergmann 	int i;
124235ba63b8SArnd Bergmann 	struct vme_bridge *fake_bridge;
124335ba63b8SArnd Bergmann 	struct fake_driver *bridge;
124435ba63b8SArnd Bergmann 
124535ba63b8SArnd Bergmann 	fake_bridge = exit_pointer;
124635ba63b8SArnd Bergmann 
124735ba63b8SArnd Bergmann 	bridge = fake_bridge->driver_priv;
124835ba63b8SArnd Bergmann 
124935ba63b8SArnd Bergmann 	pr_debug("Driver is being unloaded.\n");
125035ba63b8SArnd Bergmann 
125135ba63b8SArnd Bergmann 	/*
125235ba63b8SArnd Bergmann 	 *  Shutdown all inbound and outbound windows.
125335ba63b8SArnd Bergmann 	 */
125435ba63b8SArnd Bergmann 	for (i = 0; i < FAKE_MAX_MASTER; i++)
125535ba63b8SArnd Bergmann 		bridge->masters[i].enabled = 0;
125635ba63b8SArnd Bergmann 
125735ba63b8SArnd Bergmann 	for (i = 0; i < FAKE_MAX_SLAVE; i++)
125835ba63b8SArnd Bergmann 		bridge->slaves[i].enabled = 0;
125935ba63b8SArnd Bergmann 
126035ba63b8SArnd Bergmann 	/*
126135ba63b8SArnd Bergmann 	 *  Shutdown Location monitor.
126235ba63b8SArnd Bergmann 	 */
126335ba63b8SArnd Bergmann 	bridge->lm_enabled = 0;
126435ba63b8SArnd Bergmann 
126535ba63b8SArnd Bergmann 	vme_unregister_bridge(fake_bridge);
126635ba63b8SArnd Bergmann 
126735ba63b8SArnd Bergmann 	fake_crcsr_exit(fake_bridge);
126835ba63b8SArnd Bergmann 	/* resources are stored in link list */
126935ba63b8SArnd Bergmann 	list_for_each_safe(pos, tmplist, &fake_bridge->slave_resources) {
127035ba63b8SArnd Bergmann 		slave_image = list_entry(pos, struct vme_slave_resource, list);
127135ba63b8SArnd Bergmann 		list_del(pos);
127235ba63b8SArnd Bergmann 		kfree(slave_image);
127335ba63b8SArnd Bergmann 	}
127435ba63b8SArnd Bergmann 
127535ba63b8SArnd Bergmann 	/* resources are stored in link list */
127635ba63b8SArnd Bergmann 	list_for_each_safe(pos, tmplist, &fake_bridge->master_resources) {
127735ba63b8SArnd Bergmann 		master_image = list_entry(pos, struct vme_master_resource,
127835ba63b8SArnd Bergmann 				list);
127935ba63b8SArnd Bergmann 		list_del(pos);
128035ba63b8SArnd Bergmann 		kfree(master_image);
128135ba63b8SArnd Bergmann 	}
128235ba63b8SArnd Bergmann 
128335ba63b8SArnd Bergmann 	kfree(fake_bridge->driver_priv);
128435ba63b8SArnd Bergmann 
128535ba63b8SArnd Bergmann 	kfree(fake_bridge);
128635ba63b8SArnd Bergmann 
128735ba63b8SArnd Bergmann 	root_device_unregister(vme_root);
128835ba63b8SArnd Bergmann }
128935ba63b8SArnd Bergmann 
129035ba63b8SArnd Bergmann MODULE_PARM_DESC(geoid, "Set geographical addressing");
129135ba63b8SArnd Bergmann module_param(geoid, int, 0);
129235ba63b8SArnd Bergmann 
129335ba63b8SArnd Bergmann MODULE_DESCRIPTION("Fake VME bridge driver");
129435ba63b8SArnd Bergmann MODULE_LICENSE("GPL");
129535ba63b8SArnd Bergmann 
129635ba63b8SArnd Bergmann module_init(fake_init);
129735ba63b8SArnd Bergmann module_exit(fake_exit);
1298