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