// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Fake VME bridge support.
*
* This drive provides a fake VME bridge chip, this enables debugging of the
* VME framework in the absence of a VME system.
*
* This driver has to do a number of things in software that would be driven
* by hardware if it was available, it will also result in extra overhead at
* times when compared with driving actual hardware.
*
* Author: Martyn Welch <martyn@welches.me.uk>
* Copyright (c) 2014 Martyn Welch
*
* Based on vme_tsi148.c:
*
* Author: Martyn Welch <martyn.welch@ge.com>
* Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc.
*
* Based on work by Tom Armistead and Ajit Prem
* Copyright 2004 Motorola Inc.
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/vme.h>
#include "../vme_bridge.h"
/*
* Define the number of each that the fake driver supports.
*/
#define FAKE_MAX_MASTER 8 /* Max Master Windows */
#define FAKE_MAX_SLAVE 8 /* Max Slave Windows */
/* Structures to hold information normally held in device registers */
struct fake_slave_window {
int enabled;
unsigned long long vme_base;
unsigned long long size;
void *buf_base;
u32 aspace;
u32 cycle;
};
struct fake_master_window {
int enabled;
unsigned long long vme_base;
unsigned long long size;
u32 aspace;
u32 cycle;
u32 dwidth;
};
/* Structure used to hold driver specific information */
struct fake_driver {
struct vme_bridge *parent;
struct fake_slave_window slaves[FAKE_MAX_SLAVE];
struct fake_master_window masters[FAKE_MAX_MASTER];
u32 lm_enabled;
unsigned long long lm_base;
u32 lm_aspace;
u32 lm_cycle;
void (*lm_callback[4])(void *);
void *lm_data[4];
struct tasklet_struct int_tasklet;
int int_level;
int int_statid;
void *crcsr_kernel;
dma_addr_t crcsr_bus;
/* Only one VME interrupt can be generated at a time, provide locking */
struct mutex vme_int;
};
/* Module parameter */
static int geoid;
static const char driver_name[] = "vme_fake";
static struct vme_bridge *exit_pointer;
static struct device *vme_root;
/*
* Calling VME bus interrupt callback if provided.
*/
static void fake_VIRQ_tasklet(unsigned long data)
{
struct vme_bridge *fake_bridge;
struct fake_driver *bridge;
fake_bridge = (struct vme_bridge *) data;
bridge = fake_bridge->driver_priv;
vme_irq_handler(fake_bridge, bridge->int_level, bridge->int_statid);
}
/*
* Configure VME interrupt
*/
static void fake_irq_set(struct vme_bridge *fake_bridge, int level,
int state, int sync)
{