206 lines
5.7 KiB
C
206 lines
5.7 KiB
C
/*
|
|
* 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 <linux/types.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/module.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/init.h>
|
|
#include <linux/err.h>
|
|
#include <linux/rcupdate.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/io.h>
|
|
#include <linux/of.h>
|
|
#include <linux/completion.h>
|
|
#include <asm/io.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/fcntl.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/gpio.h>
|
|
#include <gsc3280/gpio.h>
|
|
|
|
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");
|