yb_arm/driver/stc-dev/stc-dev.c
2023-07-12 14:14:31 +08:00

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");