/* * LED Driver for the Freecom FSG-3 * * Copyright (c) 2008 Rod Whitby <rod@whitby.id.au> * * Author: Rod Whitby <rod@whitby.id.au> * * Based on leds-spitz.c * Copyright 2005-2006 Openedhand Ltd. * Author: Richard Purdie <rpurdie@openedhand.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> #include <asm/arch/hardware.h> #include <asm/io.h> static short __iomem *latch_address; static unsigned short latch_value; static void fsg_led_wlan_set(struct led_classdev *led_cdev, enum led_brightness value) { if (value) { latch_value &= ~(1 << FSG_LED_WLAN_BIT); *latch_address = latch_value; } else { latch_value |= (1 << FSG_LED_WLAN_BIT); *latch_address = latch_value; } } static void fsg_led_wan_set(struct led_classdev *led_cdev, enum led_brightness value) { if (value) { latch_value &= ~(1 << FSG_LED_WAN_BIT); *latch_address = latch_value; } else { latch_value |= (1 << FSG_LED_WAN_BIT); *latch_address = latch_value; } } static void fsg_led_sata_set(struct led_classdev *led_cdev, enum led_brightness value) { if (value) { latch_value &= ~(1 << FSG_LED_SATA_BIT); *latch_address = latch_value; } else { latch_value |= (1 << FSG_LED_SATA_BIT); *latch_address = latch_value; } } static void fsg_led_usb_set(struct led_classdev *led_cdev, enum led_brightness value) { if (value) { latch_value &= ~(1 << FSG_LED_USB_BIT); *latch_address = latch_value; } else { latch_value |= (1 << FSG_LED_USB_BIT); *latch_address = latch_value; } } static void fsg_led_sync_set(struct led_classdev *led_cdev, enum led_brightness value) { if (value) { latch_value &= ~(1 << FSG_LED_SYNC_BIT); *latch_address = latch_value; } else { latch_value |= (1 << FSG_LED_SYNC_BIT); *latch_address = latch_value; } } static void fsg_led_ring_set(struct led_classdev *led_cdev, enum led_brightness value) { if (value) { latch_value &= ~(1 << FSG_LED_RING_BIT); *latch_address = latch_value; } else { latch_value |= (1 << FSG_LED_RING_BIT); *latch_address = latch_value; } } static struct led_classdev fsg_wlan_led = { .name = "fsg:blue:wlan", .brightness_set = fsg_led_wlan_set, }; static struct led_classdev fsg_wan_led = { .name = "fsg:blue:wan", .brightness_set = fsg_led_wan_set, }; static struct led_classdev fsg_sata_led = { .name = "fsg:blue:sata", .brightness_set = fsg_led_sata_set, }; static struct led_classdev fsg_usb_led = { .name = "fsg:blue:usb", .brightness_set = fsg_led_usb_set, }; static struct led_classdev fsg_sync_led = { .name = "fsg:blue:sync", .brightness_set = fsg_led_sync_set, }; static struct led_classdev fsg_ring_led = { .name = "fsg:blue:ring", .brightness_set = fsg_led_ring_set, }; #ifdef CONFIG_PM static int fsg_led_suspend(struct platform_device *dev, pm_message_t state) { led_classdev_suspend(&fsg_wlan_led); led_classdev_suspend(&fsg_wan_led); led_classdev_suspend(&fsg_sata_led); led_classdev_suspend(&fsg_usb_led); led_classdev_suspend(&fsg_sync_led); led_classdev_suspend(&fsg_ring_led); return 0; } static int fsg_led_resume(struct platform_device *dev) { led_classdev_resume(&fsg_wlan_led); led_classdev_resume(&fsg_wan_led); led_classdev_resume(&fsg_sata_led); led_classdev_resume(&fsg_usb_led); led_classdev_resume(&fsg_sync_led); led_classdev_resume(&fsg_ring_led); return 0; } #endif static int fsg_led_probe(struct platform_device *pdev) { int ret; ret = led_classdev_register(&pdev->dev, &fsg_wlan_led); if (ret < 0) goto failwlan; ret = led_classdev_register(&pdev->dev, &fsg_wan_led); if (ret < 0) goto failwan; ret = led_classdev_register(&pdev->dev, &fsg_sata_led); if (ret < 0) goto failsata; ret = led_classdev_register(&pdev->dev, &fsg_usb_led); if (ret < 0) goto failusb; ret = led_classdev_register(&pdev->dev, &fsg_sync_led); if (ret < 0) goto failsync; ret = led_classdev_register(&pdev->dev, &fsg_ring_led); if (ret < 0) goto failring; /* Map the LED chip select address space */ latch_address = (unsigned short *) ioremap(IXP4XX_EXP_BUS_BASE(2), 512); if (!latch_address) { ret = -ENOMEM; goto failremap; } latch_value = 0xffff; *latch_address = latch_value; return ret; failremap: led_classdev_unregister(&fsg_ring_led); failring: led_classdev_unregister(&fsg_sync_led); failsync: led_classdev_unregister(&fsg_usb_led); failusb: led_classdev_unregister(&fsg_sata_led); failsata: led_classdev_unregister(&fsg_wan_led); failwan: led_classdev_unregister(&fsg_wlan_led); failwlan: return ret; } static int fsg_led_remove(struct platform_device *pdev) { iounmap(latch_address); led_classdev_unregister(&fsg_wlan_led); led_classdev_unregister(&fsg_wan_led); led_classdev_unregister(&fsg_sata_led); led_classdev_unregister(&fsg_usb_led); led_classdev_unregister(&fsg_sync_led); led_classdev_unregister(&fsg_ring_led); return 0; } static struct platform_driver fsg_led_driver = { .probe = fsg_led_probe, .remove = fsg_led_remove, #ifdef CONFIG_PM .suspend = fsg_led_suspend, .resume = fsg_led_resume, #endif .driver = { .name = "fsg-led", }, }; static int __init fsg_led_init(void) { return platform_driver_register(&fsg_led_driver); } static void __exit fsg_led_exit(void) { platform_driver_unregister(&fsg_led_driver); } module_init(fsg_led_init); module_exit(fsg_led_exit); MODULE_AUTHOR("Rod Whitby <rod@whitby.id.au>"); MODULE_DESCRIPTION("Freecom FSG-3 LED driver"); MODULE_LICENSE("GPL");