/* * amr_ledctrl.c - driver for Emi Display in Loongson soc * * Copyright (C) 2012 Loongson Corporation * * 2013-01-31 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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"); /*----------------------------------------------------------------------------*/