1*4da498fcSLinus Walleij /* 2*4da498fcSLinus Walleij * This program is free software; you can redistribute it and/or modify 3*4da498fcSLinus Walleij * it under the terms of the GNU General Public License version 2 as 4*4da498fcSLinus Walleij * published by the Free Software Foundation. 5*4da498fcSLinus Walleij * 6*4da498fcSLinus Walleij * h3xxx atmel micro companion support, notification LED subdevice 7*4da498fcSLinus Walleij * 8*4da498fcSLinus Walleij * Author : Linus Walleij <linus.walleij@linaro.org> 9*4da498fcSLinus Walleij */ 10*4da498fcSLinus Walleij 11*4da498fcSLinus Walleij #include <linux/module.h> 12*4da498fcSLinus Walleij #include <linux/platform_device.h> 13*4da498fcSLinus Walleij #include <linux/mfd/ipaq-micro.h> 14*4da498fcSLinus Walleij #include <linux/leds.h> 15*4da498fcSLinus Walleij 16*4da498fcSLinus Walleij #define LED_YELLOW 0x00 17*4da498fcSLinus Walleij #define LED_GREEN 0x01 18*4da498fcSLinus Walleij 19*4da498fcSLinus Walleij #define LED_EN (1 << 4) /* LED ON/OFF 0:off, 1:on */ 20*4da498fcSLinus Walleij #define LED_AUTOSTOP (1 << 5) /* LED ON/OFF auto stop set 0:disable, 1:enable */ 21*4da498fcSLinus Walleij #define LED_ALWAYS (1 << 6) /* LED Interrupt Mask 0:No mask, 1:mask */ 22*4da498fcSLinus Walleij 23*4da498fcSLinus Walleij static void micro_leds_brightness_set(struct led_classdev *led_cdev, 24*4da498fcSLinus Walleij enum led_brightness value) 25*4da498fcSLinus Walleij { 26*4da498fcSLinus Walleij struct ipaq_micro *micro = dev_get_drvdata(led_cdev->dev->parent->parent); 27*4da498fcSLinus Walleij /* 28*4da498fcSLinus Walleij * In this message: 29*4da498fcSLinus Walleij * Byte 0 = LED color: 0 = yellow, 1 = green 30*4da498fcSLinus Walleij * yellow LED is always ~30 blinks per minute 31*4da498fcSLinus Walleij * Byte 1 = duration (flags?) appears to be ignored 32*4da498fcSLinus Walleij * Byte 2 = green ontime in 1/10 sec (deciseconds) 33*4da498fcSLinus Walleij * 1 = 1/10 second 34*4da498fcSLinus Walleij * 0 = 256/10 second 35*4da498fcSLinus Walleij * Byte 3 = green offtime in 1/10 sec (deciseconds) 36*4da498fcSLinus Walleij * 1 = 1/10 second 37*4da498fcSLinus Walleij * 0 = 256/10 seconds 38*4da498fcSLinus Walleij */ 39*4da498fcSLinus Walleij struct ipaq_micro_msg msg = { 40*4da498fcSLinus Walleij .id = MSG_NOTIFY_LED, 41*4da498fcSLinus Walleij .tx_len = 4, 42*4da498fcSLinus Walleij }; 43*4da498fcSLinus Walleij 44*4da498fcSLinus Walleij msg.tx_data[0] = LED_GREEN; 45*4da498fcSLinus Walleij msg.tx_data[1] = 0; 46*4da498fcSLinus Walleij if (value) { 47*4da498fcSLinus Walleij msg.tx_data[2] = 0; /* Duty cycle 256 */ 48*4da498fcSLinus Walleij msg.tx_data[3] = 1; 49*4da498fcSLinus Walleij } else { 50*4da498fcSLinus Walleij msg.tx_data[2] = 1; 51*4da498fcSLinus Walleij msg.tx_data[3] = 0; /* Duty cycle 256 */ 52*4da498fcSLinus Walleij } 53*4da498fcSLinus Walleij ipaq_micro_tx_msg_sync(micro, &msg); 54*4da498fcSLinus Walleij } 55*4da498fcSLinus Walleij 56*4da498fcSLinus Walleij /* Maximum duty cycle in ms 256/10 sec = 25600 ms */ 57*4da498fcSLinus Walleij #define IPAQ_LED_MAX_DUTY 25600 58*4da498fcSLinus Walleij 59*4da498fcSLinus Walleij static int micro_leds_blink_set(struct led_classdev *led_cdev, 60*4da498fcSLinus Walleij unsigned long *delay_on, 61*4da498fcSLinus Walleij unsigned long *delay_off) 62*4da498fcSLinus Walleij { 63*4da498fcSLinus Walleij struct ipaq_micro *micro = dev_get_drvdata(led_cdev->dev->parent->parent); 64*4da498fcSLinus Walleij /* 65*4da498fcSLinus Walleij * In this message: 66*4da498fcSLinus Walleij * Byte 0 = LED color: 0 = yellow, 1 = green 67*4da498fcSLinus Walleij * yellow LED is always ~30 blinks per minute 68*4da498fcSLinus Walleij * Byte 1 = duration (flags?) appears to be ignored 69*4da498fcSLinus Walleij * Byte 2 = green ontime in 1/10 sec (deciseconds) 70*4da498fcSLinus Walleij * 1 = 1/10 second 71*4da498fcSLinus Walleij * 0 = 256/10 second 72*4da498fcSLinus Walleij * Byte 3 = green offtime in 1/10 sec (deciseconds) 73*4da498fcSLinus Walleij * 1 = 1/10 second 74*4da498fcSLinus Walleij * 0 = 256/10 seconds 75*4da498fcSLinus Walleij */ 76*4da498fcSLinus Walleij struct ipaq_micro_msg msg = { 77*4da498fcSLinus Walleij .id = MSG_NOTIFY_LED, 78*4da498fcSLinus Walleij .tx_len = 4, 79*4da498fcSLinus Walleij }; 80*4da498fcSLinus Walleij 81*4da498fcSLinus Walleij msg.tx_data[0] = LED_GREEN; 82*4da498fcSLinus Walleij if (*delay_on > IPAQ_LED_MAX_DUTY || 83*4da498fcSLinus Walleij *delay_off > IPAQ_LED_MAX_DUTY) 84*4da498fcSLinus Walleij return -EINVAL; 85*4da498fcSLinus Walleij 86*4da498fcSLinus Walleij if (*delay_on == 0 && *delay_off == 0) { 87*4da498fcSLinus Walleij *delay_on = 100; 88*4da498fcSLinus Walleij *delay_off = 100; 89*4da498fcSLinus Walleij } 90*4da498fcSLinus Walleij 91*4da498fcSLinus Walleij msg.tx_data[1] = 0; 92*4da498fcSLinus Walleij if (*delay_on >= IPAQ_LED_MAX_DUTY) 93*4da498fcSLinus Walleij msg.tx_data[2] = 0; 94*4da498fcSLinus Walleij else 95*4da498fcSLinus Walleij msg.tx_data[2] = (u8) DIV_ROUND_CLOSEST(*delay_on, 100); 96*4da498fcSLinus Walleij if (*delay_off >= IPAQ_LED_MAX_DUTY) 97*4da498fcSLinus Walleij msg.tx_data[3] = 0; 98*4da498fcSLinus Walleij else 99*4da498fcSLinus Walleij msg.tx_data[3] = (u8) DIV_ROUND_CLOSEST(*delay_off, 100); 100*4da498fcSLinus Walleij return ipaq_micro_tx_msg_sync(micro, &msg); 101*4da498fcSLinus Walleij } 102*4da498fcSLinus Walleij 103*4da498fcSLinus Walleij static struct led_classdev micro_led = { 104*4da498fcSLinus Walleij .name = "led-ipaq-micro", 105*4da498fcSLinus Walleij .brightness_set = micro_leds_brightness_set, 106*4da498fcSLinus Walleij .blink_set = micro_leds_blink_set, 107*4da498fcSLinus Walleij .flags = LED_CORE_SUSPENDRESUME, 108*4da498fcSLinus Walleij }; 109*4da498fcSLinus Walleij 110*4da498fcSLinus Walleij static int micro_leds_probe(struct platform_device *pdev) 111*4da498fcSLinus Walleij { 112*4da498fcSLinus Walleij int ret; 113*4da498fcSLinus Walleij 114*4da498fcSLinus Walleij ret = led_classdev_register(&pdev->dev, µ_led); 115*4da498fcSLinus Walleij if (ret) { 116*4da498fcSLinus Walleij dev_err(&pdev->dev, "registering led failed: %d\n", ret); 117*4da498fcSLinus Walleij return ret; 118*4da498fcSLinus Walleij } 119*4da498fcSLinus Walleij dev_info(&pdev->dev, "iPAQ micro notification LED driver\n"); 120*4da498fcSLinus Walleij 121*4da498fcSLinus Walleij return 0; 122*4da498fcSLinus Walleij } 123*4da498fcSLinus Walleij 124*4da498fcSLinus Walleij static int micro_leds_remove(struct platform_device *pdev) 125*4da498fcSLinus Walleij { 126*4da498fcSLinus Walleij led_classdev_unregister(µ_led); 127*4da498fcSLinus Walleij return 0; 128*4da498fcSLinus Walleij } 129*4da498fcSLinus Walleij 130*4da498fcSLinus Walleij struct platform_driver micro_leds_device_driver = { 131*4da498fcSLinus Walleij .driver = { 132*4da498fcSLinus Walleij .name = "ipaq-micro-leds", 133*4da498fcSLinus Walleij }, 134*4da498fcSLinus Walleij .probe = micro_leds_probe, 135*4da498fcSLinus Walleij .remove = micro_leds_remove, 136*4da498fcSLinus Walleij }; 137*4da498fcSLinus Walleij module_platform_driver(micro_leds_device_driver); 138*4da498fcSLinus Walleij 139*4da498fcSLinus Walleij MODULE_LICENSE("GPL"); 140*4da498fcSLinus Walleij MODULE_DESCRIPTION("driver for iPAQ Atmel micro leds"); 141*4da498fcSLinus Walleij MODULE_ALIAS("platform:ipaq-micro-leds"); 142