#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*red led gpio PE1*/ #define RLED_NUM 129 #define ENABLE 1 #define DISABLE 0 #define MAX 100 #define LED_DEFAULT_STATE 9 #define LED_DEFAULT_DELAY (HZ / 20) //#define DEBUG_PWM #ifdef DEBUG_PWM #define pwm_printf pr_info #else #define pwm_printf pr_debug #endif static unsigned int duty_ns = 8000000; /*占空,高电平 u32*/ static unsigned long long period_ns = 10000000; /*周期__u64*/ static struct mutex pwm_muter; static struct delayed_work blue_led_delay_work; static struct delayed_work green_led_delay_work; struct pwm_green_led { struct pwm_device *pwm0_dev; int pwm_id; /*0:green;1:blue*/ unsigned long green_status; int time_delay; }; struct pwm_blue_led { struct pwm_device *pwm1_dev; int pwm_id; /*0:green;1:blue*/ unsigned long blue_status; int time_delay; }; struct pwm_red_led { unsigned long red_status; u32 rled_gpio; }; static struct pwm_green_led *t_pwm_green_led; static struct pwm_blue_led *t_pwm_blue_led; static struct pwm_red_led *t_pwm_red_led; static void set_pwm(int pwm_id, int level) { unsigned int ret = 0; unsigned duty_ns, period_ns ; mutex_lock(&pwm_muter); period_ns = 100000; /*1000000000/10000 freq=10KHz, 1s=1000000000ns*/ duty_ns = (period_ns * level)/MAX ; if (pwm_id == 0) { ret = pwm_config(t_pwm_green_led->pwm0_dev, duty_ns, period_ns); ret = pwm_enable(t_pwm_green_led->pwm0_dev); } else if (pwm_id == 1) { ret = pwm_config(t_pwm_blue_led->pwm1_dev, duty_ns, period_ns); ret = pwm_enable(t_pwm_blue_led->pwm1_dev); } pwm_printf("[pwm_leds] pwm_config pwm%d:<%d | %d>, ret = %d\n", pwm_id, duty_ns, period_ns, ret); mutex_unlock(&pwm_muter); } static ssize_t get_blue_func(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", (int)t_pwm_blue_led->blue_status); } static ssize_t set_blue_func(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret; ret = kstrtoul(buf, 10, &t_pwm_blue_led->blue_status); if (t_pwm_blue_led->time_delay != 0) cancel_delayed_work_sync(&blue_led_delay_work); pwm_printf("id = %d, status= %d\n", t_pwm_blue_led->pwm_id, t_pwm_blue_led->blue_status); switch (t_pwm_blue_led->blue_status) { case 0: /*LOW_BRIGHT*/ t_pwm_blue_led->time_delay = 0; set_pwm(t_pwm_blue_led->pwm_id, 90); break; case 1: /*MIDIUM_BRIGHT*/ t_pwm_blue_led->time_delay = 0; set_pwm(t_pwm_blue_led->pwm_id, 50); break; case 2: /*HIGHT_BRIGHT*/ t_pwm_blue_led->time_delay = 0; set_pwm(t_pwm_blue_led->pwm_id, 10); break; case 4: /*DISABLE*/ t_pwm_blue_led->time_delay = 0; pwm_disable(t_pwm_blue_led->pwm1_dev); break; case 6: /*ADJEST_BRGHT*/ t_pwm_blue_led->time_delay = HZ / 2; break; default: t_pwm_blue_led->time_delay = 0; pr_err("inval cmd\n"); break; } if (t_pwm_blue_led->time_delay) schedule_delayed_work(&blue_led_delay_work, t_pwm_blue_led->time_delay); return size; } static ssize_t get_green_func(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", (int)t_pwm_green_led->green_status); } static ssize_t set_green_func(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret; ret = kstrtoul(buf, 10, &t_pwm_green_led->green_status); if (t_pwm_green_led->time_delay != 0) cancel_delayed_work_sync(&green_led_delay_work); pwm_printf("pwm_id = %d, status= %d\n", t_pwm_green_led->pwm_id, t_pwm_green_led->green_status); switch (t_pwm_green_led->green_status) { case 0: /*LOW_BRIGHT*/ t_pwm_green_led->time_delay = 0; set_pwm(t_pwm_green_led->pwm_id, 90); break; case 1: /*MIDIUM_BRIGHT*/ t_pwm_green_led->time_delay = 0; set_pwm(t_pwm_green_led->pwm_id, 50); break; case 2: /*HIGHT_BRIGHT*/ t_pwm_green_led->time_delay = 0; set_pwm(t_pwm_green_led->pwm_id, 10); break; case 4: /*DISABLE*/ t_pwm_green_led->time_delay = 0; pwm_disable(t_pwm_green_led->pwm0_dev); break; case 6: /*ADJEST_BRGHT*/ t_pwm_green_led->time_delay = HZ / 20; break; default: t_pwm_green_led->time_delay = 0; pr_err("inval cmd\n"); break; } if (t_pwm_green_led->time_delay) schedule_delayed_work(&green_led_delay_work, t_pwm_green_led->time_delay); #if 0 /*int pwm_id = 0;*/ switch (green_status) { case 1: /*MIDIUM_BRIGHT*/ set_pwm0(0, 30); break; case 4: /*DISABLE*/ pwm_disable(pwm0_dev); break; default: pr_err("inval cmd\n"); break; } #endif return size; } static ssize_t set_red_func(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret; ret = kstrtoul(buf, 10, &t_pwm_red_led->red_status); pwm_printf("red_status= %d\n", t_pwm_red_led->red_status); switch (t_pwm_red_led->red_status) { case 0: case 1: /*MIDIUM_BRIGHT*/ case 2: gpio_set_value(t_pwm_red_led->rled_gpio, 1); break; case 4: /*DISABLE*/ gpio_set_value(t_pwm_red_led->rled_gpio, 0); break; default: pr_err("inval cmd\n"); break; } return size; } static ssize_t get_red_func(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", (int)t_pwm_red_led->red_status); } static DEVICE_ATTR(blue_func, 0664, get_blue_func, set_blue_func); static DEVICE_ATTR(green_func, 0664, get_green_func, set_green_func); static DEVICE_ATTR(red_func, 0664, get_red_func, set_red_func); static struct attribute *sunxi_pwmleds_attributes[] = { &dev_attr_blue_func.attr, &dev_attr_green_func.attr, &dev_attr_red_func.attr, NULL }; static struct attribute_group sunxi_pwmleds_attribute_group = { .name = "pwm_leds", .attrs = sunxi_pwmleds_attributes }; static struct miscdevice sunxi_pwmleds_dev = { .minor = MISC_DYNAMIC_MINOR, .name = "misc_dev", }; static void pwm_blue_led_delay_work(struct work_struct *work) { static int level; static int pol; pwm_printf(">>>led_timer_func level= %d, pol=%d+++\n", level, pol); int pwm_id = t_pwm_blue_led->pwm_id; switch (level) { #if 1 case 0: set_pwm(pwm_id, 99); break; case 1: set_pwm(pwm_id, 96); break; case 2: set_pwm(pwm_id, 92); break; case 3: set_pwm(pwm_id, 88); break; case 4: set_pwm(pwm_id, 84); break; case 5: set_pwm(pwm_id, 80); break; case 6: set_pwm(pwm_id, 76); break; case 7: set_pwm(pwm_id, 72); break; case 8: set_pwm(pwm_id, 68); break; case 9: set_pwm(pwm_id, 64); break; case 10: set_pwm(pwm_id, 60); break; case 11: set_pwm(pwm_id, 56); break; case 12: set_pwm(pwm_id, 52); break; case 13: set_pwm(pwm_id, 48); break; case 14: set_pwm(pwm_id, 44); break; case 15: set_pwm(pwm_id, 40); break; case 16: set_pwm(pwm_id, 36); break; case 17: set_pwm(pwm_id, 32); break; case 18: set_pwm(pwm_id, 28); break; case 19: set_pwm(pwm_id, 24); break; case 20: set_pwm(pwm_id, 20); break; case 21: set_pwm(pwm_id, 16); break; case 22: set_pwm(pwm_id, 12); break; case 23: set_pwm(pwm_id, 8); break; case 24: set_pwm(pwm_id, 4); break; #else case 0: set_pwm(pwm_id, 100); break; /* case 1: set_pwm(pwm_id, 96); break;*/ case 2: set_pwm(pwm_id, 92); break; /* case 3: set_pwm(pwm_id, 88); break;*/ case 4: set_pwm(pwm_id, 84); break; /* case 5: set_pwm(pwm_id, 80); break;*/ case 6: set_pwm(pwm_id, 76); break; /* case 7: set_pwm(pwm_id, 72); break;*/ case 8: set_pwm(pwm_id, 68); break; /* case 9: set_pwm(pwm_id, 64); break;*/ case 10: set_pwm(pwm_id, 60); break; /* case 11: set_pwm(pwm_id, 56); break;*/ case 12: set_pwm(pwm_id, 52); break; /* case 13: set_pwm(pwm_id, 48); break;*/ case 14: set_pwm(pwm_id, 44); break; /* case 15: set_pwm(pwm_id, 40); break;*/ case 16: set_pwm(pwm_id, 36); break; /* case 17: set_pwm(pwm_id, 32); break;*/ case 18: set_pwm(pwm_id, 28); break; /* case 19: set_pwm(pwm_id, 24); break;*/ case 20: set_pwm(pwm_id, 20); break; /* case 21: set_pwm(pwm_id, 16); break;*/ case 22: set_pwm(pwm_id, 12); break; /* case 23: set_pwm(pwm_id, 8); break;*/ case 24: set_pwm(pwm_id, 4); break; #endif default: break; } if (pol == 1) { level = level + 1; if (level >= 24) { level = 24; pol = 0; } } else { level = level - 1; if (level <= 0) { level = 0; pol = 1; } } if (level == 24) schedule_delayed_work(&blue_led_delay_work, HZ/1); else if (level == 0) schedule_delayed_work(&blue_led_delay_work, HZ/2); else schedule_delayed_work(&blue_led_delay_work, HZ/(level+4)); } static void pwm_green_led_delay_work(struct work_struct *work) { static int level; static int pol; int pwm_id = t_pwm_green_led->pwm_id; pwm_printf(">>>led_timer_func level= %d, pol=%d+++\n", level, pol); switch (level) { #if 1 case 0: set_pwm(pwm_id, 99); break; case 1: set_pwm(pwm_id, 96); break; case 2: set_pwm(pwm_id, 92); break; case 3: set_pwm(pwm_id, 88); break; case 4: set_pwm(pwm_id, 84); break; case 5: set_pwm(pwm_id, 80); break; case 6: set_pwm(pwm_id, 76); break; case 7: set_pwm(pwm_id, 72); break; case 8: set_pwm(pwm_id, 68); break; case 9: set_pwm(pwm_id, 64); break; case 10: set_pwm(pwm_id, 60); break; case 11: set_pwm(pwm_id, 56); break; case 12: set_pwm(pwm_id, 52); break; case 13: set_pwm(pwm_id, 48); break; case 14: set_pwm(pwm_id, 44); break; case 15: set_pwm(pwm_id, 40); break; case 16: set_pwm(pwm_id, 36); break; case 17: set_pwm(pwm_id, 32); break; case 18: set_pwm(pwm_id, 28); break; case 19: set_pwm(pwm_id, 24); break; case 20: set_pwm(pwm_id, 20); break; case 21: set_pwm(pwm_id, 16); break; case 22: set_pwm(pwm_id, 12); break; case 23: set_pwm(pwm_id, 8); break; case 24: set_pwm(pwm_id, 4); break; #else case 0: set_pwm(pwm_id, 100); break; /* case 1: set_pwm(pwm_id, 96); break;*/ case 2: set_pwm(pwm_id, 92); break; /* case 3: set_pwm(pwm_id, 88); break;*/ case 4: set_pwm(pwm_id, 84); break; /* case 5: set_pwm(pwm_id, 80); break;*/ case 6: set_pwm(pwm_id, 76); break; /* case 7: set_pwm(pwm_id, 72); break;*/ case 8: set_pwm(pwm_id, 68); break; /* case 9: set_pwm(pwm_id, 64); break;*/ case 10: set_pwm(pwm_id, 60); break; /* case 11: set_pwm(pwm_id, 56); break;*/ case 12: set_pwm(pwm_id, 52); break; /* case 13: set_pwm(pwm_id, 48); break;*/ case 14: set_pwm(pwm_id, 44); break; /* case 15: set_pwm(pwm_id, 40); break;*/ case 16: set_pwm(pwm_id, 36); break; /* case 17: set_pwm(pwm_id, 32); break;*/ case 18: set_pwm(pwm_id, 28); break; /* case 19: set_pwm(pwm_id, 24); break;*/ case 20: set_pwm(pwm_id, 20); break; /* case 21: set_pwm(pwm_id, 16); break;*/ case 22: set_pwm(pwm_id, 12); break; /* case 23: set_pwm(pwm_id, 8); break;*/ case 24: set_pwm(pwm_id, 4); break; #endif default: break; } if (pol == 1) { level = level + 1; if (level >= 24) { level = 24; pol = 0; } } else { level = level - 1; if (level <= 0) { level = 0; pol = 1; } } if (level == 24) schedule_delayed_work(&green_led_delay_work, HZ/1); else if (level == 0) schedule_delayed_work(&green_led_delay_work, HZ/2); else schedule_delayed_work(&green_led_delay_work, HZ/(level+4)); } static int pwm_leds_ctrl_init(void) { int ret = 0; int pwm_id = 0; /*pwm0_para used*/ pwm_printf("[pwm_leds]: %s: %d===into\n", __func__, __LINE__); t_pwm_green_led = kzalloc(sizeof(struct pwm_green_led), GFP_KERNEL); t_pwm_blue_led = kzalloc(sizeof(struct pwm_blue_led), GFP_KERNEL); t_pwm_red_led = kzalloc(sizeof(struct pwm_red_led), GFP_KERNEL); if (!t_pwm_green_led || !t_pwm_blue_led || !t_pwm_red_led) { pr_err("[pwm_leds] request memory fail!\n"); return -1; } t_pwm_green_led->pwm0_dev = pwm_request(0, "led_green"); if (t_pwm_green_led->pwm0_dev == NULL || IS_ERR(t_pwm_green_led->pwm0_dev)) { pr_err("[pwm_leds] pwm_request pwm0 fail!\n"); return -1; } t_pwm_green_led->green_status = LED_DEFAULT_STATE; t_pwm_green_led->pwm_id = 0; t_pwm_green_led->time_delay = LED_DEFAULT_DELAY; pwm_printf("[pwm_leds] pwm_request pwm0 success!\n"); t_pwm_blue_led->pwm1_dev = pwm_request(1, "led_blue"); if (t_pwm_blue_led->pwm1_dev == NULL || IS_ERR(t_pwm_blue_led->pwm1_dev)) { pr_err("[pwm_leds] pwm_request pwm1 fail!\n"); return -1; } t_pwm_blue_led->blue_status = LED_DEFAULT_STATE; t_pwm_blue_led->pwm_id = 1; t_pwm_blue_led->time_delay = LED_DEFAULT_DELAY; pwm_printf("[pwm_leds] pwm_request pwm1 success!\n"); t_pwm_red_led->rled_gpio = RLED_NUM; t_pwm_red_led->red_status = LED_DEFAULT_STATE; if (!gpio_is_valid(t_pwm_red_led->rled_gpio)) { pr_err("failed to get gpio\n"); return -EINVAL; } ret = gpio_request(t_pwm_red_led->rled_gpio, "rled_gpio"); if (ret) { pr_err("failed to request rled gpio\n"); return -EINVAL; } gpio_direction_output(t_pwm_red_led->rled_gpio, 1); gpio_set_value(t_pwm_red_led->rled_gpio, 0); mutex_init(&pwm_muter); INIT_DELAYED_WORK(&blue_led_delay_work, pwm_blue_led_delay_work); INIT_DELAYED_WORK(&green_led_delay_work, pwm_green_led_delay_work); return ret; } static int __init pwm_leds_init(void) { int ret, err, pwmleds_used; pwm_printf("pwmleds debug gpio driver init\n"); ret = pwm_leds_ctrl_init(); if (ret == 0) pwm_printf("pwm_leds_init is ok==============\n"); else pwm_printf("pwm_leds_init is err==============\n"); err = misc_register(&sunxi_pwmleds_dev); if (err) { pwm_printf("%s led register as misc device error\n", __func__); goto exit; } err = sysfs_create_group(&sunxi_pwmleds_dev.this_device->kobj, &sunxi_pwmleds_attribute_group); if (err) pwm_printf("%s sysfs_create_group error\n", __func__); return 0; exit: return -1; } static void __exit pwm_leds_exit(void) { pwm_printf("Bye, pwm_leds exit\n"); gpio_set_value(t_pwm_red_led->rled_gpio, 0); gpio_free(t_pwm_red_led->rled_gpio); pwm_disable(t_pwm_green_led->pwm0_dev); pwm_disable(t_pwm_blue_led->pwm1_dev); cancel_delayed_work_sync(&blue_led_delay_work); cancel_delayed_work_sync(&green_led_delay_work); pwm_free(t_pwm_green_led->pwm0_dev); pwm_free(t_pwm_blue_led->pwm1_dev); kfree(t_pwm_green_led); kfree(t_pwm_blue_led); kfree(t_pwm_red_led); sysfs_remove_group(&sunxi_pwmleds_dev.this_device->kobj, &sunxi_pwmleds_attribute_group); misc_deregister(&sunxi_pwmleds_dev); } module_init(pwm_leds_init); module_exit(pwm_leds_exit); MODULE_DESCRIPTION("a simple pwmleds driver"); MODULE_AUTHOR("yanu"); MODULE_LICENSE("GPL");