/* * GSC3280 sf US23040452 Watchdog Driver * * Copyright (C) 2017 BLX IC Design Corp.,Ltd. * Author: Song Xingjia, songxingjia@china-cpu.com * * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct wd_gpio_param { unsigned int index; unsigned int value; unsigned int dirt; //0: input ; 1:output }; #define WGPIO_IOC_MAGIC 'W' #define WGPIO_SET_VAL _IOW(WGPIO_IOC_MAGIC, 0, struct wd_gpio_param) #define WGPIO_GET_VAL _IOR(WGPIO_IOC_MAGIC, 1, struct wd_gpio_param) #define WGPIO_SET_DIRT _IOW(WGPIO_IOC_MAGIC, 2, struct wd_gpio_param) #define WGPIO_GET_DIRT _IOR(WGPIO_IOC_MAGIC, 3, struct wd_gpio_param) enum{ // WORK_LED, //GPIO21 v22 // TEST_LED, //GPIO30 v25 WTD_OE, //GPIO19 WTD_FEED, //GPIO20 // PHY_RESET, //GPIO62 // PHY_RXER, //GPIO15 WGPIO_MAX_PIN_NR }; struct gpio WGPIOs[WGPIO_MAX_PIN_NR] = { // {GSC3280_GPA(21), GPIOF_OUT_INIT_LOW, "SF work led"}, // {GSC3280_GPA(30), GPIOF_OUT_INIT_HIGH, "test led"}, {GSC3280_GPA(19), GPIOF_OUT_INIT_HIGH, "watchdog enable"}, {GSC3280_GPA(20), GPIOF_OUT_INIT_LOW, "watchdog feed"}, // {GSC3280_GPB(30), GPIOF_OUT_INIT_HIGH, "PHY_RESET"}, // {GSC3280_GPA(15), GPIOF_IN, "PHY RX ERROR"}, }; struct wd_gpio { struct cdev cdev; struct class *WGPIO_class; dev_t dev; }; struct wd_gpio wd_gpio_priv; static long WGPIO_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){ int err = 0; struct wd_gpio_param param; /* Check type and command number */ if (_IOC_TYPE(cmd) != WGPIO_IOC_MAGIC) return -ENOTTY; /* Check access direction once here; don't repeat below. * IOC_DIR is from the user perspective, while access_ok is * from the kernel perspective; so they look reversed. */ if (_IOC_DIR(cmd) & _IOC_READ) err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); if (err) return -EFAULT; copy_from_user(¶m, (struct wd_gpio_param*)arg, sizeof(struct wd_gpio_param)); if (param.index >= WGPIO_MAX_PIN_NR) return -EINVAL; switch(cmd) { case WGPIO_SET_VAL: if(param.value&0x1) gpio_set_value(WGPIOs[param.index].gpio,1); else gpio_set_value(WGPIOs[param.index].gpio,0); break; case WGPIO_GET_VAL: param.value = gpio_get_value(WGPIOs[param.index].gpio); copy_to_user((struct wd_gpio_param*)arg, ¶m, sizeof(struct wd_gpio_param)); break; case WGPIO_SET_DIRT: if(param.dirt&0x1) { if(param.value&0x1) gpio_direction_output(WGPIOs[param.index].gpio, 1); else gpio_direction_output(WGPIOs[param.index].gpio, 0); } else gpio_direction_input(WGPIOs[param.index].gpio); break; case WGPIO_GET_DIRT: if(WGPIOs[param.index].gpio<33) //GPA { param.dirt = (*(volatile unsigned int *)(0xbc110004) >> WGPIOs[param.index].gpio) & 0x1; //printk(KERN_INFO "Test gpio dirt is %0x \n", param.dirt); } else if (WGPIOs[param.index].gpio<65) //GPB param.dirt = (*(volatile unsigned int *)(0xbc110010) >> (WGPIOs[param.index].gpio-32)) & 0x1; else if (WGPIOs[param.index].gpio<89) //GPC param.dirt = (*(volatile unsigned int *)(0xbc11001C) >> (WGPIOs[param.index].gpio-64)) & 0x1; copy_to_user((struct wd_gpio_param*)arg, ¶m, sizeof(struct wd_gpio_param)); break; default: return -ENOTTY; } return 0; } static int WGPIO_open(struct inode *inode, struct file *filp) { return 0; } static int WGPIO_release(struct inode *inode, struct file *filp) { return 0; } struct file_operations WGPIO_fops = { .owner = THIS_MODULE, .unlocked_ioctl = WGPIO_ioctl, .open = WGPIO_open, .release = WGPIO_release, }; static int __init wd_gpio_init(void) { int err,ret; int result; int major; result = alloc_chrdev_region(&wd_gpio_priv.dev, 0, 0, "wgpio"); major = MAJOR(wd_gpio_priv.dev); if (result < 0) { printk(KERN_WARNING "wd_gpio: can't get major %d\n", major); return result; } cdev_init(&wd_gpio_priv.cdev, &WGPIO_fops); wd_gpio_priv.cdev.owner = THIS_MODULE; wd_gpio_priv.cdev.ops = &WGPIO_fops; err = cdev_add(&wd_gpio_priv.cdev, wd_gpio_priv.dev, 1); if (err) { printk(KERN_NOTICE "Error[%d] cdev_add .\n", err); return -1; } wd_gpio_priv.WGPIO_class = class_create(THIS_MODULE, "wgpio"); if (IS_ERR(wd_gpio_priv.WGPIO_class)) { printk(KERN_ERR "Failed in create WGPIO_cdev class\n"); return -1; } device_create(wd_gpio_priv.WGPIO_class, NULL, wd_gpio_priv.dev, NULL, "wgpio"); ret = gpio_request_array(WGPIOs, WGPIO_MAX_PIN_NR); // printk(KERN_INFO "Test gpio request array %d\n",ret); if (ret < 0) { printk(KERN_INFO "GPIO request error \n"); } return 0; } static void __exit wd_gpio_exit(void) { gpio_free_array(WGPIOs, WGPIO_MAX_PIN_NR); cdev_del(&wd_gpio_priv.cdev); unregister_chrdev_region(wd_gpio_priv.dev, 1); device_destroy(wd_gpio_priv.WGPIO_class, wd_gpio_priv.dev); class_destroy(wd_gpio_priv.WGPIO_class); } module_init(wd_gpio_init); module_exit(wd_gpio_exit); MODULE_AUTHOR("songxingjia@china-cpu.com"); MODULE_DESCRIPTION("GSC3280 WATCHDOG GPIO Driver"); MODULE_LICENSE("GPL");