/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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");