448 lines
14 KiB
C
Executable File
448 lines
14 KiB
C
Executable File
/***************************************************************
|
||
Copyright Statement:
|
||
|
||
This software/firmware and related documentation (<28><>EcoNet Software<72><65>)
|
||
are protected under relevant copyright laws. The information contained herein
|
||
is confidential and proprietary to EcoNet (HK) Limited (<28><>EcoNet<65><74>) and/or
|
||
its licensors. Without the prior written permission of EcoNet and/or its licensors,
|
||
any reproduction, modification, use or disclosure of EcoNet Software, and
|
||
information contained herein, in whole or in part, shall be strictly prohibited.
|
||
|
||
EcoNet (HK) Limited EcoNet. ALL RIGHTS RESERVED.
|
||
|
||
BY OPENING OR USING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY
|
||
ACKNOWLEDGES AND AGREES THAT THE SOFTWARE/FIRMWARE AND ITS
|
||
DOCUMENTATIONS (<28><>ECONET SOFTWARE<52><45>) RECEIVED FROM ECONET
|
||
AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON AN <20><>AS IS<49><53>
|
||
BASIS ONLY. ECONET EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
|
||
WHETHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
|
||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
|
||
OR NON-INFRINGEMENT. NOR DOES ECONET PROVIDE ANY WARRANTY
|
||
WHATSOEVER WITH RESPECT TO THE SOFTWARE OF ANY THIRD PARTIES WHICH
|
||
MAY BE USED BY, INCORPORATED IN, OR SUPPLIED WITH THE ECONET SOFTWARE.
|
||
RECEIVER AGREES TO LOOK ONLY TO SUCH THIRD PARTIES FOR ANY AND ALL
|
||
WARRANTY CLAIMS RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
|
||
THAT IT IS RECEIVER<45><52>S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD
|
||
PARTY ALL PROPER LICENSES CONTAINED IN ECONET SOFTWARE.
|
||
|
||
ECONET SHALL NOT BE RESPONSIBLE FOR ANY ECONET SOFTWARE RELEASES
|
||
MADE TO RECEIVER<45><52>S SPECIFICATION OR CONFORMING TO A PARTICULAR
|
||
STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND
|
||
ECONET'S ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE ECONET
|
||
SOFTWARE RELEASED HEREUNDER SHALL BE, AT ECONET'S SOLE OPTION, TO
|
||
REVISE OR REPLACE THE ECONET SOFTWARE AT ISSUE OR REFUND ANY SOFTWARE
|
||
LICENSE FEES OR SERVICE CHARGES PAID BY RECEIVER TO ECONET FOR SUCH
|
||
ECONET SOFTWARE.
|
||
***************************************************************/
|
||
|
||
/************************************************************************
|
||
* I N C L U D E S
|
||
*************************************************************************
|
||
*/
|
||
#include <linux/module.h>
|
||
#include <linux/types.h>
|
||
#include <linux/interrupt.h>
|
||
#include <linux/of_platform.h>
|
||
#include <linux/platform_device.h>
|
||
#include <asm/io.h>
|
||
#include <linux/delay.h>
|
||
|
||
/************************************************************************
|
||
* D E F I N E S & C O N S T A N T S
|
||
*************************************************************************
|
||
*/
|
||
|
||
#define TRNG_ADDR_BASE 0x00000000
|
||
|
||
/* M TRNG Register */
|
||
#define RNG_ADDR_RO_VERSION (TRNG_ADDR_BASE + 0x000)
|
||
#define RNG_ADDR_RO_STATUS (TRNG_ADDR_BASE + 0x004)
|
||
#define RNG_ADDR_WO_IRQ_CLR (TRNG_ADDR_BASE + 0x008)
|
||
#define RNG_ADDR_RW_SWRST (TRNG_ADDR_BASE + 0x010)
|
||
#define RNG_ADDR_RW_IRQ_CFG (TRNG_ADDR_BASE + 0x014)
|
||
#define RNG_ADDR_RW_EN (TRNG_ADDR_BASE + 0x020)
|
||
#define RNG_ADDR_RW_PRM (TRNG_ADDR_BASE + 0x024)
|
||
#define RNG_ADDR_RW_HTEST (TRNG_ADDR_BASE + 0x028)
|
||
#define RNG_ADDR_RW_DRBG (TRNG_ADDR_BASE + 0x02C)
|
||
#define RNG_ADDR_RO_OUT (TRNG_ADDR_BASE + 0x030)
|
||
#define RNG_ADDR_RO_SEED (TRNG_ADDR_BASE + 0x034)
|
||
#define RNG_ADDR_RO_RAW (TRNG_ADDR_BASE + 0x038)
|
||
#define RNG_ADDR_WO_EXT (TRNG_ADDR_BASE + 0x03C)
|
||
|
||
#define M_RAW_NRBG_DRBG_INIT 0x00000111
|
||
|
||
/* m TRNG Register */
|
||
#define TRNG_IP_RDY (TRNG_ADDR_BASE + 0x800)
|
||
#define NS_SEL_AND_DAT_EN (TRNG_ADDR_BASE + 0x804)
|
||
#define HEALTH_TEST_SW_RST (TRNG_ADDR_BASE + 0x808)
|
||
#define HEALTH_TEST_EN (TRNG_ADDR_BASE + 0x80C)
|
||
#define HEALTH_TEST_CUTOFF (TRNG_ADDR_BASE + 0x810)
|
||
#define DRBG_OUT_FORBID (TRNG_ADDR_BASE + 0x814)
|
||
#define INTR_EN (TRNG_ADDR_BASE + 0x818)
|
||
#define CONTINUE_FAIL_CLR (TRNG_ADDR_BASE + 0x81C)
|
||
#define TEST_CUTOFF_REAL (TRNG_ADDR_BASE + 0x820)
|
||
#define HEALTH_TEST_STATUS (TRNG_ADDR_BASE + 0x824)
|
||
#define RAW_DATA_OUT (TRNG_ADDR_BASE + 0x828)
|
||
#define NOISE_DATA_IN (TRNG_ADDR_BASE + 0x82C)
|
||
#define SHA_TEST_MODE (TRNG_ADDR_BASE + 0x830)
|
||
#define RESEED_COUNT (TRNG_ADDR_BASE + 0x834)
|
||
#define TEST_MODE_SHA_DONE (TRNG_ADDR_BASE + 0x838)
|
||
#define DRBG_DATA_OUT (TRNG_ADDR_BASE + 0x83C)
|
||
#define DRBG_DEBUG_OUT_0 (TRNG_ADDR_BASE + 0x840)
|
||
#define DRBG_DEBUG_OUT_1 (TRNG_ADDR_BASE + 0x844)
|
||
#define DRBG_DEBUG_OUT_2 (TRNG_ADDR_BASE + 0x848)
|
||
#define DRBG_DEBUG_OUT_3 (TRNG_ADDR_BASE + 0x84C)
|
||
#define INTR_SOURCE (TRNG_ADDR_BASE + 0xC00)
|
||
|
||
#define RAW_HW_INIT 0x80010002
|
||
#define DRBG_HW_INIT 0x80000002
|
||
|
||
/************************************************************************
|
||
* M A C R O S
|
||
*************************************************************************
|
||
*/
|
||
|
||
|
||
/************************************************************************
|
||
* D A T A T Y P E S
|
||
*************************************************************************
|
||
*/
|
||
struct ecnt_trng {
|
||
struct device *dev;
|
||
void __iomem *base;
|
||
u32 irq;
|
||
};
|
||
|
||
/************************************************************************
|
||
* STATIC VARIABLE DECLARATIONS
|
||
*************************************************************************
|
||
*/
|
||
struct ecnt_trng *ecnt_trng = NULL;
|
||
static const struct of_device_id ecnt_trng_of_id[] = {
|
||
{ .compatible = "econet,ecnt-trng"},
|
||
{ /* sentinel */}
|
||
};
|
||
MODULE_DEVICE_TABLE(of, ecnt_trng_of_id);
|
||
|
||
char g_MmSel = 'm';
|
||
|
||
/************************************************************************
|
||
* E X T E R N A L D A T A D E C L A R A T I O N S
|
||
*************************************************************************
|
||
*/
|
||
|
||
/************************************************************************
|
||
* F U N C T I O N D E C L A R A T I O N S
|
||
*************************************************************************
|
||
*/
|
||
extern void scu_Enable_Module_Clock (u8 module);
|
||
|
||
/************************************************************************
|
||
* S T A T I C F U N C T I O N
|
||
*************************************************************************
|
||
*/
|
||
/* don't EXPORT this function. Create API for your purpose instead. */
|
||
static u32 get_trng_data(u32 reg)
|
||
{
|
||
return readl(ecnt_trng->base + reg);
|
||
}
|
||
|
||
/* don't EXPORT this function. Create API for your purpose instead. */
|
||
static void set_trng_data(u32 reg, u32 val)
|
||
{
|
||
writel(val, ecnt_trng->base + reg);
|
||
}
|
||
|
||
/* M TRNG API */
|
||
static u32 TRNG_GET_M_RNG_ADDR_RO_OUT(void)
|
||
{
|
||
return get_trng_data(RNG_ADDR_RO_OUT);
|
||
}
|
||
|
||
static u32 TRNG_GET_M_RNG_ADDR_RO_STATUS(void)
|
||
{
|
||
return get_trng_data(RNG_ADDR_RO_STATUS);
|
||
}
|
||
|
||
static void TRNG_SET_M_RNG_ADDR_RW_EN(u32 val)
|
||
{
|
||
set_trng_data(RNG_ADDR_RW_EN, val);
|
||
return;
|
||
}
|
||
|
||
static void TRNG_SET_M_RNG_ADDR_WO_IRQ_CLR(u32 val)
|
||
{
|
||
set_trng_data(RNG_ADDR_WO_IRQ_CLR, val);
|
||
return;
|
||
}
|
||
|
||
/* m TRNG API */
|
||
static u32 TRNG_GET_HEALTH_TEST_STATUS(void)
|
||
{
|
||
return get_trng_data(HEALTH_TEST_STATUS);
|
||
}
|
||
|
||
static u32 TRNG_GET_RAW_DATA_OUT(void)
|
||
{
|
||
return get_trng_data(RAW_DATA_OUT);
|
||
}
|
||
|
||
static u32 TRNG_GET_TEST_MODE_SHA_DONE(void)
|
||
{
|
||
return get_trng_data(TEST_MODE_SHA_DONE);
|
||
}
|
||
|
||
static u32 TRNG_GET_DRBG_DATA_OUT(void)
|
||
{
|
||
return get_trng_data(DRBG_DATA_OUT);
|
||
}
|
||
|
||
static u32 TRNG_GET_TRNG_IP_RDY(void)
|
||
{
|
||
return get_trng_data(TRNG_IP_RDY);
|
||
}
|
||
|
||
static u32 TRNG_GET_INTR_SOURCE(void)
|
||
{
|
||
return get_trng_data(INTR_SOURCE);
|
||
}
|
||
|
||
static void TRNG_SET_INTR_EN(u32 val)
|
||
{
|
||
set_trng_data(INTR_EN, val);
|
||
return;
|
||
}
|
||
|
||
static void TRNG_SET_CONTINUE_FAIL_CLR(u32 val)
|
||
{
|
||
set_trng_data(CONTINUE_FAIL_CLR, val);
|
||
return;
|
||
}
|
||
|
||
static void TRNG_SET_NS_SEL_AND_DAT_EN(u32 val)
|
||
{
|
||
set_trng_data(NS_SEL_AND_DAT_EN, val);
|
||
return;
|
||
}
|
||
|
||
/* Shared TRNG API of Mm */
|
||
static int is_drbg_data_ready(void)
|
||
{
|
||
if('M' == g_MmSel){
|
||
return ((TRNG_GET_M_RNG_ADDR_RO_STATUS() >> 4) & 0x1);
|
||
}else{
|
||
return ((TRNG_GET_TEST_MODE_SHA_DONE() >> 31) & 0x1);
|
||
}
|
||
}
|
||
|
||
static unsigned int get_drbg_data(void)
|
||
{
|
||
if('M' == g_MmSel){
|
||
return TRNG_GET_M_RNG_ADDR_RO_OUT();
|
||
}else{
|
||
return TRNG_GET_DRBG_DATA_OUT();
|
||
}
|
||
}
|
||
|
||
int ecnt_trng_get_data(uint8_t raw_drbg_sel, unsigned int* data)
|
||
{
|
||
unsigned int count = 0;
|
||
switch(raw_drbg_sel){
|
||
case 0:
|
||
if('M' == g_MmSel){
|
||
printk("No support for raw data acuqision from M trng hardware module.\n");
|
||
return -1;
|
||
}
|
||
while( ((TRNG_GET_HEALTH_TEST_STATUS() >> 7) & 0x1) == 0x0){
|
||
/* go inside the while-loop if data is not ready */
|
||
mdelay(1);
|
||
if(++count >= 10){
|
||
printk("[Error] Failed to get RAW TRNG data !\n");
|
||
return -1;
|
||
}
|
||
}
|
||
/* read data from RAW register & output using ASCII format */
|
||
*data = TRNG_GET_RAW_DATA_OUT();
|
||
break;
|
||
case 1:
|
||
while(!is_drbg_data_ready()){
|
||
mdelay(1);
|
||
if(++count >= 10){
|
||
printk("[Error] Failed to get DRBG TRNG data !\n");
|
||
return -1;
|
||
}
|
||
}
|
||
*data = get_drbg_data();
|
||
break;
|
||
default:
|
||
printk("[Warnning] No such choice!\n");
|
||
break;
|
||
}
|
||
}
|
||
EXPORT_SYMBOL(ecnt_trng_get_data);
|
||
|
||
static void trng_raw_hw_init(void)
|
||
{
|
||
if('M' == g_MmSel){
|
||
printk("No support for raw data acuqision from M trng hardware module.\n");
|
||
return -1;
|
||
}
|
||
/* Select noise source, enable raw data output, enable ring oscilator */
|
||
/* RAW_HW_INIT: 0x80010002 (m)*/
|
||
TRNG_SET_NS_SEL_AND_DAT_EN(RAW_HW_INIT);
|
||
}
|
||
|
||
static void trng_drbg_hw_init(void)
|
||
{
|
||
if('M' == g_MmSel){
|
||
TRNG_SET_M_RNG_ADDR_RW_EN(M_RAW_NRBG_DRBG_INIT);
|
||
}else{
|
||
/* Select noise source, enable drbg data output, enable ring oscilator */
|
||
/* DRBG_HW_INIT: 0x80000002 (m)*/
|
||
TRNG_SET_NS_SEL_AND_DAT_EN(DRBG_HW_INIT);
|
||
}
|
||
}
|
||
|
||
void hw_init_trng_module (void)
|
||
{
|
||
/* Disable TRNG module in boot loader. Need to enable TRNG at insert TRNG module. */
|
||
scu_Enable_Module_Clock(1);
|
||
}
|
||
EXPORT_SYMBOL(hw_init_trng_module);
|
||
|
||
static void check_trng_ip_status(void)
|
||
{
|
||
unsigned int count = 0;
|
||
|
||
if('M' == g_MmSel){
|
||
/* No such register feature offered in M TRNG hardware module */
|
||
}else{
|
||
while(!(TRNG_GET_TRNG_IP_RDY() & 0x00000001)){
|
||
/* go inside the while-loop if TRNG IP is not ready */
|
||
mdelay(1);
|
||
if(++count >= 10){
|
||
printk("[Error] TRNG IP is not ready !\n");
|
||
return -1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
int trng_hw_init(uint8_t raw_drbg_sel, char MmSel)
|
||
{
|
||
g_MmSel = MmSel;
|
||
switch(raw_drbg_sel){
|
||
case 0:
|
||
trng_raw_hw_init();
|
||
break;
|
||
case 1:
|
||
trng_drbg_hw_init();
|
||
break;
|
||
default:
|
||
printk("[Warnning] No such hardware init choice!\n");
|
||
break;
|
||
}
|
||
|
||
check_trng_ip_status();
|
||
|
||
return 0;
|
||
}
|
||
EXPORT_SYMBOL(trng_hw_init);
|
||
|
||
/* Interrupt service routine */
|
||
static irqreturn_t trng_interrupt_handler(int irq, void *dev_instance)
|
||
{
|
||
if((TRNG_GET_INTR_SOURCE() & 0x1) == 0x1){
|
||
printk("[m TRNG Interrupt]\n");
|
||
/* Clear irq status */
|
||
TRNG_SET_CONTINUE_FAIL_CLR(0x00000001);
|
||
|
||
/* Global reset */
|
||
SET_SCU_RST_RG((GET_SCU_RST_RG() | (1 << 12)));
|
||
SET_SCU_RST_RG((GET_SCU_RST_RG() & (~(1 << 12))));
|
||
}else{
|
||
printk("[M TRNG Interrupt]\n");
|
||
/* Clear irq status */
|
||
TRNG_SET_M_RNG_ADDR_WO_IRQ_CLR(0x00000001);
|
||
|
||
/* Global reset */
|
||
SET_SCU_RST_RG((GET_SCU_RST_RG() | (1 << 11)));
|
||
SET_SCU_RST_RG((GET_SCU_RST_RG() & (~(1 << 11))));
|
||
}
|
||
|
||
return IRQ_HANDLED;
|
||
}
|
||
|
||
static int ecnt_trng_drv_probe(struct platform_device *pdev)
|
||
{
|
||
struct resource *res = NULL;
|
||
int ret = 0;
|
||
|
||
printk("[trng] ecnt_trng_drv_probe\n");
|
||
if (!pdev->dev.of_node) {
|
||
dev_err(&pdev->dev, "No trng DT node found");
|
||
return -EINVAL;
|
||
}
|
||
|
||
/* TRNG allocate memory */
|
||
ecnt_trng = devm_kzalloc(&pdev->dev, sizeof(struct ecnt_trng), GFP_KERNEL);
|
||
if (!ecnt_trng)
|
||
return -ENOMEM;
|
||
|
||
platform_set_drvdata(pdev, ecnt_trng);
|
||
|
||
/* get TRNG base address */
|
||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||
ecnt_trng->base = devm_ioremap_resource(&pdev->dev, res);
|
||
if (IS_ERR(ecnt_trng->base))
|
||
return PTR_ERR(ecnt_trng->base);
|
||
|
||
ecnt_trng->dev = &pdev->dev;
|
||
/* TRNG Interrupt Setting */
|
||
ecnt_trng->irq = platform_get_irq(pdev, 0);
|
||
if(ecnt_trng->irq <= 0) {
|
||
printk("\n get pbus timeout irq number failed\n");
|
||
return ecnt_trng->irq;
|
||
}
|
||
ret = request_irq(ecnt_trng->irq, trng_interrupt_handler, 0, "TRNG_Data_Ready_ISR",
|
||
ecnt_trng->dev);
|
||
if(ret) {
|
||
printk("\n request_irq() (irq number: %d) failed (ret: %d)\n", ecnt_trng->irq, ret);
|
||
return (ret);
|
||
}
|
||
|
||
#if 0
|
||
printk("[trng] res->name:%s\n", res->name);
|
||
printk("[trng] res->start:0x%llx ===\n", res->start);
|
||
printk("[trng] res->end:0x%llx ===\n", res->end);
|
||
printk("[trng] ecnt_trng->base:0x%lx\n", (unsigned long)ecnt_trng->base);
|
||
printk("[trng] ecnt_trng->irq:0x%lx\n", (unsigned long)ecnt_trng->irq);
|
||
#endif
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int ecnt_trng_drv_remove(struct platform_device *pdev)
|
||
{
|
||
printk("[trng] ecnt_trng_drv_remove\n");
|
||
return 0;
|
||
}
|
||
|
||
/************************************************************************
|
||
* P L A T F O R M D R I V E R S D E C L A R A T I O N S
|
||
*************************************************************************
|
||
*/
|
||
|
||
static struct platform_driver ecnt_trng_driver = {
|
||
.probe = ecnt_trng_drv_probe,
|
||
.remove = ecnt_trng_drv_remove,
|
||
.driver = {
|
||
.name = "ecnt-trng",
|
||
.of_match_table = ecnt_trng_of_id
|
||
},
|
||
};
|
||
module_platform_driver(ecnt_trng_driver);
|
||
MODULE_DESCRIPTION("EcoNet trng Driver");
|
||
|
||
|