/*
 * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

/*!
 * @file linux_port.h
 *
 * OS_PORT ported to Linux (2.6.9+ for now)
 *
 */

 /*!
  * @if USE_MAINPAGE
  * @mainpage ==Linux version of== Generic OS API for STC Drivers
  * @endif
  *
  * @section intro_sec Introduction
  *
  * This API / kernel programming environment blah blah.
  *
  * See @ref dkops "Driver-to-Kernel Operations" as a good place to start.
  */

#ifndef LINUX_PORT_H
#define LINUX_PORT_H

#define PORTABLE_OS_VERSION 101

/* Linux Kernel Includes */
#include <linux/version.h>	/* Current version Linux kernel */

#if defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS)
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
#include <linux/modversions.h>
#endif
#define MODVERSIONS
#endif
/*!
 * __NO_VERSION__ defined due to Kernel module possibly spanning multiple
 * files.
 */
#define __NO_VERSION__

#include <linux/module.h>	/* Basic support for loadable modules,
				   printk */
#include <linux/init.h>		/* module_init, module_exit */
#include <linux/kernel.h>	/* General kernel system calls */
#include <linux/sched.h>	/* for interrupt.h */
#include <linux/fs.h>		/* for inode */
#include <linux/random.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/slab.h>		/* kmalloc */

#include <stdarg.h>

#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
#include <linux/device.h>  /* used in dynamic power management */
#else
#include <linux/platform_device.h>  /* used in dynamic power management */
#endif

#include <linux/dmapool.h>
#include <linux/dma-mapping.h>

#include <linux/clk.h>         /* clock en/disable for DPM */

#include <linux/dmapool.h>
#include <linux/dma-mapping.h>

#include <asm/uaccess.h>            /* copy_to_user(), copy_from_user() */
#include <asm/io.h>                 /* ioremap() */
#include <asm/irq.h>
#include <asm/cacheflush.h>

#ifndef TRUE
/*! Useful symbol for unsigned values used as flags. */
#define TRUE 1
#endif

#ifndef FALSE
/*! Useful symbol for unsigned values used as flags. */
#define FALSE 0
#endif

/* These symbols are defined in Linux 2.6 and later.  Include here for minimal
 * support of 2.4 kernel.
 **/
#if !defined(LINUX_VERSION_CODE) || LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
/*!
 * Symbol defined somewhere in 2.5/2.6.  It is the return signature of an ISR.
 */
#define irqreturn_t void
/*! Possible return value of 'modern' ISR routine. */
#define IRQ_HANDLED
/*! Method of generating value of 'modern' ISR routine. */
#define IRQ_RETVAL(x)
#endif

/*!
 * Type used for registering and deregistering interrupts.
 */
typedef int os_interrupt_id_t;

/*!
 * Type used as handle for a process
 *
 * See #os_get_process_handle() and #os_send_signal().
 */
/*
 * The following should be defined this way, but it gets compiler errors
 * on the current tool chain.
 *
 * typedef task_t *os_process_handle_t;
 */

#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
typedef task_t *os_process_handle_t;
#else
typedef struct task_struct *os_process_handle_t;
#endif

/*!
 * Generic return code for functions which need such a thing.
 *
 * No knowledge should be assumed of the value of any of these symbols except
 * that @c OS_ERROR_OK_S is guaranteed to be zero.
 */
typedef enum {
	OS_ERROR_OK_S = 0,	/*!< Success  */
	OS_ERROR_FAIL_S = -EIO,	/*!< Generic driver failure */
	OS_ERROR_NO_MEMORY_S = -ENOMEM,	/*!< Failure to acquire/use memory  */
	OS_ERROR_BAD_ADDRESS_S = -EFAULT,	/*!< Bad address  */
	OS_ERROR_BAD_ARG_S = -EINVAL,	/*!< Bad input argument  */
} os_error_code;

/*!
 * Handle to a lock.
 */
#ifdef CONFIG_PREEMPT_RT
typedef raw_spinlock_t *os_lock_t;
#else
typedef spinlock_t *os_lock_t;
#endif

/*!
 * Context while locking.
 */
typedef unsigned long os_lock_context_t;

/*!
 * Declare a wait object for sleeping/waking processes.
 */
#define OS_WAIT_OBJECT(name)                                            \
    DECLARE_WAIT_QUEUE_HEAD(name##_qh)

/*!
 * Driver registration handle
 *
 * Used with #os_driver_init_registration(), #os_driver_add_registration(),
 * and #os_driver_complete_registration().
 */
typedef struct {
	unsigned reg_complete;	/*!< TRUE if next inits succeeded. */
	dev_t dev;		/*!< dev_t for register_chrdev() */
	struct file_operations fops;	/*!< struct for register_chrdev() */
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
	struct class_simple *cs;	/*!< results of class_simple_create() */
#else
	struct class *cs;	/*!< results of class_create() */
#endif
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
	struct class_device *cd;	/*!< Result of class_device_create() */
#else
	struct device *cd;	/*!< Result of device_create() */
#endif
	unsigned power_complete;	/*!< TRUE if next inits succeeded */
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
	struct device_driver dd;	/*!< struct for register_driver() */
#else
	struct platform_driver dd;	/*!< struct for register_driver() */
#endif
	struct platform_device pd;	/*!< struct for platform_register_device() */
} os_driver_reg_t;

/*
 *  Function types which can be associated with driver entry points.
 *
 *  Note that init and shutdown are absent.
 */
/*! @{ */
/*! Keyword for registering open() operation handler. */
#define OS_FN_OPEN open
/*! Keyword for registering close() operation handler. */
#define OS_FN_CLOSE release
/*! Keyword for registering read() operation handler. */
#define OS_FN_READ read
/*! Keyword for registering write() operation handler. */
#define OS_FN_WRITE write
/*! Keyword for registering ioctl() operation handler. */
#define OS_FN_IOCTL ioctl
/*! Keyword for registering mmap() operation handler. */
#define OS_FN_MMAP mmap
/*! @} */

/*!
 * Function signature for the portable interrupt handler
 *
 * While it would be nice to know which interrupt is being serviced, the
 * Least Common Denominator rule says that no arguments get passed in.
 *
 * @return  Zero if not handled, non-zero if handled.
 */
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
typedef int (*os_interrupt_handler_t) (int, void *, struct pt_regs *);
#else
typedef int (*os_interrupt_handler_t) (int, void *);
#endif

/*!
 * @defgroup dkops Driver-to-Kernel Operations
 *
 * These are the operations which drivers should call to get the OS to perform
 * services.
 */

/*! @addtogroup dkops */
/*! @{ */

/*!
 * Register an interrupt handler.
 *
 * @param driver_name    The name of the driver
 * @param interrupt_id   The interrupt line to monitor (type
 *                       #os_interrupt_id_t)
 * @param function       The function to be called to handle an interrupt
 *
 * @return #os_error_code
 */
#define os_register_interrupt(driver_name, interrupt_id, function)            \
     request_irq(interrupt_id, function, 0, driver_name, NULL)

/*!
 * Deregister an interrupt handler.
 *
 * @param interrupt_id   The interrupt line to stop monitoring
 *
 * @return #os_error_code
 */
#define os_deregister_interrupt(interrupt_id)                                 \
     free_irq(interrupt_id, NULL)

/*!
 * INTERNAL implementation of os_driver_init_registration()
 *
 * @return An os error code.
 */
inline static int os_drv_do_init_reg(os_driver_reg_t * handle)
{
	memset(handle, 0, sizeof(*handle));
	handle->fops.owner = THIS_MODULE;
	handle->power_complete = FALSE;
	handle->reg_complete = FALSE;
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
	handle->dd.name = NULL;
#else
	handle->dd.driver.name = NULL;
#endif

	return OS_ERROR_OK_S;
}

/*!
 * Initialize driver registration.
 *
 * If the driver handles open(), close(), ioctl(), read(), write(), or mmap()
 * calls, then it needs to register their location with the kernel so that they
 * get associated with the device.
 *
 * @param handle  The handle object to be used with this registration.  The
 *                object must live (be in memory somewhere) at least until
 *                os_driver_remove_registration() is called.
 *
 * @return A handle for further driver registration, or NULL if failed.
 */
#define os_driver_init_registration(handle)                                   \
    os_drv_do_init_reg(&handle)

/*!
 * Add a function registration to driver registration.
 *
 * @param handle    A handle initialized by #os_driver_init_registration().
 * @param name      Which function is being supported.
 * @param function  The result of a call to a @c _REF version of one of the
 *                  driver function signature macros
 * @return void
 */
#define os_driver_add_registration(handle, name, function)                    \
    do {handle.fops.name = (void*)(function); } while (0)

/*!
 * Record 'power suspend' function for the device.
 *
 * @param handle    A handle initialized by #os_driver_init_registration().
 * @param function  Name of function to call on power suspend request
 *
 * Status: Provisional
 *
 * @return void
 */
#define os_driver_register_power_suspend(handle, function)                    \
   handle.dd.suspend = function

/*!
 * Record 'power resume' function for the device.
 *
 * @param handle    A handle initialized by #os_driver_init_registration().
 * @param function  Name of function to call on power resume request
 *
 * Status: Provisional
 *
 * @return void
 */
#define os_driver_register_resume(handle, function)                          \
   handle.dd.resume = function

/*!
 * INTERNAL function of the Linux port of the OS API.   Implements the
 * os_driver_complete_registration() function.
 *
 * @param handle    The handle used with #os_driver_init_registration().
 * @param major      The major device number to be associated with the driver.
 *                   If this value is zero, a major number may be assigned.
 *                   See #os_driver_get_major() to determine final value.
 *                   #os_driver_remove_registration().
 * @param driver_name The driver name.  Can be used as part of 'device node'
 *                   name on platforms which support such a feature.
 *
 * @return  An error code
 */
inline static int os_drv_do_reg(os_driver_reg_t * handle,
				unsigned major, char *driver_name)
{
	os_error_code code = OS_ERROR_NO_MEMORY_S;
	char *name = kmalloc(strlen(driver_name) + 1, 0);

	if (name != NULL) {
		memcpy(name, driver_name, strlen(driver_name) + 1);
		code = OS_ERROR_OK_S;	/* OK so far */
		/* If any chardev/POSIX routines were added, then do chrdev part */
		if (handle->fops.open || handle->fops.release
		    || handle->fops.read || handle->fops.write
		    || handle->fops.ioctl || handle->fops.mmap) {

			printk("ioctl pointer: %p.  mmap pointer: %p\n",
			       handle->fops.ioctl, handle->fops.mmap);

			/* this method is depricated, see:
			 * http://lwn.net/Articles/126808/
			 */
			code =
			    register_chrdev(major, driver_name, &handle->fops);

			/* instead something like this: */
#if 0
			handle->dev = MKDEV(major, 0);
			code =
			    register_chrdev_region(handle->dev, 1, driver_name);
			if (code < 0) {
				code = OS_ERROR_FAIL_S;
			} else {
				cdev_init(&handle->cdev, &handle->fops);
				code = cdev_add(&handle->cdev, major, 1);
			}
#endif

			if (code < 0) {
				code = OS_ERROR_FAIL_S;
			} else {
				if (code != 0) {
					/* Zero was passed in for major; code is actual value */
					handle->dev = MKDEV(code, 0);
				} else {
					handle->dev = MKDEV(major, 0);
				}
				code = OS_ERROR_OK_S;
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
				handle->cs =
				    class_simple_create(THIS_MODULE,
							driver_name);
				if (IS_ERR(handle->cs)) {
					code = (os_error_code) handle->cs;
					handle->cs = NULL;
				} else {
					handle->cd =
					    class_simple_device_add(handle->cs,
								    handle->dev,
								    NULL,
								    driver_name);
					if (IS_ERR(handle->cd)) {
						class_simple_device_remove
						    (handle->dev);
						unregister_chrdev(MAJOR
								  (handle->dev),
								  driver_name);
						code =
						    (os_error_code) handle->cs;
						handle->cs = NULL;
					} else {
						handle->reg_complete = TRUE;
					}
				}
#else
				handle->cs =
				    class_create(THIS_MODULE, driver_name);
				if (IS_ERR(handle->cs)) {
					code = (os_error_code) handle->cs;
					handle->cs = NULL;
				} else {
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
					handle->cd =
					    class_device_create(handle->cs,
								NULL,
								handle->dev,
								NULL,
								driver_name);
#else
					handle->cd =
					    device_create(handle->cs, NULL,
							  handle->dev, NULL,
							  driver_name);
#endif
					if (IS_ERR(handle->cd)) {
						class_destroy(handle->cs);
						unregister_chrdev(MAJOR
								  (handle->dev),
								  driver_name);
						code =
						    (os_error_code) handle->cs;
						handle->cs = NULL;
					} else {
						handle->reg_complete = TRUE;
					}
				}
#endif
			}
		}
		/* ... fops routine registered */
		/* Handle power management fns through separate interface */
		if ((code == OS_ERROR_OK_S) &&
		    (handle->dd.suspend || handle->dd.resume)) {
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
			handle->dd.name = name;
			handle->dd.bus = &platform_bus_type;
			code = driver_register(&handle->dd);
#else
			handle->dd.driver.name = name;
			handle->dd.driver.bus = &platform_bus_type;
			code = driver_register(&handle->dd.driver);
#endif
			if (code == OS_ERROR_OK_S) {
				handle->pd.name = name;
				handle->pd.id = 0;
				code = platform_device_register(&handle->pd);
				if (code != OS_ERROR_OK_S) {
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
					driver_unregister(&handle->dd);
#else
					driver_unregister(&handle->dd.driver);
#endif
				} else {
					handle->power_complete = TRUE;
				}
			}
		}		/* ... suspend or resume */
	}			/* name != NULL */
	return code;
}

/*!
 * Finalize the driver registration with the kernel.
 *
 * Upon return from this call, the driver may begin receiving calls at the
 * defined entry points.
 *
 * @param handle    The handle used with #os_driver_init_registration().
 * @param major      The major device number to be associated with the driver.
 *                   If this value is zero, a major number may be assigned.
 *                   See #os_driver_get_major() to determine final value.
 *                   #os_driver_remove_registration().
 * @param driver_name The driver name.  Can be used as part of 'device node'
 *                   name on platforms which support such a feature.
 *
 * @return  An error code
 */
#define os_driver_complete_registration(handle, major, driver_name)           \
   os_drv_do_reg(&handle, major, driver_name)

/*!
 * Get driver Major Number from handle after a successful registration.
 *
 * @param   handle  A handle which has completed registration.
 *
 * @return The major number (if any) associated with the handle.
 */
#define os_driver_get_major(handle)                                           \
    (handle.reg_complete ? MAJOR(handle.dev) : -1)

/*!
 * INTERNAL implemention of os_driver_remove_registration.
 *
 * @param handle    A handle initialized by #os_driver_init_registration().
 *
 * @return  An error code.
 */
inline static int os_drv_rmv_reg(os_driver_reg_t * handle)
{
	if (handle->reg_complete) {
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
		if (handle->cd != NULL) {
			class_simple_device_remove(handle->dev);
			handle->cd = NULL;
		}
		if (handle->cs != NULL) {
			class_simple_destroy(handle->cs);
			handle->cs = NULL;
		}
		unregister_chrdev(MAJOR(handle->dev), handle->dd.name);
#else
		if (handle->cd != NULL) {
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
			class_device_destroy(handle->cs, handle->dev);
#else
			device_destroy(handle->cs, handle->dev);
#endif
			handle->cd = NULL;
		}
		if (handle->cs != NULL) {
			class_destroy(handle->cs);
			handle->cs = NULL;
		}
		unregister_chrdev(MAJOR(handle->dev), handle->dd.driver.name);
#endif
		handle->reg_complete = FALSE;
	}
	if (handle->power_complete) {
		platform_device_unregister(&handle->pd);
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
		driver_unregister(&handle->dd);
#else
		driver_unregister(&handle->dd.driver);
#endif
		handle->power_complete = FALSE;
	}
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
	if (handle->dd.name != NULL) {
		kfree(handle->dd.name);
		handle->dd.name = NULL;
	}
#else
	if (handle->dd.driver.name != NULL) {
		kfree(handle->dd.driver.name);
		handle->dd.driver.name = NULL;
	}
#endif
	return OS_ERROR_OK_S;
}

/*!
 * Remove the driver's registration with the kernel.
 *
 * Upon return from this call, the driver not receive any more calls at the
 * defined entry points (other than ISR and shutdown).
 *
 * @param handle    A handle initialized by #os_driver_init_registration().
 *
 * @return  An error code.
 */
#define os_driver_remove_registration(handle)                                 \
   os_drv_rmv_reg(&handle)

/*!
 * Register a driver with the Linux Device Model.
 *
 * @param   driver_information  The device_driver structure information
 *
 * @return  An error code.
 *
 * Status: denigrated in favor of #os_driver_complete_registration()
 */
#define os_register_to_driver(driver_information)                             \
              driver_register(driver_information)

/*!
 * Unregister a driver from the Linux Device Model
 *
 * this routine unregisters from the Linux Device Model
 *
 * @param   driver_information  The device_driver structure information
 *
 * @return  An error code.
 *
 * Status: Denigrated. See #os_register_to_driver().
 */
#define os_unregister_from_driver(driver_information)                         \
                driver_unregister(driver_information)

/*!
 * register a device to a driver
 *
 * this routine registers a drivers devices to the Linux Device Model
 *
 * @param   device_information  The platform_device structure information
 *
 * @return  An error code.
 *
 * Status: denigrated in favor of #os_driver_complete_registration()
 */
#define os_register_a_device(device_information)                              \
    platform_device_register(device_information)

/*!
 * unregister a device from a driver
 *
 * this routine unregisters a drivers devices from the Linux Device Model
 *
 * @param   device_information  The platform_device structure information
 *
 * @return  An error code.
 *
 * Status: Denigrated.  See #os_register_a_device().
 */
#define os_unregister_a_device(device_information)                            \
    platform_device_unregister(device_information)

/*!
 * Print a message to console / into log file.  After the @c msg argument a
 * number of printf-style arguments may be added.  Types should be limited to
 * printf string, char, octal, decimal, and hexadecimal types.  (This excludes
 * pointers, and floating point).
 *
 * @param  msg    The main text of the message to be logged
 * @param  s      The printf-style arguments which go with msg, if any
 *
 * @return (void)
 */
#define os_printk(...)                                                        \
    (void) printk(__VA_ARGS__)

/*!
 * Prepare a task to execute the given function.  This should only be done once
 * per function,, during the driver's initialization routine.
 *
 * @param task_fn   Name of the OS_DEV_TASK() function to be created.
 *
 * @return an OS ERROR code.
 */
#define os_create_task(function_name)                                         \
    OS_ERROR_OK_S

/*!
 * Schedule execution of a task.
 *
 * @param function_name   The function associated with the task.
 *
 * @return (void)
 */
#define os_dev_schedule_task(function_name)                                   \
    tasklet_schedule(&(function_name ## let))

/*!
 * Make sure that task is no longer running and will no longer run.
 *
 * This function will not return until both are true.  This is useful when
 * shutting down a driver.
 */
#define os_dev_stop_task(function_name)                                       \
do {                                                                          \
    tasklet_disable(&(function_name ## let));                                 \
    tasklet_kill(&(function_name ## let));                                    \
} while (0)

/*!
 * Allocate some kernel memory
 *
 * @param amount   Number of 8-bit bytes to allocate
 * @param flags    Some indication of purpose of memory (needs definition)
 *
 * @return  Pointer to allocated memory, or NULL if failed.
 */
#define os_alloc_memory(amount, flags)                                        \
    (void*)kmalloc(amount, flags)

/*!
 * Free some kernel memory
 *
 * @param location  The beginning of the region to be freed.
 *
 * Do some OSes have separate free() functions which should be
 * distinguished by passing in @c flags here, too? Don't some also require the
 * size of the buffer being freed?
 */
#define os_free_memory(location)                                              \
    kfree(location)

/*!
 * Allocate cache-coherent memory
 *
 * @param       amount     Number of bytes to allocate
 * @param[out]  dma_addrp  Location to store physical address of allocated
 *                         memory.
 * @param       flags      Some indication of purpose of memory (needs
 *                         definition).
 *
 * @return (virtual space) pointer to allocated memory, or NULL if failed.
 *
 */
#define os_alloc_coherent(amount, dma_addrp, flags)                           \
    (void*)dma_alloc_coherent(NULL, amount, dma_addrp, flags)

/*!
 * Free cache-coherent memory
 *
 * @param       size       Number of bytes which were allocated.
 * @param       virt_addr  Virtual(kernel) address of memory.to be freed, as
 *                         returned by #os_alloc_coherent().
 * @param       dma_addr   Physical address of memory.to be freed, as returned
 *                         by #os_alloc_coherent().
 *
 * @return void
 *
 */
#define os_free_coherent(size, virt_addr, dma_addr)                           \
    dma_free_coherent(NULL, size, virt_addr, dma_addr

/*!
 * Map an I/O space into kernel memory space
 *
 * @param start       The starting address of the (physical / io space) region
 * @param range_bytes The number of bytes to map
 *
 * @return A pointer to the mapped area, or NULL on failure
 */
#define os_map_device(start, range_bytes)                                     \
    (void*)ioremap_nocache((start), range_bytes)

/*!
 * Unmap an I/O space from kernel memory space
 *
 * @param start       The starting address of the (virtual) region
 * @param range_bytes The number of bytes to unmap
 *
 * @return None
 */
#define os_unmap_device(start, range_bytes)                                   \
    iounmap((void*)(start))

/*!
 * Copy data from Kernel space to User space
 *
 * @param to   The target location in user memory
 * @param from The source location in kernel memory
 * @param size The number of bytes to be copied
 *
 * @return #os_error_code
 */
#define os_copy_to_user(to, from, size)                                       \
    ((copy_to_user(to, from, size) == 0) ? 0 : OS_ERROR_BAD_ADDRESS_S)

/*!
 * Copy data from User space to Kernel space
 *
 * @param to   The target location in kernel memory
 * @param from The source location in user memory
 * @param size The number of bytes to be copied
 *
 * @return #os_error_code
 */
#define os_copy_from_user(to, from, size)                                     \
    ((copy_from_user(to, from, size) == 0) ? 0 : OS_ERROR_BAD_ADDRESS_S)

/*!
 * Read a 8-bit device register
 *
 * @param register_address  The (bus) address of the register to write to
 * @return                  The value in the register
 */
#define os_read8(register_address)                                            \
    __raw_readb(register_address)

/*!
 * Write a 8-bit device register
 *
 * @param register_address  The (bus) address of the register to write to
 * @param value             The value to write into the register
 */
#define os_write8(register_address, value)                                    \
    __raw_writeb(value, register_address)

/*!
 * Read a 16-bit device register
 *
 * @param register_address  The (bus) address of the register to write to
 * @return                  The value in the register
 */
#define os_read16(register_address)                                           \
    __raw_readw(register_address)

/*!
 * Write a 16-bit device register
 *
 * @param register_address  The (bus) address of the register to write to
 * @param value             The value to write into the register
 */
#define os_write16(register_address, value)                                   \
    __raw_writew(value, (uint32_t*)(register_address))

/*!
 * Read a 32-bit device register
 *
 * @param register_address  The (bus) address of the register to write to
 * @return                  The value in the register
 */
#define os_read32(register_address)                                           \
    __raw_readl((uint32_t*)(register_address))

/*!
 * Write a 32-bit device register
 *
 * @param register_address  The (bus) address of the register to write to
 * @param value             The value to write into the register
 */
#define os_write32(register_address, value)                                   \
    __raw_writel(value, register_address)

/*!
 * Read a 64-bit device register
 *
 * @param register_address  The (bus) address of the register to write to
 * @return                  The value in the register
 */
#define os_read64(register_address)                                           \
     ERROR_UNIMPLEMENTED

/*!
 * Write a 64-bit device register
 *
 * @param register_address  The (bus) address of the register to write to
 * @param value             The value to write into the register
 */
#define os_write64(register_address, value)                                   \
    ERROR_UNIMPLEMENTED

/*!
 * Delay some number of microseconds
 *
 * Note that this is a busy-loop, not a suspension of the task/process.
 *
 * @param  msecs   The number of microseconds to delay
 *
 * @return void
 */
#define os_mdelay mdelay

/*!
 * Calculate virtual address from physical address
 *
 * @param pa    Physical address
 *
 * @return virtual address
 *
 * @note this assumes that addresses are 32 bits wide
 */
#define os_va __va

/*!
 * Calculate physical address from virtual address
 *
 *
 * @param va    Virtual address
 *
 * @return physical address
 *
 * @note this assumes that addresses are 32 bits wide
 */
#define os_pa __pa

#ifdef CONFIG_PREEMPT_RT
/*!
 * Allocate and initialize a lock, returning a lock handle.
 *
 * The lock state will be initialized to 'unlocked'.
 *
 * @return A lock handle, or NULL if an error occurred.
 */
inline static os_lock_t os_lock_alloc_init(void)
{
	raw_spinlock_t *lockp;
	lockp = (raw_spinlock_t *) kmalloc(sizeof(raw_spinlock_t), 0);
	if (lockp) {
		_raw_spin_lock_init(lockp);
	} else {
		printk("OS: lock init failed\n");
	}

	return lockp;
}
#else
/*!
 * Allocate and initialize a lock, returning a lock handle.
 *
 * The lock state will be initialized to 'unlocked'.
 *
 * @return A lock handle, or NULL if an error occurred.
 */
inline static os_lock_t os_lock_alloc_init(void)
{
	spinlock_t *lockp;
	lockp = (spinlock_t *) kmalloc(sizeof(spinlock_t), 0);
	if (lockp) {
		spin_lock_init(lockp);
	} else {
		printk("OS: lock init failed\n");
	}

	return lockp;
}
#endif				/* CONFIG_PREEMPT_RT */

/*!
 * Acquire a lock.
 *
 * This function should only be called from an interrupt service routine.
 *
 * @param   lock_handle  A handle to the lock to acquire.
 *
 * @return void
 */
#define os_lock(lock_handle)                                              \
   spin_lock(lock_handle)

/*!
 * Unlock a lock.  Lock must have been acquired by #os_lock().
 *
 * @param   lock_handle  A handle to the lock to unlock.
 *
 * @return void
 */
#define os_unlock(lock_handle)                                            \
   spin_unlock(lock_handle)

/*!
 * Acquire a lock in non-ISR context
 *
 * This function will spin until the lock is available.
 *
 * @param lock_handle  A handle of the lock to acquire.
 * @param context      Place to save the before-lock context
 *
 * @return void
 */
#define os_lock_save_context(lock_handle, context)                        \
    spin_lock_irqsave(lock_handle, context)

/*!
 * Release a lock in non-ISR context
 *
 * @param lock_handle  A handle of the lock to release.
 * @param context      Place where before-lock context was saved.
 *
 * @return void
 */
#define os_unlock_restore_context(lock_handle, context)                  \
    spin_unlock_irqrestore(lock_handle, context)

/*!
 * Deallocate a lock handle.
 *
 * @param lock_handle   An #os_lock_t that has been allocated.
 *
 * @return void
 */
#define os_lock_deallocate(lock_handle)                                   \
    kfree(lock_handle)

/*!
 * Determine process handle
 *
 * The process handle of the current user is returned.
 *
 * @return   A handle on the current process.
 */
#define os_get_process_handle()                                           \
    current

/*!
 * Send a signal to a process
 *
 * @param  proc   A handle to the target process.
 * @param  sig    The POSIX signal to send to that process.
 */
#define os_send_signal(proc, sig)                                         \
    send_sig(sig, proc, 0);

/*!
 * Get some random bytes
 *
 * @param buf    The location to store the random data.
 * @param count  The number of bytes to store.
 *
 * @return  void
 */
#define os_get_random_bytes(buf, count)                                   \
    get_random_bytes(buf, count)

/*!
 * Go to sleep on an object.
 *
 * @param object    The object on which to sleep
 * @param condition An expression to check for sleep completion.  Must be
 *                  coded so that it can be referenced more than once inside
 *                  macro, i.e., no ++ or other modifying expressions.
 * @param atomic    Non-zero if sleep must not return until condition.
 *
 * @return error code -- OK or sleep interrupted??
 */
#define os_sleep(object, condition, atomic)                             \
({                                                                      \
    DEFINE_WAIT(_waitentry_);                                           \
    os_error_code code = OS_ERROR_OK_S;                                 \
                                                                        \
    while (!(condition)) {                                              \
        prepare_to_wait(&(object##_qh), &_waitentry_,                   \
                        atomic ? 0 : TASK_INTERRUPTIBLE);               \
        if (!(condition)) {                                             \
            schedule();                                                 \
        }                                                               \
                                                                        \
        finish_wait(&(object##_qh), &_waitentry_);                      \
                                                                        \
        if (!atomic && signal_pending(current)) {                       \
            code = OS_ERROR_FAIL_S; /* NEED SOMETHING BETTER */         \
            break;                                                      \
        }                                                               \
    };                                                                  \
                                                                        \
    code;                                                               \
})

/*!
 * Wake up whatever is sleeping on sleep object
 *
 * @param object  The object on which things might be sleeping
 *
 * @return none
 */
#define os_wake_sleepers(object)                                        \
    wake_up_interruptible(&(object##_qh));

	  /*! @} *//* dkops */

/******************************************************************************
 *  Function signature-generating macros
 *****************************************************************************/

/*!
 * @defgroup drsigs Driver Signatures
 *
 * These macros will define the entry point signatures for interrupt handlers;
 * driver initialization and shutdown; device open/close; etc.
 *
 * There are two versions of each macro for a given Driver Entry Point.  The
 * first version is used to define a function and its implementation in the
 * driver.c file, e.g. #OS_DEV_INIT().
 *
 * The second form is used whenever a forward declaration (prototype) is
 * needed.  It has the letters @c _DCL appended to the name of the defintion
 * function, and takes only the first two arguments (driver_name and
 * function_name).  These are not otherwise mentioned in this documenation.
 *
 * There is a third form used when a reference to a function is required, for
 * instance when passing the routine as a pointer to a function.  It has the
 * letters @c _REF appended to it, and takes only the first two arguments
 * (driver_name and function_name).  These functions are not otherwise
 * mentioned in this documentation.
 *
 * (Note that these two extra forms are required because of the
 * possibility/likelihood of having a 'wrapper function' which invokes the
 * generic function with expected arguments.  An alternative would be to have a
 * generic function which isn't able to get at any arguments directly, but
 * would be equipped with macros which could get at information passed in.
 *
 * Example:
 *
 * (in a header file)
 * @code
 * OS_DEV_INIT_DCL(widget, widget_init);
 * @endcode
 *
 * (in an implementation file)
 * @code
 * OS_DEV_INIT(widget, widget_init)
 * {
 *     os_dev_init_return(TRUE);
 * }
 * @endcode
 *
 */

/*! @addtogroup drsigs */
/*! @{ */

/*!
 * Define a function which will handle device initialization
 *
 * This is tne driver initialization routine.  This is normally where the
 * part would be initialized; queues, locks, interrupts handlers defined;
 * long-term dynamic memory allocated for driver use; etc.
 *
 * @param function_name   The name of the portable initialization function.
 *
 * @return  A call to #os_dev_init_return()
 *
 */
#define OS_DEV_INIT(function_name)                                            \
module_init(function_name);                                                   \
static int __init function_name (void)

/*! Make declaration for driver init function.
 * @param function_name foo
 */
#define OS_DEV_INIT_DCL(function_name)                                        \
static int __init function_name (void);

/*!
 * Generate a function reference to the driver's init function.
 * @param function_name   Name of the OS_DEV_INIT() function.
 *
 * @return A function pointer.
 */
#define OS_DEV_INIT_REF(function_name)                                        \
function_name

/*!
 * Define a function which will handle device shutdown
 *
 * This is the inverse of the #OS_DEV_INIT() routine.
 *
 * @param function_name   The name of the portable driver shutdown routine.
 *
 * @return  A call to #os_dev_shutdown_return()
 *
 */
#define OS_DEV_SHUTDOWN(function_name)                                        \
module_exit(function_name);                                                   \
static void function_name(void)

/*!
 * Generate a function reference to the driver's shutdown function.
 * @param function_name   Name of the OS_DEV_HUSTDOWN() function.
 *
 * @return A function pointer.
 */
#define OS_DEV_SHUTDOWN_DCL(function_name)                                    \
static void function_name(void);

/*!
 * Generate a reference to driver's shutdown function
 * @param function_name   Name of the OS_DEV_HUSTDOWN() function.
*/

#define OS_DEV_SHUTDOWN_REF(function_name)                                    \
function_name

/*!
 * Define a function which will open the device for a user.
 *
 * @param function_name The name of the driver open() function
 *
 * @return  A call to #os_dev_open_return()
 */
#define OS_DEV_OPEN(function_name)                                            \
static int function_name(struct inode* inode_p_, struct file* file_p_)

/*!
 * Declare prototype for an open() function.
 *
 * @param function_name The name of the OS_DEV_OPEN() function.
 */
#define OS_DEV_OPEN_DCL(function_name)                                        \
OS_DEV_OPEN(function_name);

/*!
 * Generate a function reference to the driver's open() function.
 * @param function_name   Name of the OS_DEV_OPEN() function.
 *
 * @return A function pointer.
 */
#define OS_DEV_OPEN_REF(function_name)                                        \
function_name

/*!
 * Define a function which will handle a user's ioctl() request
 *
 * @param function_name The name of the driver ioctl() function
 *
 * @return A call to #os_dev_ioctl_return()
 */
#define OS_DEV_IOCTL(function_name)                                           \
static int function_name(struct inode* inode_p_, struct file* file_p_,        \
                     unsigned int cmd_, unsigned long data_)

/*! Boo. */
#define OS_DEV_IOCTL_DCL(function_name)                                       \
OS_DEV_IOCTL(function_name);

/*!
 * Generate a function reference to the driver's ioctl() function.
 * @param function_name   Name of the OS_DEV_IOCTL() function.
 *
 * @return A function pointer.
 */
#define OS_DEV_IOCTL_REF(function_name)                                       \
function_name

/*!
 * Define a function which will handle a user's mmap() request
 *
 * @param function_name The name of the driver mmap() function
 *
 * @return A call to #os_dev_ioctl_return()
 */
#define OS_DEV_MMAP(function_name)                                            \
int function_name(struct file* file_p_, struct vm_area_struct* vma_)

#define OS_DEV_MMAP_DCL(function_name)                                        \
OS_DEV_MMAP(function_name);

#define OS_DEV_MMAP_REF(function_name)                                        \
function_name

/* Retrieve the context to the memory structure that is to be MMAPed */
#define os_mmap_memory_ctx() (vma_)

/* Determine the size of the requested MMAP region*/
#define os_mmap_memory_size() (vma_->vm_end - vma_->vm_start)

/* Determine the base address of the requested MMAP region*/
#define os_mmap_user_base() (vma_->vm_start)

/*!
 * Declare prototype for an read() function.
 *
 * @param function_name The name of the driver read function.
 */
#define OS_DEV_READ_DCL(function_name)                                        \
OS_DEV_READ(function_name);

/*!
 * Generate a function reference to the driver's read() routine
 * @param function_name   Name of the OS_DEV_READ() function.
 *
 * @return A function pointer.
 */
#define OS_DEV_READ_REF(function_name)                                        \
function_name

/*!
 * Define a function which will handle a user's write() request
 *
 * @param function_name The name of the driver write() function
 *
 * @return A call to #os_dev_write_return()
 */
#define OS_DEV_WRITE(function_name)                                           \
static ssize_t function_name(struct file* file_p_, char* user_buffer_,        \
                     size_t count_bytes_, loff_t* file_position_)

/*!
 * Declare prototype for an write() function.
 *
 * @param function_name The name of the driver write function.
 */
#define OS_DEV_WRITE_DCL(function_name)                                       \
OS_DEV_WRITE(function_name);

/*!
 * Generate a function reference to the driver's write() routine
 * @param function_name   Name of the OS_DEV_WRITE() function.
 *
 * @return A function pointer.
 */
#define OS_DEV_WRITE_REF(function_name)                                       \
function_name

/*!
 * Define a function which will close the device - opposite of OS_DEV_OPEN()
 *
 * @param function_name The name of the driver close() function
 *
 * @return A call to #os_dev_close_return()
 */
#define OS_DEV_CLOSE(function_name)                                           \
static int function_name(struct inode* inode_p_, struct file* file_p_)

/*!
 * Declare prototype for an close() function
 *
 * @param function_name The name of the driver close() function.
 */
#define OS_DEV_CLOSE_DCL(function_name)                                       \
OS_DEV_CLOSE(function_name);

/*!
 * Generate a function reference to the driver's close function.
 * @param function_name   Name of the OS_DEV_CLOSE() function.
 *
 * @return A function pointer.
 */
#define OS_DEV_CLOSE_REF(function_name)                                       \
function_name

/*!
 * Define a function which will handle an interrupt
 *
 * No arguments are available to the generic function.  It must not invoke any
 * OS functions which are illegal in a ISR.  It gets no parameters, and must
 * have a call to #os_dev_isr_return() instead of any/all return statements.
 *
 * Example:
 * @code
 * OS_DEV_ISR(widget)
 * {
 *     os_dev_isr_return(1);
 * }
 * @endcode
 *
 * @param function_name The name of the driver ISR function
 *
 * @return   A call to #os_dev_isr_return()
 */
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
#define OS_DEV_ISR(function_name)                                             \
static irqreturn_t function_name(int N1_, void* N2_, struct pt_regs* N3_)
#else
#define OS_DEV_ISR(function_name)                                             \
static irqreturn_t function_name(int N1_, void* N2_)
#endif

/*!
 * Declare prototype for an ISR function.
 *
 * @param function_name The name of the driver ISR function.
 */
#define OS_DEV_ISR_DCL(function_name)                                         \
OS_DEV_ISR(function_name);

/*!
 * Generate a function reference to the driver's interrupt service routine
 * @param function_name   Name of the OS_DEV_ISR() function.
 *
 * @return A function pointer.
 */
#define OS_DEV_ISR_REF(function_name)                                         \
function_name

/*!
 * Define a function which will operate as a background task / bottom half.
 *
 * Tasklet stuff isn't strictly limited to 'Device drivers', but leave it
 * this namespace anyway.
 *
 * @param function_name The name of this background task function
 *
 * @return A call to #os_dev_task_return()
 */
#define OS_DEV_TASK(function_name)                                            \
static void function_name(unsigned long data_)

/*!
 * Declare prototype for a background task / bottom half function
 *
 * @param function_name The name of this background task function
 */
#define OS_DEV_TASK_DCL(function_name)                                        \
OS_DEV_TASK(function_name);                                                   \
DECLARE_TASKLET(function_name ## let, function_name, 0);

/*!
 * Generate a reference to an #OS_DEV_TASK() function
 *
 * @param function_name   The name of the task being referenced.
 */
#define OS_DEV_TASK_REF(function_name)                                        \
    (function_name ## let)

	  /*! @} *//* drsigs */

/*****************************************************************************
 *  Functions/Macros for returning values from Driver Signature routines
 *****************************************************************************/

/*!
 * Return from the #OS_DEV_INIT() function
 *
 * @param code    An error code to report success or failure.
 *
 */
#define os_dev_init_return(code)                                             \
    return code

/*!
 * Return from the #OS_DEV_SHUTDOWN() function
 *
 * @param code    An error code to report success or failure.
 *
 */
#define os_dev_shutdown_return(code)                                         \
    return

/*!
 * Return from the #OS_DEV_ISR() function
 *
 * The function should verify that it really was supposed to be called,
 * and that its device needed attention, in order to properly set the
 * return code.
 *
 * @param code    non-zero if interrupt handled, zero otherwise.
 *
 */
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
#define os_dev_isr_return(code)                                              \
do {                                                                         \
    /* Unused warnings */                                                    \
    (void)N1_;                                                               \
    (void)N2_;                                                               \
    (void)N3_;                                                               \
                                                                             \
    return IRQ_RETVAL(code);                                                 \
} while (0)
#else
#define os_dev_isr_return(code)                                              \
do {                                                                         \
    /* Unused warnings */                                                    \
    (void)N1_;                                                               \
    (void)N2_;                                                               \
                                                                             \
    return IRQ_RETVAL(code);                                                 \
} while (0)
#endif

/*!
 * Return from the #OS_DEV_OPEN() function
 *
 * @param code    An error code to report success or failure.
 *
 */
#define os_dev_open_return(code)                                             \
do {                                                                         \
    int retcode = code;                                                      \
                                                                             \
    /* get rid of 'unused parameter' warnings */                             \
    (void)inode_p_;                                                          \
    (void)file_p_;                                                           \
                                                                             \
    return retcode;                                                          \
} while (0)

/*!
 * Return from the #OS_DEV_IOCTL() function
 *
 * @param code    An error code to report success or failure.
 *
 */
#define os_dev_ioctl_return(code)                                            \
do {                                                                         \
    int retcode = code;                                                      \
                                                                             \
    /* get rid of 'unused parameter' warnings */                             \
    (void)inode_p_;                                                          \
    (void)file_p_;                                                           \
    (void)cmd_;                                                              \
    (void)data_;                                                             \
                                                                             \
    return retcode;                                                          \
} while (0)

/*!
 * Return from the #OS_DEV_READ() function
 *
 * @param code    Number of bytes read, or an error code to report failure.
 *
 */
#define os_dev_read_return(code)                                             \
do {                                                                         \
    ssize_t retcode = code;                                                  \
                                                                             \
    /* get rid of 'unused parameter' warnings */                             \
    (void)file_p_;                                                           \
    (void)user_buffer_;                                                      \
    (void)count_bytes_;                                                      \
    (void)file_position_;                                                    \
                                                                             \
    return retcode;                                                          \
} while (0)

/*!
 * Return from the #OS_DEV_WRITE() function
 *
 * @param code    Number of bytes written, or an error code to report failure.
 *
 */
#define os_dev_write_return(code)                                            \
do {                                                                         \
    ssize_t retcode = code;                                                  \
                                                                             \
    /* get rid of 'unused parameter' warnings */                             \
    (void)file_p_;                                                           \
    (void)user_buffer_;                                                      \
    (void)count_bytes_;                                                      \
    (void)file_position_;                                                    \
                                                                             \
    return retcode;                                                          \
} while (0)

/*!
 * Return from the #OS_DEV_CLOSE() function
 *
 * @param code    An error code to report success or failure.
 *
 */
#define os_dev_close_return(code)                                            \
do {                                                                         \
    ssize_t retcode = code;                                                  \
                                                                             \
    /* get rid of 'unused parameter' warnings */                             \
    (void)inode_p_;                                                          \
    (void)file_p_;                                                           \
                                                                             \
    return retcode;                                                          \
} while (0)

/*!
 * Start the #OS_DEV_TASK() function
 *
 * In some implementations, this could be turned into a label for
 * the os_dev_task_return() call.
 *
 * @return none
 */
#define os_dev_task_begin()

/*!
 * Return from the #OS_DEV_TASK() function
 *
 * In some implementations, this could be turned into a sleep followed
 * by a jump back to the os_dev_task_begin() call.
 *
 * @param code    An error code to report success or failure.
 *
 */
#define os_dev_task_return(code)                                             \
do {                                                                         \
    /* Unused warnings */                                                    \
    (void)data_;                                                             \
                                                                             \
    return;                                                                  \
} while (0)

/*****************************************************************************
 *  Functions/Macros for accessing arguments from Driver Signature routines
 *****************************************************************************/

/*! @defgroup drsigargs Functions for Getting Arguments in Signature functions
 *
 */
/* @addtogroup @drsigargs */
/*! @{ */
/*!
 * Used in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ() and
 * #OS_DEV_WRITE() routines to check whether user is requesting read
 * (permission)
 */
#define os_dev_is_flag_read()                                                 \
   (file_p_->f_mode & FMODE_READ)

/*!
 * Used in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ() and
 * #OS_DEV_WRITE() routines to check whether user is requesting write
 * (permission)
 */
#define os_dev_is_flag_write()                                                \
   (file_p_->f_mode & FMODE_WRITE)

/*!
 * Used in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ() and
 * #OS_DEV_WRITE() routines to check whether user is requesting non-blocking
 * I/O.
 */
#define os_dev_is_flag_nonblock()                                             \
   (file_p_->f_flags & (O_NONBLOCK | O_NDELAY))

/*!
 * Used in #OS_DEV_OPEN() and #OS_DEV_CLOSE() to determine major device being
 * accessed.
 */
#define os_dev_get_major()                                                    \
    (imajor(inode_p_))

/*!
 * Used in #OS_DEV_OPEN() and #OS_DEV_CLOSE() to determine minor device being
 * accessed.
 */
#define os_dev_get_minor()                                                    \
    (iminor(inode_p_))

/*!
 * Used in #OS_DEV_IOCTL() to determine which operation the user wants
 * performed.
 *
 * @return Value of the operation.
 */
#define os_dev_get_ioctl_op()                                                 \
    (cmd_)

/*!
 * Used in #OS_DEV_IOCTL() to return the associated argument for the desired
 * operation.
 *
 * @return    A value which can be cast to a struct pointer or used as
 *            int/long.
 */
#define os_dev_get_ioctl_arg()                                                \
    (data_)

/*!
 * Used in OS_DEV_READ() and OS_DEV_WRITE() routines to access the requested
 * byte count.
 *
 * @return  (unsigned) a count of bytes
 */
#define os_dev_get_count()                                                    \
    ((unsigned)count_bytes_)

/*!
 * Used in OS_DEV_READ() and OS_DEV_WRITE() routines to return the pointer
 * byte count.
 *
 * @return   char* pointer to user buffer
 */
#define os_dev_get_user_buffer()                                              \
    ((void*)user_buffer_)

/*!
 * Used in OS_DEV_READ(), OS_DEV_WRITE(), and OS_DEV_IOCTL() routines to
 * get the POSIX flags field for the associated open file).
 *
 * @return The flags associated with the file.
 */
#define os_dev_get_file_flags()                                               \
    (file_p_->f_flags)

/*!
 * Set the driver's private structure associated with this file/open.
 *
 * Generally used during #OS_DEV_OPEN().  See #os_dev_get_user_private().
 *
 * @param  struct_p   The driver data structure to associate with this user.
 */
#define os_dev_set_user_private(struct_p)                                     \
    file_p_->private_data = (void*)(struct_p)

/*!
 * Get the driver's private structure associated with this file.
 *
 * May be used during #OS_DEV_OPEN(), #OS_DEV_READ(), #OS_DEV_WRITE(),
 * #OS_DEV_IOCTL(), and #OS_DEV_CLOSE().  See #os_dev_set_user_private().
 *
 * @return   The driver data structure to associate with this user.
 */
#define os_dev_get_user_private()                                             \
    ((void*)file_p_->private_data)

/*!
 * Get the IRQ associated with this call to the #OS_DEV_ISR() function.
 *
 * @return  The IRQ (integer) interrupt number.
 */
#define os_dev_get_irq()                                                      \
    N1_

	   /*! @} *//* drsigargs */

/*!
 * @defgroup cacheops Cache Operations
 *
 * These functions are for synchronizing processor cache with RAM.
 */
/*! @addtogroup cacheops */
/*! @{ */

/*!
 * Flush and invalidate all cache lines.
 */
#if 0
#define os_flush_cache_all()                                              \
    flush_cache_all()
#else
/* Call ARM fn directly, in case L2cache=on3 not set */
#define os_flush_cache_all()                                              \
    v6_flush_kern_cache_all_L2()

/*!
 * ARM-routine to flush all cache.  Defined here, because it exists in no
 * easy-access header file.  ARM-11 with L210 cache only!
 */
extern void v6_flush_kern_cache_all_L2(void);
#endif

/*
 *  These macros are using part of the Linux DMA API.  They rely on the
 *  map function to do nothing more than the equivalent clean/inv/flush
 *  operation at the time of the mapping, and do nothing at an unmapping
 *  call, which the Sahara driver code will never invoke.
 */

/*!
 * Clean a range of addresses from the cache.  That is, write updates back
 * to (RAM, next layer).
 *
 * @param  start    Starting virtual address
 * @param  len      Number of bytes to flush
 *
 * @return void
 */
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
#define os_cache_clean_range(start,len)                                   \
    dma_map_single(NULL, (void*)start, len, DMA_TO_DEVICE)
#else
#define os_cache_clean_range(start,len)                                   \
{                                                                         \
    void *s = (void*)start;                                               \
    void *e = s + len;                                                    \
    dmac_clean_range(s, e);                                               \
    outer_clean_range(__pa(s), __pa(e));                                  \
}
#endif

/*!
 * Invalidate a range of addresses in the cache
 *
 * @param  start    Starting virtual address
 * @param  len      Number of bytes to flush
 *
 * @return void
 */
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
#define os_cache_inv_range(start,len)                                     \
    dma_map_single(NULL, (void*)start, len, DMA_FROM_DEVICE)
#else
#define os_cache_inv_range(start,len)                                     \
{                                                                         \
    void *s = (void*)start;                                               \
    void *e = s + len;                                                    \
    dmac_inv_range(s, e);                                                 \
    outer_inv_range(__pa(s), __pa(e));                                    \
}
#endif

/*!
 * Flush a range of addresses from the cache.  That is, perform clean
 * and invalidate
 *
 * @param  start    Starting virtual address
 * @param  len      Number of bytes to flush
 *
 * @return void
 */
#if  LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
#define os_cache_flush_range(start,len)                                   \
    dma_map_single(NULL, (void*)start, len, DMA_BIDIRECTIONAL)
#else
#define os_cache_flush_range(start,len)                                   \
{                                                                         \
    void *s = (void*)start;                                               \
    void *e = s + len;                                                    \
    dmac_flush_range(s, e);                                               \
    outer_flush_range(__pa(s), __pa(e));                                  \
}
#endif

	  /*! @} *//* cacheops */

#endif				/* LINUX_PORT_H */
