ckfwq/linux-3.0.4/drivers/char/wd_gpio.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(&param, (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, &param, 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, &param, 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");