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