358 lines
10 KiB
C
358 lines
10 KiB
C
![]() |
/*
|
||
|
* Driver for stc module.
|
||
|
*
|
||
|
* Copyright (C) 2022, liweihua@smartlogictech.com
|
||
|
* Copyright 2022
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License version 2 as
|
||
|
* published by the Free Software Foundation.
|
||
|
*/
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/device.h>
|
||
|
#include <linux/cdev.h>
|
||
|
#include <linux/uaccess.h>
|
||
|
#include <linux/wait.h>
|
||
|
#include <linux/init.h>
|
||
|
#include <linux/err.h>
|
||
|
#include <linux/gpio.h>
|
||
|
#include <linux/io.h>
|
||
|
#include <linux/of.h>
|
||
|
#include <linux/of_gpio.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
#include "stc_def.h"
|
||
|
|
||
|
#define UCP4008_STC_MODULE_VERSION ("V1.0")
|
||
|
#define UCP4008_STC_DEVICE_NAME "stc_irq"
|
||
|
|
||
|
struct stc_driver g_stc_drv;
|
||
|
|
||
|
static irqreturn_t stc_tod_1pps_handler(int irqid, void *param)
|
||
|
{
|
||
|
struct stc_driver *pirqstc = param;
|
||
|
|
||
|
__raw_writel(0x1,pirqstc->io_base_addr + 4); //clear int
|
||
|
pirqstc->irq_1pps_tod_cnt++;
|
||
|
pirqstc->cond_1pps_tod = STC_TOD_1PPS;
|
||
|
//printk("enter:%s,complete_cond = %d,\n",__func__,pirqstc->complete_cond);
|
||
|
wake_up_interruptible(&pirqstc->pp1s_tod_queue);
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
static irqreturn_t stc_1pps_in_handler(int irqid, void *param)
|
||
|
{
|
||
|
struct stc_driver *pirqstc = param;
|
||
|
|
||
|
__raw_writel(0x2,pirqstc->io_base_addr + 4); //clear int
|
||
|
pirqstc->irq_1pps_in_cnt++;
|
||
|
pirqstc->cond_1pps_in = STC_EX_1PPS_IN;
|
||
|
//printk("enter:%s,complete_cond = %d,\n",__func__,pirqstc->complete_cond);
|
||
|
wake_up_interruptible(&pirqstc->pp1s_in_queue);
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
static irqreturn_t stc_hscc_dump_handler(int irqid, void *param)
|
||
|
{
|
||
|
struct stc_driver *pirqstc = param;
|
||
|
|
||
|
__raw_writel(0x4,pirqstc->io_base_addr + 4); //clear int
|
||
|
pirqstc->irq_hscc_dump_cnt++;
|
||
|
pirqstc->cond_hscc_dump = STC_HSCC_DUMP;
|
||
|
//printk("enter:%s,complete_cond = %d,\n",__func__,pirqstc->complete_cond);
|
||
|
wake_up_interruptible(&pirqstc->hscc_dump_queue);
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
static int stc_open(struct inode* inode, struct file* file)
|
||
|
{
|
||
|
printk(KERN_INFO "[%s]\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static ssize_t stc_read(struct file* file, char __user* buf, size_t size, loff_t* pos)
|
||
|
{
|
||
|
sint32 ret;
|
||
|
uint32 cond_value;
|
||
|
|
||
|
if(STC_TOD_1PPS == size)
|
||
|
{
|
||
|
ret = wait_event_interruptible(g_stc_drv.pp1s_tod_queue, g_stc_drv.cond_1pps_tod);
|
||
|
cond_value = g_stc_drv.irq_1pps_tod_cnt;
|
||
|
g_stc_drv.cond_1pps_tod = STC_INT_RESERVED;
|
||
|
}
|
||
|
else if(STC_EX_1PPS_IN == size)
|
||
|
{
|
||
|
ret = wait_event_interruptible(g_stc_drv.pp1s_in_queue, g_stc_drv.cond_1pps_in);
|
||
|
cond_value = g_stc_drv.irq_1pps_in_cnt;
|
||
|
g_stc_drv.cond_1pps_in = STC_INT_RESERVED;
|
||
|
}
|
||
|
else if(STC_HSCC_DUMP == size)
|
||
|
{
|
||
|
ret = wait_event_interruptible(g_stc_drv.hscc_dump_queue, g_stc_drv.cond_hscc_dump);
|
||
|
cond_value = g_stc_drv.irq_hscc_dump_cnt;
|
||
|
g_stc_drv.cond_hscc_dump = STC_INT_RESERVED;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return FAILURE;
|
||
|
}
|
||
|
|
||
|
if(ret != 0)
|
||
|
{
|
||
|
printk("exe:%s error!\n",__func__);
|
||
|
return FAILURE;
|
||
|
}
|
||
|
ret = copy_to_user(buf, &cond_value, sizeof(uint32));
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static ssize_t stc_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static sint64 stc_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
|
||
|
{
|
||
|
sint32 s32Ret = SUCCESS;
|
||
|
stc_int_type_e int_type;
|
||
|
struct stc_driver *stc_drv = &g_stc_drv;
|
||
|
uint32 irq_enable = __raw_readl(g_stc_drv.io_base_addr + 8);
|
||
|
|
||
|
printk("enter:%s\n",__func__);
|
||
|
|
||
|
switch(cmd)
|
||
|
{
|
||
|
case STC_INT_CFG:
|
||
|
{
|
||
|
s32Ret = copy_from_user(&int_type, (void __user*)arg, sizeof(stc_int_type_e));
|
||
|
if(s32Ret != 0)
|
||
|
{
|
||
|
printk(KERN_INFO "[%s]Copy user data failed!\n", __func__);
|
||
|
return FAILURE;
|
||
|
}
|
||
|
|
||
|
switch(int_type)
|
||
|
{
|
||
|
case STC_TOD_1PPS:
|
||
|
{
|
||
|
s32Ret = request_irq(stc_drv->irq_1pps_tod, stc_tod_1pps_handler, IRQF_SHARED, "tod_1pps", (void *)stc_drv);
|
||
|
if(s32Ret != SUCCESS)
|
||
|
{
|
||
|
free_irq(stc_drv->irq_1pps_tod, stc_drv);
|
||
|
printk(KERN_INFO "[%s]irq_ipps_tod request error!\n", __func__);
|
||
|
return FAILURE;
|
||
|
}
|
||
|
|
||
|
irq_enable |= 1;
|
||
|
__raw_writel(irq_enable,g_stc_drv.io_base_addr + 8); //
|
||
|
break;
|
||
|
}
|
||
|
case STC_EX_1PPS_IN:
|
||
|
{
|
||
|
s32Ret = request_irq(stc_drv->irq_1pps_in, stc_1pps_in_handler, IRQF_SHARED, "1pps_in", (void *)stc_drv);
|
||
|
if(s32Ret != SUCCESS)
|
||
|
{
|
||
|
free_irq(stc_drv->irq_1pps_in, stc_drv);
|
||
|
printk(KERN_INFO "[%s]irq_ipps_in request error!\n", __func__);
|
||
|
return FAILURE;
|
||
|
}
|
||
|
printk(KERN_INFO "[%s]enable irq_ipps_in int!\n", __func__);
|
||
|
irq_enable |= 2;
|
||
|
__raw_writel(irq_enable,g_stc_drv.io_base_addr + 8); //
|
||
|
break;
|
||
|
}
|
||
|
case STC_HSCC_DUMP:
|
||
|
{
|
||
|
s32Ret = request_irq(stc_drv->irq_hscc_dump, stc_hscc_dump_handler, IRQF_SHARED,"hscc_dump" ,(void *)stc_drv);
|
||
|
if(s32Ret != SUCCESS)
|
||
|
{
|
||
|
free_irq(stc_drv->irq_hscc_dump, stc_drv);
|
||
|
printk(KERN_INFO "[%s]irq_hscc_dump request error!\n", __func__);
|
||
|
return FAILURE;
|
||
|
}
|
||
|
irq_enable |= 4;
|
||
|
__raw_writel(irq_enable,g_stc_drv.io_base_addr + 8); //
|
||
|
break;
|
||
|
}
|
||
|
default:break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
s32Ret = FAILURE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return s32Ret;
|
||
|
}
|
||
|
|
||
|
static int stc_release(struct inode* inode, struct file* file)
|
||
|
{
|
||
|
printk(KERN_INFO "[%s]realease \n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct file_operations stc_fops = {
|
||
|
.owner = THIS_MODULE,
|
||
|
.open = stc_open,
|
||
|
.read = stc_read,
|
||
|
.write = stc_write,
|
||
|
.release = stc_release,
|
||
|
.unlocked_ioctl = stc_ioctl,
|
||
|
};
|
||
|
|
||
|
static struct miscdevice g_stc_dev = {
|
||
|
.minor = MISC_DYNAMIC_MINOR,
|
||
|
.name = "stc_dev",
|
||
|
.fops = &stc_fops,
|
||
|
};
|
||
|
|
||
|
#if 0
|
||
|
static int stc_module_init(void)
|
||
|
{
|
||
|
sint32 iRet;
|
||
|
uint32 u32idx;
|
||
|
|
||
|
iRet = misc_register(&g_stc_dev);
|
||
|
if(iRet != SUCCESS)
|
||
|
{
|
||
|
printk("misc_register error! iRet = %d\n", iRet);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
memset(&g_stc_drv, 0, sizeof(struct stc_driver));
|
||
|
|
||
|
g_stc_drv.irq_ipps_in = UCP4008_IRQ_1PPS_IN;
|
||
|
g_stc_drv.irq_hscc_dump = UCP4008_IRQ_HSCC_DUMP;
|
||
|
g_stc_drv.irq_ipps_tod = UCP4008_IRQ_1PPS_TOD;
|
||
|
g_stc_drv.irq_ipps_in_cnt = 0;
|
||
|
g_stc_drv.irq_hscc_dump_cnt = 0;
|
||
|
g_stc_drv.irq_ipps_tod_cnt = 0;
|
||
|
|
||
|
init_waitqueue_head(&g_stc_drv.complete_queue);
|
||
|
g_stc_drv.complete_cond = STC_INT_RESERVED;
|
||
|
|
||
|
g_stc_drv.io_base_addr = ioremap(STC_REG_BASE_ADDR,STC_REG_LEN);
|
||
|
|
||
|
//init timer
|
||
|
#if 0
|
||
|
init_timer(&g_stc_drv.avcn_timer_list);
|
||
|
|
||
|
setup_timer(&g_stc_drv.avcn_timer_list, avcn_debounce_timer, GPIO_INT_FUNC_RESET);
|
||
|
#else
|
||
|
//timer_setup(&g_stc_drv.avcn_timer_list, avcn_debounce_timer, 0);
|
||
|
#endif
|
||
|
printk(KERN_INFO "[%s] module version %s init...\n", __func__, UCP4008_STC_MODULE_VERSION);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void stc_module_exit(void)
|
||
|
{
|
||
|
misc_deregister(&g_stc_dev);
|
||
|
|
||
|
free_irq(g_stc_drv.irq_ipps_in , &g_stc_drv);
|
||
|
free_irq(g_stc_drv.irq_ipps_tod , &g_stc_drv);
|
||
|
free_irq(g_stc_drv.irq_hscc_dump , &g_stc_drv);
|
||
|
|
||
|
printk(KERN_INFO "[%s] module version %s exit...\n", __func__, UCP4008_STC_MODULE_VERSION);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static int smartlogic_stc_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
struct resource *res_common;
|
||
|
struct device *dev = &pdev->dev;
|
||
|
int i, irq, iRet;
|
||
|
int irq_no[3];
|
||
|
|
||
|
printk("enter:%s\n",__func__);
|
||
|
|
||
|
iRet = misc_register(&g_stc_dev);
|
||
|
if(iRet != SUCCESS)
|
||
|
{
|
||
|
printk("misc_register error! iRet = %d\n", iRet);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < 3; ++i)
|
||
|
{
|
||
|
irq = platform_get_irq(pdev, i);
|
||
|
if (irq < 0) {
|
||
|
dev_err(dev, "failed to get irq index %d\n", i);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
irq_no[i] = irq;
|
||
|
}
|
||
|
|
||
|
g_stc_drv.irq_1pps_tod = irq_no[0];
|
||
|
g_stc_drv.irq_1pps_in = irq_no[1];
|
||
|
g_stc_drv.irq_hscc_dump = irq_no[2];
|
||
|
|
||
|
res_common = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||
|
if (!res_common)
|
||
|
{
|
||
|
dev_err(&pdev->dev, "memory resource not found");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
g_stc_drv.io_base_addr = devm_ioremap_resource(dev, res_common);
|
||
|
if (IS_ERR(g_stc_drv.io_base_addr))
|
||
|
{
|
||
|
return PTR_ERR(g_stc_drv.io_base_addr);
|
||
|
}
|
||
|
|
||
|
init_waitqueue_head(&g_stc_drv.pp1s_tod_queue);
|
||
|
init_waitqueue_head(&g_stc_drv.pp1s_in_queue);
|
||
|
init_waitqueue_head(&g_stc_drv.hscc_dump_queue);
|
||
|
g_stc_drv.cond_1pps_tod = STC_INT_RESERVED;
|
||
|
g_stc_drv.cond_1pps_in = STC_INT_RESERVED;
|
||
|
g_stc_drv.cond_hscc_dump = STC_INT_RESERVED;
|
||
|
g_stc_drv.irq_1pps_tod_cnt = 0;
|
||
|
g_stc_drv.irq_1pps_in_cnt = 0;
|
||
|
g_stc_drv.irq_hscc_dump_cnt = 0;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int smartlogic_stc_remove(struct platform_device *pdev)
|
||
|
{
|
||
|
misc_deregister(&g_stc_dev);
|
||
|
|
||
|
free_irq(g_stc_drv.irq_1pps_in , &g_stc_drv);
|
||
|
free_irq(g_stc_drv.irq_1pps_tod , &g_stc_drv);
|
||
|
free_irq(g_stc_drv.irq_hscc_dump , &g_stc_drv);
|
||
|
|
||
|
printk(KERN_INFO "[%s] module version %s exit...\n", __func__, UCP4008_STC_MODULE_VERSION);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct of_device_id smartlogic_stc_match[] = {
|
||
|
{ .compatible = "smartlogic,stc" },
|
||
|
{},
|
||
|
};
|
||
|
MODULE_DEVICE_TABLE(of, smartlogic_stc_match);
|
||
|
|
||
|
static struct platform_driver smartlogic_stc_driver = {
|
||
|
.driver = {
|
||
|
.name = "smartlogic_stc",
|
||
|
.of_match_table = smartlogic_stc_match,
|
||
|
},
|
||
|
.probe = smartlogic_stc_probe,
|
||
|
.remove = smartlogic_stc_remove,
|
||
|
};
|
||
|
module_platform_driver(smartlogic_stc_driver);
|
||
|
|
||
|
MODULE_AUTHOR("liweihua@smartlogictech.com");
|
||
|
MODULE_DESCRIPTION ("Smartlogictech STC module driver!");
|
||
|
MODULE_LICENSE ("GPL");
|
||
|
|