275 lines
6.9 KiB
C
275 lines
6.9 KiB
C
/*
|
|
* amr_ledctrl.c - driver for Emi Display in Loongson soc
|
|
*
|
|
* Copyright (C) 2012 Loongson Corporation
|
|
*
|
|
* 2013-01-31
|
|
*/
|
|
#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/timer.h>
|
|
#include "amr_ledctrl.h"
|
|
|
|
|
|
static void amr_stop_led(struct amr_ledsctrl_param *param);
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
struct gpio amr_leds_gpios[AMR_MAX_PIN_NR] = {
|
|
{GSC3280_GPC(15), GPIOF_OUT_INIT_HIGH, "AMR run led"},
|
|
{GSC3280_GPC(16), GPIOF_OUT_INIT_HIGH, "AMR alarm led"}
|
|
};
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
struct amr_ledctrl {
|
|
struct cdev cdev;
|
|
struct class *amrleds_class;
|
|
dev_t dev;
|
|
|
|
bool led_on;
|
|
bool timer_run;
|
|
unsigned int total_time;
|
|
struct gsc3280_hard_timer amr_hard_timer;
|
|
struct amr_ledsctrl_param amr_param;
|
|
};
|
|
|
|
struct amr_ledctrl amr_ledctrl_priv;
|
|
|
|
|
|
void amr_hard_timer_fn(unsigned long data)
|
|
{
|
|
struct amr_ledsctrl_param *param = &amr_ledctrl_priv.amr_param;
|
|
|
|
if (amr_ledctrl_priv.led_on == true)
|
|
amr_ledctrl_priv.total_time += param->on_time;
|
|
else
|
|
amr_ledctrl_priv.total_time += param->off_time;
|
|
|
|
if (amr_ledctrl_priv.total_time >= param->run_time)
|
|
{
|
|
amr_stop_led(param);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (amr_ledctrl_priv.led_on == true)
|
|
{
|
|
amr_ledctrl_priv.amr_hard_timer.expires = param->off_time;
|
|
gpio_set_value(amr_leds_gpios[param->led_index].gpio, 1);
|
|
amr_ledctrl_priv.led_on = false;
|
|
}
|
|
else
|
|
{
|
|
amr_ledctrl_priv.amr_hard_timer.expires = param->on_time;
|
|
gpio_set_value(amr_leds_gpios[param->led_index].gpio, 0);
|
|
amr_ledctrl_priv.led_on = true;
|
|
}
|
|
|
|
gsc3280_mod_timer(&amr_ledctrl_priv.amr_hard_timer);
|
|
}
|
|
}
|
|
|
|
static void amr_stop_led(struct amr_ledsctrl_param *param)
|
|
{
|
|
amr_ledctrl_priv.led_on = false;
|
|
|
|
gsc3280_timer_stop(&amr_ledctrl_priv.amr_hard_timer);
|
|
amr_ledctrl_priv.timer_run = false;
|
|
|
|
gpio_set_value(amr_leds_gpios[param->led_index].gpio, 1);
|
|
|
|
}
|
|
|
|
static int amr_start_led(struct amr_ledsctrl_param *param)
|
|
{
|
|
amr_ledctrl_priv.timer_run = true;
|
|
|
|
amr_ledctrl_priv.amr_hard_timer.type = ONCE;
|
|
amr_ledctrl_priv.amr_hard_timer.expires = param->on_time;
|
|
amr_ledctrl_priv.amr_hard_timer.function = amr_hard_timer_fn;
|
|
amr_ledctrl_priv.amr_hard_timer.data = (unsigned long)param;
|
|
|
|
gpio_set_value(amr_leds_gpios[param->led_index].gpio, 0);
|
|
amr_ledctrl_priv.led_on = true;
|
|
gsc3280_timer_start(&amr_ledctrl_priv.amr_hard_timer);
|
|
amr_ledctrl_priv.total_time = 0;
|
|
|
|
return 0;
|
|
}
|
|
/*----------------------------------------------------------------------------*/
|
|
/*
|
|
*/
|
|
static long amrleds_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
{
|
|
int ret = 0;
|
|
struct amr_ledsctrl_param *param = &amr_ledctrl_priv.amr_param;
|
|
|
|
if (_IOC_TYPE(cmd) != AMR_LED_MAGIC) return -ENOTTY;
|
|
if (_IOC_NR(cmd) > EMIDISP_IOC_MAXNR) return -ENOTTY;
|
|
|
|
/*
|
|
if (amr_ledctrl_priv.timer_run == true)
|
|
return -EBUSY;
|
|
*/
|
|
copy_from_user(param, (unsigned char*)arg, sizeof(struct amr_ledsctrl_param));
|
|
|
|
if (param->led_index >= AMR_MAX_PIN_NR) return -EINVAL;
|
|
|
|
if (param->run_time < (param->on_time + param->off_time)) {
|
|
param->run_time = param->on_time + param->off_time;
|
|
}
|
|
|
|
switch(cmd) {
|
|
case AMR_LED_RUN:
|
|
if (0 != param->on_time) {
|
|
ret = amr_start_led(param);
|
|
if (ret)
|
|
return ret;
|
|
} else
|
|
return -EINVAL;
|
|
|
|
break;
|
|
case AMR_LED_STOP:
|
|
amr_stop_led(param);
|
|
|
|
break;
|
|
case AMR_LED_ON:
|
|
gpio_set_value(amr_leds_gpios[param->led_index].gpio, 0);
|
|
amr_ledctrl_priv.led_on = true;
|
|
break;
|
|
|
|
case AMR_LED_OFF:
|
|
gpio_set_value(amr_leds_gpios[param->led_index].gpio, 1);
|
|
amr_ledctrl_priv.led_on = false;
|
|
break;
|
|
default:
|
|
return -ENOTTY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int amrleds_open(struct inode *inode, struct file *filp) {
|
|
|
|
int ret = 0;
|
|
|
|
ret = gpio_request_array(amr_leds_gpios, AMR_MAX_PIN_NR);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
memset(&amr_ledctrl_priv.amr_hard_timer,
|
|
0x00, sizeof(amr_ledctrl_priv.amr_hard_timer));
|
|
|
|
ret = gsc3280_request_hard_timer(&amr_ledctrl_priv.amr_hard_timer);
|
|
if (ret)
|
|
return ret;
|
|
|
|
amr_ledctrl_priv.timer_run = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int amrleds_release(struct inode *inode, struct file *filp)
|
|
{
|
|
gsc3280_free_hard_timer(&amr_ledctrl_priv.amr_hard_timer);
|
|
|
|
gpio_free_array(amr_leds_gpios, AMR_MAX_PIN_NR);
|
|
|
|
amr_stop_led(&amr_ledctrl_priv.amr_param);
|
|
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct file_operations amrleds_fops = {
|
|
.owner = THIS_MODULE,
|
|
.unlocked_ioctl = amrleds_ioctl,
|
|
.open = amrleds_open,
|
|
.release = amrleds_release,
|
|
};
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static int __init amr_ledctrl_init(void)
|
|
{
|
|
int err;
|
|
int result;
|
|
int major;
|
|
|
|
result = alloc_chrdev_region(&amr_ledctrl_priv.dev, 0, 0, "amrleds");
|
|
major = MAJOR(amr_ledctrl_priv.dev);
|
|
if (result < 0) {
|
|
printk(KERN_WARNING "amr_ledctrl: can't get major %d\n", major);
|
|
return result;
|
|
}
|
|
|
|
cdev_init(&amr_ledctrl_priv.cdev, &amrleds_fops);
|
|
amr_ledctrl_priv.cdev.owner = THIS_MODULE;
|
|
amr_ledctrl_priv.cdev.ops = &amrleds_fops;
|
|
|
|
err = cdev_add(&amr_ledctrl_priv.cdev, amr_ledctrl_priv.dev, 1);
|
|
if (err) {
|
|
printk(KERN_NOTICE "Error[%d] cdev_add emidisp\n", err);
|
|
return -1;
|
|
}
|
|
|
|
amr_ledctrl_priv.amrleds_class = class_create(THIS_MODULE, "amrleds");
|
|
if (IS_ERR(amr_ledctrl_priv.amrleds_class)) {
|
|
printk(KERN_ERR "Failed in create amrleds_cdev class\n");
|
|
return -1;
|
|
}
|
|
|
|
device_create(amr_ledctrl_priv.amrleds_class, NULL,
|
|
amr_ledctrl_priv.dev,
|
|
NULL, "amrleds");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static void __exit amr_ledctrl_exit(void)
|
|
{
|
|
cdev_del(&amr_ledctrl_priv.cdev);
|
|
unregister_chrdev_region(amr_ledctrl_priv.dev, 1);
|
|
device_destroy(amr_ledctrl_priv.amrleds_class, amr_ledctrl_priv.dev);
|
|
class_destroy(amr_ledctrl_priv.amrleds_class);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
module_init(amr_ledctrl_init);
|
|
module_exit(amr_ledctrl_exit);
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
MODULE_AUTHOR("ansonn.wang@gmail.com");
|
|
MODULE_DESCRIPTION("amr leds driver");
|
|
MODULE_LICENSE("GPL");
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|