14da498fcSLinus Walleij /* 24da498fcSLinus Walleij * This program is free software; you can redistribute it and/or modify 34da498fcSLinus Walleij * it under the terms of the GNU General Public License version 2 as 44da498fcSLinus Walleij * published by the Free Software Foundation. 54da498fcSLinus Walleij * 64da498fcSLinus Walleij * h3xxx atmel micro companion support, notification LED subdevice 74da498fcSLinus Walleij * 84da498fcSLinus Walleij * Author : Linus Walleij <linus.walleij@linaro.org> 94da498fcSLinus Walleij */ 104da498fcSLinus Walleij 114da498fcSLinus Walleij #include <linux/module.h> 124da498fcSLinus Walleij #include <linux/platform_device.h> 134da498fcSLinus Walleij #include <linux/mfd/ipaq-micro.h> 144da498fcSLinus Walleij #include <linux/leds.h> 154da498fcSLinus Walleij 164da498fcSLinus Walleij #define LED_YELLOW 0x00 174da498fcSLinus Walleij #define LED_GREEN 0x01 184da498fcSLinus Walleij 194da498fcSLinus Walleij #define LED_EN (1 << 4) /* LED ON/OFF 0:off, 1:on */ 204da498fcSLinus Walleij #define LED_AUTOSTOP (1 << 5) /* LED ON/OFF auto stop set 0:disable, 1:enable */ 214da498fcSLinus Walleij #define LED_ALWAYS (1 << 6) /* LED Interrupt Mask 0:No mask, 1:mask */ 224da498fcSLinus Walleij 234da498fcSLinus Walleij static void micro_leds_brightness_set(struct led_classdev *led_cdev, 244da498fcSLinus Walleij enum led_brightness value) 254da498fcSLinus Walleij { 264da498fcSLinus Walleij struct ipaq_micro *micro = dev_get_drvdata(led_cdev->dev->parent->parent); 274da498fcSLinus Walleij /* 284da498fcSLinus Walleij * In this message: 294da498fcSLinus Walleij * Byte 0 = LED color: 0 = yellow, 1 = green 304da498fcSLinus Walleij * yellow LED is always ~30 blinks per minute 314da498fcSLinus Walleij * Byte 1 = duration (flags?) appears to be ignored 324da498fcSLinus Walleij * Byte 2 = green ontime in 1/10 sec (deciseconds) 334da498fcSLinus Walleij * 1 = 1/10 second 344da498fcSLinus Walleij * 0 = 256/10 second 354da498fcSLinus Walleij * Byte 3 = green offtime in 1/10 sec (deciseconds) 364da498fcSLinus Walleij * 1 = 1/10 second 374da498fcSLinus Walleij * 0 = 256/10 seconds 384da498fcSLinus Walleij */ 394da498fcSLinus Walleij struct ipaq_micro_msg msg = { 404da498fcSLinus Walleij .id = MSG_NOTIFY_LED, 414da498fcSLinus Walleij .tx_len = 4, 424da498fcSLinus Walleij }; 434da498fcSLinus Walleij 444da498fcSLinus Walleij msg.tx_data[0] = LED_GREEN; 454da498fcSLinus Walleij msg.tx_data[1] = 0; 464da498fcSLinus Walleij if (value) { 474da498fcSLinus Walleij msg.tx_data[2] = 0; /* Duty cycle 256 */ 484da498fcSLinus Walleij msg.tx_data[3] = 1; 494da498fcSLinus Walleij } else { 504da498fcSLinus Walleij msg.tx_data[2] = 1; 514da498fcSLinus Walleij msg.tx_data[3] = 0; /* Duty cycle 256 */ 524da498fcSLinus Walleij } 534da498fcSLinus Walleij ipaq_micro_tx_msg_sync(micro, &msg); 544da498fcSLinus Walleij } 554da498fcSLinus Walleij 564da498fcSLinus Walleij /* Maximum duty cycle in ms 256/10 sec = 25600 ms */ 574da498fcSLinus Walleij #define IPAQ_LED_MAX_DUTY 25600 584da498fcSLinus Walleij 594da498fcSLinus Walleij static int micro_leds_blink_set(struct led_classdev *led_cdev, 604da498fcSLinus Walleij unsigned long *delay_on, 614da498fcSLinus Walleij unsigned long *delay_off) 624da498fcSLinus Walleij { 634da498fcSLinus Walleij struct ipaq_micro *micro = dev_get_drvdata(led_cdev->dev->parent->parent); 644da498fcSLinus Walleij /* 654da498fcSLinus Walleij * In this message: 664da498fcSLinus Walleij * Byte 0 = LED color: 0 = yellow, 1 = green 674da498fcSLinus Walleij * yellow LED is always ~30 blinks per minute 684da498fcSLinus Walleij * Byte 1 = duration (flags?) appears to be ignored 694da498fcSLinus Walleij * Byte 2 = green ontime in 1/10 sec (deciseconds) 704da498fcSLinus Walleij * 1 = 1/10 second 714da498fcSLinus Walleij * 0 = 256/10 second 724da498fcSLinus Walleij * Byte 3 = green offtime in 1/10 sec (deciseconds) 734da498fcSLinus Walleij * 1 = 1/10 second 744da498fcSLinus Walleij * 0 = 256/10 seconds 754da498fcSLinus Walleij */ 764da498fcSLinus Walleij struct ipaq_micro_msg msg = { 774da498fcSLinus Walleij .id = MSG_NOTIFY_LED, 784da498fcSLinus Walleij .tx_len = 4, 794da498fcSLinus Walleij }; 804da498fcSLinus Walleij 814da498fcSLinus Walleij msg.tx_data[0] = LED_GREEN; 824da498fcSLinus Walleij if (*delay_on > IPAQ_LED_MAX_DUTY || 834da498fcSLinus Walleij *delay_off > IPAQ_LED_MAX_DUTY) 844da498fcSLinus Walleij return -EINVAL; 854da498fcSLinus Walleij 864da498fcSLinus Walleij if (*delay_on == 0 && *delay_off == 0) { 874da498fcSLinus Walleij *delay_on = 100; 884da498fcSLinus Walleij *delay_off = 100; 894da498fcSLinus Walleij } 904da498fcSLinus Walleij 914da498fcSLinus Walleij msg.tx_data[1] = 0; 924da498fcSLinus Walleij if (*delay_on >= IPAQ_LED_MAX_DUTY) 934da498fcSLinus Walleij msg.tx_data[2] = 0; 944da498fcSLinus Walleij else 954da498fcSLinus Walleij msg.tx_data[2] = (u8) DIV_ROUND_CLOSEST(*delay_on, 100); 964da498fcSLinus Walleij if (*delay_off >= IPAQ_LED_MAX_DUTY) 974da498fcSLinus Walleij msg.tx_data[3] = 0; 984da498fcSLinus Walleij else 994da498fcSLinus Walleij msg.tx_data[3] = (u8) DIV_ROUND_CLOSEST(*delay_off, 100); 1004da498fcSLinus Walleij return ipaq_micro_tx_msg_sync(micro, &msg); 1014da498fcSLinus Walleij } 1024da498fcSLinus Walleij 1034da498fcSLinus Walleij static struct led_classdev micro_led = { 1044da498fcSLinus Walleij .name = "led-ipaq-micro", 1054da498fcSLinus Walleij .brightness_set = micro_leds_brightness_set, 1064da498fcSLinus Walleij .blink_set = micro_leds_blink_set, 1074da498fcSLinus Walleij .flags = LED_CORE_SUSPENDRESUME, 1084da498fcSLinus Walleij }; 1094da498fcSLinus Walleij 1104da498fcSLinus Walleij static int micro_leds_probe(struct platform_device *pdev) 1114da498fcSLinus Walleij { 1124da498fcSLinus Walleij int ret; 1134da498fcSLinus Walleij 1144da498fcSLinus Walleij ret = led_classdev_register(&pdev->dev, µ_led); 1154da498fcSLinus Walleij if (ret) { 1164da498fcSLinus Walleij dev_err(&pdev->dev, "registering led failed: %d\n", ret); 1174da498fcSLinus Walleij return ret; 1184da498fcSLinus Walleij } 1194da498fcSLinus Walleij dev_info(&pdev->dev, "iPAQ micro notification LED driver\n"); 1204da498fcSLinus Walleij 1214da498fcSLinus Walleij return 0; 1224da498fcSLinus Walleij } 1234da498fcSLinus Walleij 1244da498fcSLinus Walleij static int micro_leds_remove(struct platform_device *pdev) 1254da498fcSLinus Walleij { 1264da498fcSLinus Walleij led_classdev_unregister(µ_led); 1274da498fcSLinus Walleij return 0; 1284da498fcSLinus Walleij } 1294da498fcSLinus Walleij 130*e661c897SWei Yongjun static struct platform_driver micro_leds_device_driver = { 1314da498fcSLinus Walleij .driver = { 1324da498fcSLinus Walleij .name = "ipaq-micro-leds", 1334da498fcSLinus Walleij }, 1344da498fcSLinus Walleij .probe = micro_leds_probe, 1354da498fcSLinus Walleij .remove = micro_leds_remove, 1364da498fcSLinus Walleij }; 1374da498fcSLinus Walleij module_platform_driver(micro_leds_device_driver); 1384da498fcSLinus Walleij 1394da498fcSLinus Walleij MODULE_LICENSE("GPL"); 1404da498fcSLinus Walleij MODULE_DESCRIPTION("driver for iPAQ Atmel micro leds"); 1414da498fcSLinus Walleij MODULE_ALIAS("platform:ipaq-micro-leds"); 142