1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * via-pmu LED class device
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * This program is free software; you can redistribute it and/or modify
7*4882a593Smuzhiyun * it under the terms of the GNU General Public License as published by
8*4882a593Smuzhiyun * the Free Software Foundation; either version 2 of the License, or
9*4882a593Smuzhiyun * (at your option) any later version.
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * This program is distributed in the hope that it will be useful, but
12*4882a593Smuzhiyun * WITHOUT ANY WARRANTY; without even the implied warranty of
13*4882a593Smuzhiyun * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
14*4882a593Smuzhiyun * NON INFRINGEMENT. See the GNU General Public License for more
15*4882a593Smuzhiyun * details.
16*4882a593Smuzhiyun *
17*4882a593Smuzhiyun * You should have received a copy of the GNU General Public License
18*4882a593Smuzhiyun * along with this program; if not, write to the Free Software
19*4882a593Smuzhiyun * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20*4882a593Smuzhiyun *
21*4882a593Smuzhiyun */
22*4882a593Smuzhiyun #include <linux/types.h>
23*4882a593Smuzhiyun #include <linux/kernel.h>
24*4882a593Smuzhiyun #include <linux/device.h>
25*4882a593Smuzhiyun #include <linux/leds.h>
26*4882a593Smuzhiyun #include <linux/adb.h>
27*4882a593Smuzhiyun #include <linux/pmu.h>
28*4882a593Smuzhiyun #include <asm/prom.h>
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun static spinlock_t pmu_blink_lock;
31*4882a593Smuzhiyun static struct adb_request pmu_blink_req;
32*4882a593Smuzhiyun /* -1: no change, 0: request off, 1: request on */
33*4882a593Smuzhiyun static int requested_change;
34*4882a593Smuzhiyun
pmu_req_done(struct adb_request * req)35*4882a593Smuzhiyun static void pmu_req_done(struct adb_request * req)
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun unsigned long flags;
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun spin_lock_irqsave(&pmu_blink_lock, flags);
40*4882a593Smuzhiyun /* if someone requested a change in the meantime
41*4882a593Smuzhiyun * (we only see the last one which is fine)
42*4882a593Smuzhiyun * then apply it now */
43*4882a593Smuzhiyun if (requested_change != -1 && !pmu_sys_suspended)
44*4882a593Smuzhiyun pmu_request(&pmu_blink_req, NULL, 4, 0xee, 4, 0, requested_change);
45*4882a593Smuzhiyun /* reset requested change */
46*4882a593Smuzhiyun requested_change = -1;
47*4882a593Smuzhiyun spin_unlock_irqrestore(&pmu_blink_lock, flags);
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun
pmu_led_set(struct led_classdev * led_cdev,enum led_brightness brightness)50*4882a593Smuzhiyun static void pmu_led_set(struct led_classdev *led_cdev,
51*4882a593Smuzhiyun enum led_brightness brightness)
52*4882a593Smuzhiyun {
53*4882a593Smuzhiyun unsigned long flags;
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun spin_lock_irqsave(&pmu_blink_lock, flags);
56*4882a593Smuzhiyun switch (brightness) {
57*4882a593Smuzhiyun case LED_OFF:
58*4882a593Smuzhiyun requested_change = 0;
59*4882a593Smuzhiyun break;
60*4882a593Smuzhiyun case LED_FULL:
61*4882a593Smuzhiyun requested_change = 1;
62*4882a593Smuzhiyun break;
63*4882a593Smuzhiyun default:
64*4882a593Smuzhiyun goto out;
65*4882a593Smuzhiyun break;
66*4882a593Smuzhiyun }
67*4882a593Smuzhiyun /* if request isn't done, then don't do anything */
68*4882a593Smuzhiyun if (pmu_blink_req.complete && !pmu_sys_suspended)
69*4882a593Smuzhiyun pmu_request(&pmu_blink_req, NULL, 4, 0xee, 4, 0, requested_change);
70*4882a593Smuzhiyun out:
71*4882a593Smuzhiyun spin_unlock_irqrestore(&pmu_blink_lock, flags);
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun static struct led_classdev pmu_led = {
75*4882a593Smuzhiyun .name = "pmu-led::front",
76*4882a593Smuzhiyun #ifdef CONFIG_ADB_PMU_LED_DISK
77*4882a593Smuzhiyun .default_trigger = "disk-activity",
78*4882a593Smuzhiyun #endif
79*4882a593Smuzhiyun .brightness_set = pmu_led_set,
80*4882a593Smuzhiyun };
81*4882a593Smuzhiyun
via_pmu_led_init(void)82*4882a593Smuzhiyun static int __init via_pmu_led_init(void)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun struct device_node *dt;
85*4882a593Smuzhiyun const char *model;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun /* only do this on keylargo based models */
88*4882a593Smuzhiyun if (pmu_get_model() != PMU_KEYLARGO_BASED)
89*4882a593Smuzhiyun return -ENODEV;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun dt = of_find_node_by_path("/");
92*4882a593Smuzhiyun if (dt == NULL)
93*4882a593Smuzhiyun return -ENODEV;
94*4882a593Smuzhiyun model = of_get_property(dt, "model", NULL);
95*4882a593Smuzhiyun if (model == NULL) {
96*4882a593Smuzhiyun of_node_put(dt);
97*4882a593Smuzhiyun return -ENODEV;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun if (strncmp(model, "PowerBook", strlen("PowerBook")) != 0 &&
100*4882a593Smuzhiyun strncmp(model, "iBook", strlen("iBook")) != 0 &&
101*4882a593Smuzhiyun strcmp(model, "PowerMac7,2") != 0 &&
102*4882a593Smuzhiyun strcmp(model, "PowerMac7,3") != 0) {
103*4882a593Smuzhiyun of_node_put(dt);
104*4882a593Smuzhiyun /* ignore */
105*4882a593Smuzhiyun return -ENODEV;
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun of_node_put(dt);
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun spin_lock_init(&pmu_blink_lock);
110*4882a593Smuzhiyun /* no outstanding req */
111*4882a593Smuzhiyun pmu_blink_req.complete = 1;
112*4882a593Smuzhiyun pmu_blink_req.done = pmu_req_done;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun return led_classdev_register(NULL, &pmu_led);
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun late_initcall(via_pmu_led_init);
118