1/*
2 * LED Driver for the Freecom FSG-3
3 *
4 * Copyright (c) 2008 Rod Whitby <rod@whitby.id.au>
5 *
6 * Author: Rod Whitby <rod@whitby.id.au>
7 *
8 * Based on leds-spitz.c
9 * Copyright 2005-2006 Openedhand Ltd.
10 * Author: Richard Purdie <rpurdie@openedhand.com>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License version 2 as
14 * published by the Free Software Foundation.
15 *
16 */
17
18#include <linux/kernel.h>
19#include <linux/platform_device.h>
20#include <linux/leds.h>
21#include <linux/module.h>
22#include <linux/io.h>
23#include <mach/hardware.h>
24
25#define FSG_LED_WLAN_BIT	0
26#define FSG_LED_WAN_BIT		1
27#define FSG_LED_SATA_BIT	2
28#define FSG_LED_USB_BIT		4
29#define FSG_LED_RING_BIT	5
30#define FSG_LED_SYNC_BIT	7
31
32static short __iomem *latch_address;
33static unsigned short latch_value;
34
35
36static void fsg_led_wlan_set(struct led_classdev *led_cdev,
37			     enum led_brightness value)
38{
39	if (value) {
40		latch_value &= ~(1 << FSG_LED_WLAN_BIT);
41		*latch_address = latch_value;
42	} else {
43		latch_value |=  (1 << FSG_LED_WLAN_BIT);
44		*latch_address = latch_value;
45	}
46}
47
48static void fsg_led_wan_set(struct led_classdev *led_cdev,
49			    enum led_brightness value)
50{
51	if (value) {
52		latch_value &= ~(1 << FSG_LED_WAN_BIT);
53		*latch_address = latch_value;
54	} else {
55		latch_value |=  (1 << FSG_LED_WAN_BIT);
56		*latch_address = latch_value;
57	}
58}
59
60static void fsg_led_sata_set(struct led_classdev *led_cdev,
61			     enum led_brightness value)
62{
63	if (value) {
64		latch_value &= ~(1 << FSG_LED_SATA_BIT);
65		*latch_address = latch_value;
66	} else {
67		latch_value |=  (1 << FSG_LED_SATA_BIT);
68		*latch_address = latch_value;
69	}
70}
71
72static void fsg_led_usb_set(struct led_classdev *led_cdev,
73			    enum led_brightness value)
74{
75	if (value) {
76		latch_value &= ~(1 << FSG_LED_USB_BIT);
77		*latch_address = latch_value;
78	} else {
79		latch_value |=  (1 << FSG_LED_USB_BIT);
80		*latch_address = latch_value;
81	}
82}
83
84static void fsg_led_sync_set(struct led_classdev *led_cdev,
85			     enum led_brightness value)
86{
87	if (value) {
88		latch_value &= ~(1 << FSG_LED_SYNC_BIT);
89		*latch_address = latch_value;
90	} else {
91		latch_value |=  (1 << FSG_LED_SYNC_BIT);
92		*latch_address = latch_value;
93	}
94}
95
96static void fsg_led_ring_set(struct led_classdev *led_cdev,
97			     enum led_brightness value)
98{
99	if (value) {
100		latch_value &= ~(1 << FSG_LED_RING_BIT);
101		*latch_address = latch_value;
102	} else {
103		latch_value |=  (1 << FSG_LED_RING_BIT);
104		*latch_address = latch_value;
105	}
106}
107
108
109static struct led_classdev fsg_wlan_led = {
110	.name			= "fsg:blue:wlan",
111	.brightness_set		= fsg_led_wlan_set,
112	.flags			= LED_CORE_SUSPENDRESUME,
113};
114
115static struct led_classdev fsg_wan_led = {
116	.name			= "fsg:blue:wan",
117	.brightness_set		= fsg_led_wan_set,
118	.flags			= LED_CORE_SUSPENDRESUME,
119};
120
121static struct led_classdev fsg_sata_led = {
122	.name			= "fsg:blue:sata",
123	.brightness_set		= fsg_led_sata_set,
124	.flags			= LED_CORE_SUSPENDRESUME,
125};
126
127static struct led_classdev fsg_usb_led = {
128	.name			= "fsg:blue:usb",
129	.brightness_set		= fsg_led_usb_set,
130	.flags			= LED_CORE_SUSPENDRESUME,
131};
132
133static struct led_classdev fsg_sync_led = {
134	.name			= "fsg:blue:sync",
135	.brightness_set		= fsg_led_sync_set,
136	.flags			= LED_CORE_SUSPENDRESUME,
137};
138
139static struct led_classdev fsg_ring_led = {
140	.name			= "fsg:blue:ring",
141	.brightness_set		= fsg_led_ring_set,
142	.flags			= LED_CORE_SUSPENDRESUME,
143};
144
145
146static int fsg_led_probe(struct platform_device *pdev)
147{
148	int ret;
149
150	/* Map the LED chip select address space */
151	latch_address = (unsigned short *) devm_ioremap(&pdev->dev,
152						IXP4XX_EXP_BUS_BASE(2), 512);
153	if (!latch_address)
154		return -ENOMEM;
155
156	latch_value = 0xffff;
157	*latch_address = latch_value;
158
159	ret = led_classdev_register(&pdev->dev, &fsg_wlan_led);
160	if (ret < 0)
161		goto failwlan;
162
163	ret = led_classdev_register(&pdev->dev, &fsg_wan_led);
164	if (ret < 0)
165		goto failwan;
166
167	ret = led_classdev_register(&pdev->dev, &fsg_sata_led);
168	if (ret < 0)
169		goto failsata;
170
171	ret = led_classdev_register(&pdev->dev, &fsg_usb_led);
172	if (ret < 0)
173		goto failusb;
174
175	ret = led_classdev_register(&pdev->dev, &fsg_sync_led);
176	if (ret < 0)
177		goto failsync;
178
179	ret = led_classdev_register(&pdev->dev, &fsg_ring_led);
180	if (ret < 0)
181		goto failring;
182
183	return ret;
184
185 failring:
186	led_classdev_unregister(&fsg_sync_led);
187 failsync:
188	led_classdev_unregister(&fsg_usb_led);
189 failusb:
190	led_classdev_unregister(&fsg_sata_led);
191 failsata:
192	led_classdev_unregister(&fsg_wan_led);
193 failwan:
194	led_classdev_unregister(&fsg_wlan_led);
195 failwlan:
196
197	return ret;
198}
199
200static int fsg_led_remove(struct platform_device *pdev)
201{
202	led_classdev_unregister(&fsg_wlan_led);
203	led_classdev_unregister(&fsg_wan_led);
204	led_classdev_unregister(&fsg_sata_led);
205	led_classdev_unregister(&fsg_usb_led);
206	led_classdev_unregister(&fsg_sync_led);
207	led_classdev_unregister(&fsg_ring_led);
208
209	return 0;
210}
211
212
213static struct platform_driver fsg_led_driver = {
214	.probe		= fsg_led_probe,
215	.remove		= fsg_led_remove,
216	.driver		= {
217		.name		= "fsg-led",
218	},
219};
220
221module_platform_driver(fsg_led_driver);
222
223MODULE_AUTHOR("Rod Whitby <rod@whitby.id.au>");
224MODULE_DESCRIPTION("Freecom FSG-3 LED driver");
225MODULE_LICENSE("GPL");
226