/*++
linux/drivers/char/wmt-gpio.c
Copyright (c) 2008 WonderMedia Technologies, Inc.
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.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see .
WonderMedia Technologies, Inc.
10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
--*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define WMT_GPOI_DEV_C
#include "wmt-gpio-dev.h"
#define GPIO_DEVICE_NAME "wmtgpio"
static int gpio_dev_major;
static struct cdev *gpio_cdev = NULL;
struct class *class_gpio_dev;
struct device *gpio_device;
struct gpio_list *g_gpio_list = NULL;
struct gpio_list *g_list_now = NULL;
#define GPIO_PHY_BASE_ADDR (GPIO_BASE_ADDR-WMT_MMAP_OFFSET)
extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
DEFINE_SEMAPHORE(gpio_dev_lock);
static int gpio_dev_open
(
struct inode *inode, /*!<; //[IN] a pointer point to struct inode */
struct file *filp /*!<; //[IN] a pointer point to struct file */
)
{
//printk("gpio_dev_open\n");
return 0;
}
static int gpio_dev_release
(
struct inode *inode, /*!<; //[IN] a pointer point to struct inode */
struct file *filp /*!<; //[IN] a pointer point to struct file */
)
{
return 0;
}
static int init_gpio_list(struct gpio_list *list)
{
list->addr = 0;
list->bitmap = 0;
list->regval = 0;
list->prev = NULL;
list->next = NULL;
return 0;
}
/*
static int tellme_gpio_list(void)
{
struct gpio_list *list;
int i = 0;
if (g_gpio_list == NULL) {
printk("there is no gpio list\n");
return 0;
}
list = g_gpio_list;
while(1) {
printk("list%d : \n",i);
printk("address = 0x%x , bitmap = 0x%x\n", list->addr, list->bitmap);
list = list->next;
if (list == NULL)
break;
}
return 0;
}
*/
static int free_gpio_list(void)
{
struct gpio_list *prev, *now;
prev = g_gpio_list->prev;
now = g_gpio_list;
while(1) {
kfree(now);
now = prev;
if (now == NULL)
break;
prev = now->prev;
}
g_gpio_list = NULL;
g_list_now = NULL;
return 0;
}
static struct gpio_list * search_gpio_list(struct wmt_reg_op_t *reg)
{
struct gpio_list *list;
if (g_gpio_list == NULL) {
printk("there is no gpio list\n");
return NULL;
}
list = g_gpio_list;
while(1) {
//printk("search_gpio_list list = 0x%x , reg->addr = 0x%x",list->addr,reg->addr);
if (list->addr == reg->addr)
return list;
if (list->next == NULL)
break;
list = list->next;
};
return NULL;
}
static int add_gpio_list(struct gpio_list *new, struct gpio_list *head)
{
new->prev = head;
head->next = new;
g_list_now = new;
return 0;
}
static int write_gpio_list(struct wmt_reg_op_t *reg)
{
struct gpio_list *list;
if ((reg->addr&0xffff0000) == GPIO_PHY_BASE_ADDR)
reg->addr = (reg->addr+WMT_MMAP_OFFSET);
if (g_gpio_list == NULL) {
g_gpio_list = (struct gpio_list *) kmalloc(sizeof(struct gpio_list), GFP_KERNEL);
if (g_gpio_list == NULL) {
printk("error : alloc gpio list failed\n");
return -1;
}
init_gpio_list(g_gpio_list);
list = g_gpio_list;
g_list_now = g_gpio_list;
} else {
list = search_gpio_list(reg);
if (list == NULL) {
list = (struct gpio_list *) kmalloc(sizeof(struct gpio_list), GFP_KERNEL);
if (list == NULL) {
printk("error : alloc gpio list failed\n");
return -1;
}
init_gpio_list(list);
add_gpio_list(list, g_list_now);
}
}
list->addr = reg->addr;
list->bitmap |= reg->bitmap;
return 0;
}
static int write_wmt_reg(struct wmt_reg_op_t *regop)
{
unsigned int tmp;
if ((regop->addr == 0) || (regop->bitmap == 0)) {
printk("gpio device error : address = %x , value = 0x%x\n",regop->addr,regop->bitmap);
return -1;
}
if (write_gpio_list(regop))
return -1;
/*
printk("w address = 0x%x\n",regop->addr);
printk("w reg->bitmap = 0x%x\n",regop->bitmap);
printk("w reg->regval = 0x%x\n",regop->regval);
*/
regop->regval &= regop->bitmap;
tmp = REG32_VAL(regop->addr);
tmp &= ~(regop->bitmap);
tmp |= regop->regval;
REG32_VAL(regop->addr) = tmp;
return 0;
}
static int read_wmt_reg(struct wmt_reg_op_t *regop)
{
unsigned int tmp;
if ((regop->addr == 0) || (regop->bitmap == 0)) {
printk("gpio device error : address = %x , value = 0x%x\n",regop->addr,regop->bitmap);
return -1;
}
/*
printk("r address = 0x%x\n",regop->addr);
printk("r reg->bitmap = %d\n",regop->bitmap);
printk("r reg->regval = %d\n",regop->regval);
*/
if ((regop->addr&0xffff0000) == GPIO_PHY_BASE_ADDR)
regop->addr = (regop->addr+WMT_MMAP_OFFSET);
tmp = REG32_VAL(regop->addr);
tmp &= regop->bitmap;
regop->regval = tmp;
return 0;
}
/*
extern void wmt_drop_pagecache(void);
extern void wmt_drop_slab(void);
*/
static long gpio_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int err = 0;
int retval = 0;
struct gpio_cfg_t cfg;
struct wmt_reg_op_t regop;
//unsigned int drop_caches;
/* check type and number, if fail return ENOTTY */
if( _IOC_TYPE(cmd) != GPIO_IOC_MAGIC ) return -ENOTTY;
if( _IOC_NR(cmd) >= GPIO_IOC_MAXNR ) return -ENOTTY;
/* check argument area */
if( _IOC_DIR(cmd) & _IOC_READ ) {
err = !access_ok( VERIFY_WRITE, (void __user *) arg, _IOC_SIZE(cmd));
if (err)
printk("VERIFY_WRITE failed");
}
else if ( _IOC_DIR(cmd) & _IOC_WRITE ) {
err = !access_ok( VERIFY_READ, (void __user *) arg, _IOC_SIZE(cmd));
if (err)
printk("VERIFY_READ failed");
}
if( err ) return -EFAULT;
down(&gpio_dev_lock);
switch (cmd) {
case GPIOCFG:
if (copy_from_user(&cfg, (void *) arg, sizeof(struct gpio_cfg_t)))
return -EFAULT;
if (write_wmt_reg(&cfg.ctl)) return -EFAULT;
if (write_wmt_reg(&cfg.oc)) return -EFAULT;
break;
case GPIOWREG:
if (copy_from_user(®op, (void *) arg, sizeof(struct wmt_reg_op_t)))
return -EFAULT;
if (write_wmt_reg(®op)) return -EFAULT;
break;
case GPIORREG:
if (copy_from_user(®op, (void *) arg, sizeof(struct wmt_reg_op_t)))
return -EFAULT;
if (read_wmt_reg(®op)) return -EFAULT;
if (copy_to_user((void *)arg, (void *) ®op, sizeof(struct wmt_reg_op_t)))
return -EFAULT;
break;
/*
case FREESYSCACHES:
drop_caches = (unsigned int)arg;
//printk("FREESYSCACHES , drop_caches = %d\n",drop_caches);
if (drop_caches & 1)
wmt_drop_pagecache();
if (drop_caches & 2)
wmt_drop_slab();
break;
*/
default:
retval = -ENOTTY;
break;
}
up(&gpio_dev_lock);
return retval;
}
/*!*************************************************************************
driver file operations struct define
****************************************************************************/
struct file_operations gpio_dev_fops = {
.owner = THIS_MODULE,
.open = gpio_dev_open,
.unlocked_ioctl = gpio_dev_ioctl,
.release = gpio_dev_release,
};
static int gpio_dev_probe
(
struct platform_device *pdev /*!<; // please add parameters description her*/
)
{
return 0;
}
static int gpio_dev_remove
(
struct platform_device *dev /*!<; // please add parameters description her*/
)
{
if (gpio_cdev)
cdev_del(gpio_cdev);
return 0;
}
static int gpio_dev_suspend
(
struct platform_device *pdev, /*!<; // a pointer point to struct device */
pm_message_t state /*!<; // suspend state */
)
{
struct gpio_list *list;
//int i = 0;
if (g_gpio_list == NULL) {
printk("there is no gpio list\n");
return 0;
}
list = g_gpio_list;
while(1) {
//printk("list%d : \n",i);
//printk("address = 0x%x , bitmap = 0x%x , raw = 0x%x\n", list->addr, list->bitmap,REG32_VAL(list->addr));
list->regval = REG32_VAL(list->addr)&list->bitmap;
//printk("regval = 0x%x\n", list->regval);
list = list->next;
if (list == NULL)
break;
//i++;
}
return 0;
}
static int gpio_dev_resume
(
struct platform_device *pdev /*!<; // a pointer point to struct device */
)
{
struct gpio_list *list;
int tmp = 0;
//int i = 0;
if (g_gpio_list == NULL) {
printk("there is no gpio list\n");
return 0;
}
list = g_gpio_list;
while(1) {
//printk("list%d : \n",i);
//printk("address = 0x%x , bitmap = 0x%x , raw = 0x%x\n", list->addr, list->bitmap,REG32_VAL(list->addr));
//printk("regval = 0x%x\n", list->regval);
tmp = REG32_VAL(list->addr);
tmp &= ~list->bitmap;
tmp |= list->regval;
REG32_VAL(list->addr) = tmp;
//printk("raw = 0x%x\n",REG32_VAL(list->addr));
list = list->next;
if (list == NULL)
break;
//i++;
}
return 0;
}
/*!*************************************************************************
device driver struct define
****************************************************************************/
static struct platform_driver gpio_dev_driver = {
.driver.name = "wmtgpio", // This name should equal to platform device name.
.probe = gpio_dev_probe,
.remove = gpio_dev_remove,
.suspend = gpio_dev_suspend,
.resume = gpio_dev_resume
};
static void gpio_dev_platform_release
(
struct device *device /*!<; // please add parameters description her*/
)
{
}
/*!*************************************************************************
platform device struct define
****************************************************************************/
static struct platform_device gpio_dev_device = {
.name = "wmtgpio",
.id = 0,
.dev = { .release = gpio_dev_platform_release,
},
};
static int gpio_dev_init(void)
{
int ret;
dev_t dev_no;
gpio_dev_major = register_chrdev (0, GPIO_DEVICE_NAME, &gpio_dev_fops);
if (gpio_dev_major < 0) {
printk("get gpio_dev_major failed\n");
return -EFAULT;
}
printk("mknod /dev/%s c %d 0\n", GPIO_DEVICE_NAME, gpio_dev_major);
class_gpio_dev = class_create(THIS_MODULE, GPIO_DEVICE_NAME);
if (IS_ERR(class_gpio_dev)) {
ret = PTR_ERR(class_gpio_dev);
printk(KERN_ERR "Can't create class : %s !!\n",GPIO_DEVICE_NAME);
return ret;
}
dev_no = MKDEV(gpio_dev_major, 0);
gpio_device = device_create(class_gpio_dev, NULL, dev_no, NULL, GPIO_DEVICE_NAME);
if (IS_ERR(gpio_device)) {
ret = PTR_ERR(gpio_device);
printk(KERN_ERR "Failed to create device %s !!!",GPIO_DEVICE_NAME);
return ret;
}
ret = platform_device_register(&gpio_dev_device);
if (ret != 0)
return -ENODEV;
ret = platform_driver_register(&gpio_dev_driver);
if (ret != 0) {
platform_device_unregister(&gpio_dev_device);
return -ENODEV;
}
return ret;
}
module_init(gpio_dev_init);
static void gpio_dev_exit(void)
{
dev_t dev_no;
free_gpio_list();
platform_driver_unregister(&gpio_dev_driver);
platform_device_unregister(&gpio_dev_device);
dev_no = MKDEV(gpio_dev_major, 0);
device_destroy(class_gpio_dev, dev_no);
class_destroy(class_gpio_dev);
unregister_chrdev(gpio_dev_major, GPIO_DEVICE_NAME);
return;
}
module_exit(gpio_dev_exit);
MODULE_AUTHOR("WonderMedia Technologies, Inc.");
MODULE_DESCRIPTION("WMT [gpio device] driver");
MODULE_LICENSE("GPL");
#undef WMT_GPOI_DEV_C