/**
 * RNG driver for NXP PNX833X RNG
 *
 * Copyright (c) 2007 NXP B.V. All rights reserved.
 *
 * @file pnx833x-rng.c
 * @b Module: Interface for 833x hardware random number generator ("RNG")
 *
 * This file is licensed under  the terms of the GNU General Public
 * License version 2. This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 *
 * 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; either version 2
 * of the License, or (at your option) any later version.
 *
 * 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.
 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
 *
*//*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
001 20071006    mwhittaker
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/hw_random.h>

#include <pnx833x.h>

/**
 * pnx833x onchip hardware block register definitions
 */

/** CONFIG block */
#define PNX833X_CONFIG                  (0x07000U)
#define PNX833X_CONFIG_STATISTICS       (PNX833X_CONFIG+0x0A8U)
#define PNX833X_CONFIG_MODULE_RNG       (PNX833X_CONFIG+0x0ACU)
#define PNX833X_CONFIG_POLY_7_BONDING   (PNX833X_CONFIG+0x040U)
#define PNX833X_CONFIG_POLY_7_DIS_ASM_MODULE (1<<11)

/** DEBUG block */
#define PNX833X_DEBUG                   (0x0A000U)
#define PNX833X_DEBUG_HW_VERSION        (PNX833X_DEBUG+0x018U)
#define PNX833X_DEBUG_HW_VERSION_MASK   (0xF000U)
#define PNX833X_DEBUG_HW_VERSION_M1     (0x4000U)

/* enable printing of debug info to display relevant config register values */
#define dprintk(...)


/**
 Return data read from RNG, and number of bytes of data.

 Module will only be loaded if hardware is functional,
 hence no need to test availability at this stage.

 @param rng reference to RNG framework data
 @param buffer destination word for data read
 @return number of data bytes read into buffer
*/
static int pnx833x_rng_data_read(struct hwrng *rng, u32 *buffer)
{
    *buffer = PNX833X_REG(PNX833X_CONFIG_MODULE_RNG);
    return 4;
}

/** hardware RNG registration table */
static struct hwrng pnx833x_rng_ops = {
    .name = "pnx833x",
    .data_read = pnx833x_rng_data_read,
};

/**
 Register hardware RNG with Linux hw_random device framework.

 Check whether hardware RNG is available:
 1) check whether RNG statistics register gives 'not present' value
 2) check for M1 workaround: RNG inoperative if 'dis asm' is enabled

 @todo 2) might need updating for any subsequent chips that do not disable RNG on 'dis asm'
 @return 0: RNG available, registered OK;  <0: RNG not available, do not register module
*/
static int __init pnx833x_rng_init(void)
{
    int ret = -ENODEV;

    dprintk(KERN_INFO "pnx833x_rng_init: config_stats: 0x%X\n", (unsigned int)PNX833X_REG(PNX833X_CONFIG_STATISTICS));

    /* check for 'RNG not present' status value */
    if (PNX833X_REG(PNX833X_CONFIG_STATISTICS) != 0xDEADABBA)
    {
        int is_M1 = (PNX833X_REG(PNX833X_DEBUG_HW_VERSION) & PNX833X_DEBUG_HW_VERSION_MASK) == PNX833X_DEBUG_HW_VERSION_M1;
        int asm_not_disabled = (PNX833X_REG(PNX833X_CONFIG_POLY_7_BONDING) & PNX833X_CONFIG_POLY_7_DIS_ASM_MODULE) == 0;

        /* check for 'RNG clock enabled': dis asm = 0
           @todo May need revision for later revisions that allow RNG operation
         */
        dprintk(
          KERN_INFO "pnx833x_rng_init: config_poly_7: 0x%X, debug_hw_version: 0x%X\n",
          (unsigned int)PNX833X_REG(PNX833X_CONFIG_POLY_7_BONDING), (unsigned int)PNX833X_REG(PNX833X_DEBUG_HW_VERSION)
          );

        if (!is_M1 || asm_not_disabled)
        {
            dprintk(KERN_INFO "pnx833x_rng_init: rng read: 0x%X\n", (unsigned int)PNX833X_REG(PNX833X_CONFIG_MODULE_RNG));

            hwrng_register(&pnx833x_rng_ops);

            ret = 0;
        }
    }

    printk(KERN_INFO "pnx833x_rng_init:%s installed\n", (ret < 0) ? "not" : "");

    /* fail to load module if RNG not present or RNG clock is disabled */
    return ret;
}

/**
 Unload/unregister RNG module
*/
static void __exit pnx833x_rng_exit(void)
{
    hwrng_unregister(&pnx833x_rng_ops);
}

module_init(pnx833x_rng_init);
module_exit(pnx833x_rng_exit);

MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver for PNX833x");
MODULE_LICENSE("GPL");

