/*****************************************************************************/
/* Copyright (c) 2008 NXP B.V.                                               */
/*                                                                           */
/* This program is free software; you can redistribute it and/or modify      */
/* it under the terms of the GNU General Public License as published by      */
/* the Free Software Foundation, using version 2 of the License.             */
/*                                                                           */
/* This program is distributed in the hope that it will be useful,           */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the              */
/* GNU General Public License for more details.                              */
/*                                                                           */
/* You should have received a copy of the GNU General Public License         */
/* along with this program; if not, write to the Free Software               */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307       */
/* USA.                                                                      */
/*                                                                           */
/*****************************************************************************/
/*
                                  IPSTB Project
                         ---------------------------------

Filename: phStbMmio.c


Rev Date       Author      Comments
--------------------------------------------------------------------------------
  1 20050908   neill       Initial
  2 20060419   laird       Changing MMIO base
  3 20060926   iwillis     Fixed MMIO size for PNX8335
  4 20070115   steel       Update for 2.6.19 kernel
  5 20070126   neill       Extend range to include MSVD registers
  6 20080205   batelaan    Update file copyright/license text
  7 20090121   batelaan    Update author/license.
*/

/** @file
    MMIO access for STB225.

    Allows user space applications to access and modify MMIO values
*/

#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/device.h>

/******************************************************************************
* LOCAL MACROS                                                                *
*******************************************************************************/
#define THIS_MODULE_DESCRIPTION "PNX833x MMIO Driver"
#define DEVNAME "mmio"

//  ----------------------- MMIO MODULE Debug Level  ------------------------
static int debug = 0;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0-1)");

//  ----------------------- MODULE Kernel Debug --------------------------------
#define dprintk(num, format, args...)                                           \
    do {                                                                        \
        if (debug >= num) printk(format, ##args);                               \
    } while (0)

/**** Module Setup ****/
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION(THIS_MODULE_DESCRIPTION);
MODULE_AUTHOR("NXP B.V.");

#define MMIO_BASE_ADDR   0x17C00000UL
#define MMIO_SIZE       (0x00280000UL)

/******************************************************************************
* LOCAL TYPEDEFS                                                              *
*******************************************************************************/

/******************************************************************************
* STATIC FUNCTION PROTOTYPES                                                  *
*******************************************************************************/

static int phStbMmio_Mmap(struct file * filp, struct vm_area_struct * vma);

/******************************************************************************
* EXPORTED DATA                                                               *
*******************************************************************************/

/******************************************************************************
* LOCAL DATA                                                                  *
*******************************************************************************/
static struct class *sysClass;
static struct class_device *sysDevice;
static unsigned int major;

static struct file_operations mmio_fops = {
    .mmap    = phStbMmio_Mmap
};

/******************************************************************************
* FUNCTION IMPLEMENTATION                                                     *
*******************************************************************************/

/**
 * Map the device into the calling processes memory space
 */
static int phStbMmio_Mmap(struct file * filp, struct vm_area_struct * vma)
{
    unsigned int size;

    if (vma->vm_pgoff != 0)
    {
        dprintk(1, "%s : Non-zero page offset (%d)\n", THIS_MODULE_DESCRIPTION, (int)vma->vm_pgoff);
        return -EINVAL;
    }

    size = vma->vm_end - vma->vm_start;
    if (size > MMIO_SIZE)
    {
        dprintk(1, "%s : Excessive memory size (%d)\n", THIS_MODULE_DESCRIPTION, size);
        return -EINVAL;
    }

    /* Prevent the swapper from considering these pages for swapping or expanding*/
    vma->vm_flags |= (VM_RESERVED | VM_IO | VM_DONTEXPAND);

    vma->vm_page_prot = pgprot_noncached( vma->vm_page_prot );

    return io_remap_pfn_range( vma, vma->vm_start, (unsigned long)MMIO_BASE_ADDR >> PAGE_SHIFT, size, vma->vm_page_prot);
}

/*=============================================================================
| Module functions                                                            |
==============================================================================*/

/** Module initialisation. */
static int __init phStbMmio_Init(void)
{
    /* Register the device */
    major = register_chrdev(0, DEVNAME, &mmio_fops);
    if (major < 0)
    {
        dprintk(1, "%s : Unable to get a major device number\n",THIS_MODULE_DESCRIPTION);
        return -ENOMEM;
    }

    /* Register with sysfs */
    sysClass = class_create(THIS_MODULE, DEVNAME);
    if (sysClass == NULL)
    {
        dprintk(1, "%s : Class_simple_create failed\n",THIS_MODULE_DESCRIPTION);
        return -ENOMEM;
    }
    sysDevice = class_device_create(sysClass, NULL, MKDEV(major, 0), NULL, DEVNAME);
    if (sysDevice == NULL)
    {
        dprintk(1, "%s : Class_simple_device_add failed\n",THIS_MODULE_DESCRIPTION);
        return -ENOMEM;
    }

    printk("%s (%s-%s)\n", THIS_MODULE_DESCRIPTION, __DATE__, __TIME__);

    return 0;
}

module_init(phStbMmio_Init);

/** Module deinitialisation */
static void __exit phStbMmio_Exit(void)
{
    printk("Shutting down %s\n", THIS_MODULE_DESCRIPTION);

    /* Unregister with sysfs */
    class_device_destroy(sysClass, MKDEV(major, 0));
    class_destroy(sysClass);

    /* Unregister the device */
    unregister_chrdev(major, DEVNAME);
}

module_exit(phStbMmio_Exit);

