314 lines
7.9 KiB
C
314 lines
7.9 KiB
C
/*
|
|
* gsc3280_gpioparal.c - driver for Gpio Test
|
|
* 8bits out, 4bits in
|
|
*
|
|
* Copyright (C) 2012 Loongson Corporation
|
|
*
|
|
* 2013-03-15
|
|
*/
|
|
#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>
|
|
#include <gsc3280/sysctl.h>
|
|
#include "gsc3280_gpioparal.h"
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
enum {
|
|
OUT_DAT0 = 0,
|
|
OUT_DAT1,
|
|
OUT_DAT2,
|
|
OUT_DAT3,
|
|
OUT_DAT4,
|
|
OUT_DAT5,
|
|
OUT_DAT6,
|
|
OUT_DAT7,
|
|
OUT_PIN_NR,
|
|
};
|
|
|
|
enum {
|
|
IN_DAT0 = 0,
|
|
IN_DAT1,
|
|
IN_DAT2,
|
|
IN_DAT3,
|
|
IN_PIN_NR
|
|
};
|
|
|
|
enum {
|
|
SWITCH_PIN = 0,
|
|
CTRL_PIN_NR
|
|
};
|
|
|
|
struct gpio out_gpios[OUT_PIN_NR] = {
|
|
{GSC3280_GPB(30), GPIOF_OUT_INIT_HIGH, "OUT_DAT0"},
|
|
{GSC3280_GPA(12), GPIOF_OUT_INIT_HIGH, "OUT_DAT1"},
|
|
{GSC3280_GPA(10), GPIOF_OUT_INIT_HIGH, "OUT_DAT2"},
|
|
{GSC3280_GPA(11), GPIOF_OUT_INIT_HIGH, "OUT_DAT3"},
|
|
{GSC3280_GPA(19), GPIOF_OUT_INIT_HIGH, "OUT_DAT4"},
|
|
{GSC3280_GPA(20), GPIOF_OUT_INIT_HIGH, "OUT_DAT5"},
|
|
{GSC3280_GPA(22), GPIOF_OUT_INIT_HIGH, "OUT_DAT6"},
|
|
{GSC3280_GPA(9), GPIOF_OUT_INIT_HIGH, "OUT_DAT7"}
|
|
};
|
|
|
|
struct gpio in_gpios[IN_PIN_NR] = {
|
|
{GSC3280_GPA(19), GPIOF_IN, "IN_DAT0"},
|
|
{GSC3280_GPA(20), GPIOF_IN, "IN_DAT1"},
|
|
{GSC3280_GPC(2), GPIOF_IN, "IN_DAT2"},
|
|
{GSC3280_GPC(0), GPIOF_IN, "IN_DAT3"}
|
|
};
|
|
|
|
|
|
struct gpio ctl_gpios[CTRL_PIN_NR] = {
|
|
{GSC3280_GPC(1), GPIOF_OUT_INIT_HIGH, "CTL_DAT0"}
|
|
};
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
struct gsc3280_gpioparal {
|
|
struct cdev cdev;
|
|
struct class *gpioparal_class;
|
|
bool out_gpios_init;
|
|
bool in_gpios_init;
|
|
dev_t dev;
|
|
};
|
|
|
|
struct gsc3280_gpioparal gpioparal_priv;
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
static ssize_t gpioparal_write(struct file *filp, const char __user *buf,
|
|
size_t count, loff_t *f_pos) {
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
static ssize_t gpioparal_read(struct file *filp, char __user *buf,
|
|
size_t count, loff_t *f_pos) {
|
|
return 0;
|
|
}
|
|
|
|
void gpio_paral_out_8bits(unsigned char val)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < OUT_PIN_NR; i++) {
|
|
gpio_set_value(out_gpios[i].gpio, (val >> i) & 0x01);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
unsigned char gpio_paral_in_8bits(void)
|
|
{
|
|
int i;
|
|
unsigned char val = 0;
|
|
|
|
gpio_set_value(ctl_gpios[SWITCH_PIN].gpio, 1);
|
|
udelay(1000);
|
|
for (i = 0; i < IN_PIN_NR; i++) {
|
|
val |= ((gpio_get_value(in_gpios[IN_PIN_NR - i - 1].gpio) & 0x01) << i);
|
|
}
|
|
|
|
gpio_set_value(ctl_gpios[SWITCH_PIN].gpio, 0);
|
|
udelay(1000);
|
|
for (i = 0; i < IN_PIN_NR; i++) {
|
|
val |= ((gpio_get_value(in_gpios[IN_PIN_NR - i - 1].gpio) & 0x01)
|
|
<< (i + IN_PIN_NR));
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
static long gpioparal_ioctl(struct file *filp, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
int ret = 0;
|
|
unsigned char val = 0;
|
|
if (_IOC_TYPE(cmd) != GPIOPARAL_IOC_MAGIC) return -ENOTTY;
|
|
if (_IOC_NR(cmd) > GPIOPARAL_IOC_MAXNR) return -ENOTTY;
|
|
|
|
|
|
|
|
switch(cmd) {
|
|
case GPIO_INIT_WRITE:
|
|
if (gpioparal_priv.out_gpios_init || gpioparal_priv.in_gpios_init)
|
|
break;
|
|
ret = gpio_request_array(out_gpios, OUT_PIN_NR);
|
|
if (ret < 0)
|
|
return ret;
|
|
gpioparal_priv.out_gpios_init = true;
|
|
break;
|
|
|
|
case GPIO_INIT_READ:
|
|
if (gpioparal_priv.out_gpios_init || gpioparal_priv.in_gpios_init)
|
|
break;
|
|
ret = gpio_request_array(in_gpios, IN_PIN_NR);
|
|
ret |= gpio_request_array(ctl_gpios, CTRL_PIN_NR);
|
|
if (ret < 0)
|
|
return ret;
|
|
gpioparal_priv.in_gpios_init = true;
|
|
break;
|
|
|
|
case GPIO_RELEASE_WRITE:
|
|
if (gpioparal_priv.out_gpios_init)
|
|
gpio_free_array(out_gpios, OUT_PIN_NR);
|
|
gpioparal_priv.out_gpios_init = false;
|
|
break;
|
|
|
|
case GPIO_RELEASE_READ:
|
|
if (gpioparal_priv.in_gpios_init) {
|
|
gpio_free_array(in_gpios, IN_PIN_NR);
|
|
gpio_free_array(ctl_gpios, CTRL_PIN_NR);
|
|
}
|
|
gpioparal_priv.in_gpios_init = false;
|
|
break;
|
|
|
|
case GPIO_PARAL_OUT:
|
|
if (arg) {
|
|
if (!gpioparal_priv.out_gpios_init)
|
|
return EPERM;
|
|
|
|
__get_user(val, (unsigned char*)arg);
|
|
gpio_paral_out_8bits(val);
|
|
}
|
|
break;
|
|
|
|
case GPIO_PARAL_IN:
|
|
if (arg) {
|
|
if (!gpioparal_priv.in_gpios_init)
|
|
return EPERM;
|
|
|
|
val = gpio_paral_in_8bits();
|
|
__put_user(val, (unsigned char*)arg);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return -ENOTTY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static int gpioparal_open(struct inode *inode, struct file *filp) {
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static int gpioparal_release(struct inode *inode, struct file *filp)
|
|
{
|
|
if (gpioparal_priv.out_gpios_init)
|
|
gpio_free_array(out_gpios, OUT_PIN_NR);
|
|
if (gpioparal_priv.in_gpios_init) {
|
|
gpio_free_array(in_gpios, IN_PIN_NR);
|
|
gpio_free_array(ctl_gpios, CTRL_PIN_NR);
|
|
}
|
|
gpioparal_priv.out_gpios_init = false;
|
|
gpioparal_priv.in_gpios_init = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
struct file_operations gpioparal_fops = {
|
|
.owner = THIS_MODULE,
|
|
.read = gpioparal_read,
|
|
.write = gpioparal_write,
|
|
.unlocked_ioctl = gpioparal_ioctl,
|
|
.open = gpioparal_open,
|
|
.release = gpioparal_release,
|
|
};
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static int __init gsc3280_gpioparal_init(void)
|
|
{
|
|
int err;
|
|
int result;
|
|
int major;
|
|
|
|
result = alloc_chrdev_region(&gpioparal_priv.dev, 0, 0, "gpioparal");
|
|
major = MAJOR(gpioparal_priv.dev);
|
|
if (result < 0) {
|
|
printk(KERN_WARNING "gpioparal: can't get major %d\n", major);
|
|
return result;
|
|
}
|
|
|
|
cdev_init(&gpioparal_priv.cdev, &gpioparal_fops);
|
|
gpioparal_priv.cdev.owner = THIS_MODULE;
|
|
gpioparal_priv.cdev.ops = &gpioparal_fops;
|
|
|
|
err = cdev_add(&gpioparal_priv.cdev, gpioparal_priv.dev, 1);
|
|
if (err) {
|
|
printk(KERN_NOTICE "Error[%d] cdev_add gpioparal\n", err);
|
|
return -1;
|
|
}
|
|
|
|
gpioparal_priv.gpioparal_class = class_create(THIS_MODULE,
|
|
"gsc3280_gpioparal");
|
|
if (IS_ERR(gpioparal_priv.gpioparal_class)) {
|
|
printk(KERN_ERR "Failed in create gpioparal class\n");
|
|
return -1;
|
|
}
|
|
|
|
device_create(gpioparal_priv.gpioparal_class, NULL,
|
|
gpioparal_priv.dev,
|
|
NULL, "gpioparal");
|
|
|
|
gpioparal_priv.out_gpios_init = false;
|
|
gpioparal_priv.in_gpios_init = false;
|
|
|
|
sysctl_iomux_disable(SYSCTL_IOMUX_SPI1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static void __exit gsc3280_gpioparal_exit(void)
|
|
{
|
|
cdev_del(&gpioparal_priv.cdev);
|
|
unregister_chrdev_region(gpioparal_priv.dev, 1);
|
|
device_destroy(gpioparal_priv.gpioparal_class, gpioparal_priv.dev);
|
|
class_destroy(gpioparal_priv.gpioparal_class);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
module_init(gsc3280_gpioparal_init);
|
|
module_exit(gsc3280_gpioparal_exit);
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
MODULE_AUTHOR("ansonn.wang@gmail.com");
|
|
MODULE_DESCRIPTION("gsc3280 gpio parallel test<8bits <-> 4bits> driver");
|
|
MODULE_LICENSE("GPL");
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|