170c3967dSJohannes Berg /* 270c3967dSJohannes Berg * via-pmu LED class device 370c3967dSJohannes Berg * 470c3967dSJohannes Berg * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> 570c3967dSJohannes Berg * 670c3967dSJohannes Berg * This program is free software; you can redistribute it and/or modify 770c3967dSJohannes Berg * it under the terms of the GNU General Public License as published by 870c3967dSJohannes Berg * the Free Software Foundation; either version 2 of the License, or 970c3967dSJohannes Berg * (at your option) any later version. 1070c3967dSJohannes Berg * 1170c3967dSJohannes Berg * This program is distributed in the hope that it will be useful, but 1270c3967dSJohannes Berg * WITHOUT ANY WARRANTY; without even the implied warranty of 1370c3967dSJohannes Berg * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 1470c3967dSJohannes Berg * NON INFRINGEMENT. See the GNU General Public License for more 1570c3967dSJohannes Berg * details. 1670c3967dSJohannes Berg * 1770c3967dSJohannes Berg * You should have received a copy of the GNU General Public License 1870c3967dSJohannes Berg * along with this program; if not, write to the Free Software 1970c3967dSJohannes Berg * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 2070c3967dSJohannes Berg * 2170c3967dSJohannes Berg */ 2270c3967dSJohannes Berg #include <linux/types.h> 2370c3967dSJohannes Berg #include <linux/kernel.h> 2470c3967dSJohannes Berg #include <linux/device.h> 2570c3967dSJohannes Berg #include <linux/leds.h> 2670c3967dSJohannes Berg #include <linux/adb.h> 2770c3967dSJohannes Berg #include <linux/pmu.h> 2870c3967dSJohannes Berg #include <asm/prom.h> 2970c3967dSJohannes Berg 3070c3967dSJohannes Berg static spinlock_t pmu_blink_lock; 3170c3967dSJohannes Berg static struct adb_request pmu_blink_req; 3270c3967dSJohannes Berg /* -1: no change, 0: request off, 1: request on */ 3370c3967dSJohannes Berg static int requested_change; 3470c3967dSJohannes Berg static int sleeping; 3570c3967dSJohannes Berg 3670c3967dSJohannes Berg static void pmu_req_done(struct adb_request * req) 3770c3967dSJohannes Berg { 3870c3967dSJohannes Berg unsigned long flags; 3970c3967dSJohannes Berg 4070c3967dSJohannes Berg spin_lock_irqsave(&pmu_blink_lock, flags); 4170c3967dSJohannes Berg /* if someone requested a change in the meantime 4270c3967dSJohannes Berg * (we only see the last one which is fine) 4370c3967dSJohannes Berg * then apply it now */ 4470c3967dSJohannes Berg if (requested_change != -1 && !sleeping) 4570c3967dSJohannes Berg pmu_request(&pmu_blink_req, NULL, 4, 0xee, 4, 0, requested_change); 4670c3967dSJohannes Berg /* reset requested change */ 4770c3967dSJohannes Berg requested_change = -1; 4870c3967dSJohannes Berg spin_unlock_irqrestore(&pmu_blink_lock, flags); 4970c3967dSJohannes Berg } 5070c3967dSJohannes Berg 5170c3967dSJohannes Berg static void pmu_led_set(struct led_classdev *led_cdev, 5270c3967dSJohannes Berg enum led_brightness brightness) 5370c3967dSJohannes Berg { 5470c3967dSJohannes Berg unsigned long flags; 5570c3967dSJohannes Berg 5670c3967dSJohannes Berg spin_lock_irqsave(&pmu_blink_lock, flags); 5770c3967dSJohannes Berg switch (brightness) { 5870c3967dSJohannes Berg case LED_OFF: 5970c3967dSJohannes Berg requested_change = 0; 6070c3967dSJohannes Berg break; 6170c3967dSJohannes Berg case LED_FULL: 6270c3967dSJohannes Berg requested_change = 1; 6370c3967dSJohannes Berg break; 6470c3967dSJohannes Berg default: 6570c3967dSJohannes Berg goto out; 6670c3967dSJohannes Berg break; 6770c3967dSJohannes Berg } 6870c3967dSJohannes Berg /* if request isn't done, then don't do anything */ 6970c3967dSJohannes Berg if (pmu_blink_req.complete && !sleeping) 7070c3967dSJohannes Berg pmu_request(&pmu_blink_req, NULL, 4, 0xee, 4, 0, requested_change); 7170c3967dSJohannes Berg out: 7270c3967dSJohannes Berg spin_unlock_irqrestore(&pmu_blink_lock, flags); 7370c3967dSJohannes Berg } 7470c3967dSJohannes Berg 7570c3967dSJohannes Berg static struct led_classdev pmu_led = { 7670c3967dSJohannes Berg .name = "pmu-front-led", 773a09aa47SJohannes Berg #ifdef CONFIG_ADB_PMU_LED_IDE 7870c3967dSJohannes Berg .default_trigger = "ide-disk", 7970c3967dSJohannes Berg #endif 8070c3967dSJohannes Berg .brightness_set = pmu_led_set, 8170c3967dSJohannes Berg }; 8270c3967dSJohannes Berg 8370c3967dSJohannes Berg #ifdef CONFIG_PM 8470b52b38SJohannes Berg static void pmu_led_sleep_call(struct pmu_sleep_notifier *self, int when) 8570c3967dSJohannes Berg { 8670c3967dSJohannes Berg unsigned long flags; 8770c3967dSJohannes Berg 8870c3967dSJohannes Berg spin_lock_irqsave(&pmu_blink_lock, flags); 8970c3967dSJohannes Berg 9070c3967dSJohannes Berg switch (when) { 9170c3967dSJohannes Berg case PBOOK_SLEEP_REQUEST: 9270c3967dSJohannes Berg sleeping = 1; 9370c3967dSJohannes Berg break; 9470c3967dSJohannes Berg case PBOOK_WAKE: 9570c3967dSJohannes Berg sleeping = 0; 9670c3967dSJohannes Berg break; 9770c3967dSJohannes Berg default: 9870c3967dSJohannes Berg /* do nothing */ 9970c3967dSJohannes Berg break; 10070c3967dSJohannes Berg } 10170c3967dSJohannes Berg spin_unlock_irqrestore(&pmu_blink_lock, flags); 10270c3967dSJohannes Berg } 10370c3967dSJohannes Berg 10470c3967dSJohannes Berg static struct pmu_sleep_notifier via_pmu_led_sleep_notif = { 10570c3967dSJohannes Berg .notifier_call = pmu_led_sleep_call, 10670c3967dSJohannes Berg }; 10770c3967dSJohannes Berg #endif 10870c3967dSJohannes Berg 10970c3967dSJohannes Berg static int __init via_pmu_led_init(void) 11070c3967dSJohannes Berg { 11170c3967dSJohannes Berg struct device_node *dt; 11270c3967dSJohannes Berg const char *model; 11370c3967dSJohannes Berg 11470c3967dSJohannes Berg /* only do this on keylargo based models */ 11570c3967dSJohannes Berg if (pmu_get_model() != PMU_KEYLARGO_BASED) 11670c3967dSJohannes Berg return -ENODEV; 11770c3967dSJohannes Berg 11870c3967dSJohannes Berg dt = of_find_node_by_path("/"); 11970c3967dSJohannes Berg if (dt == NULL) 12070c3967dSJohannes Berg return -ENODEV; 121*01b2726dSStephen Rothwell model = of_get_property(dt, "model", NULL); 12270c3967dSJohannes Berg if (model == NULL) 12370c3967dSJohannes Berg return -ENODEV; 12470c3967dSJohannes Berg if (strncmp(model, "PowerBook", strlen("PowerBook")) != 0 && 125e51b85dcSTony Vroon strncmp(model, "iBook", strlen("iBook")) != 0 && 126e51b85dcSTony Vroon strcmp(model, "PowerMac7,2") != 0 && 127e51b85dcSTony Vroon strcmp(model, "PowerMac7,3") != 0) { 12870c3967dSJohannes Berg of_node_put(dt); 12970c3967dSJohannes Berg /* ignore */ 13070c3967dSJohannes Berg return -ENODEV; 13170c3967dSJohannes Berg } 13270c3967dSJohannes Berg of_node_put(dt); 13370c3967dSJohannes Berg 13470c3967dSJohannes Berg spin_lock_init(&pmu_blink_lock); 13570c3967dSJohannes Berg /* no outstanding req */ 13670c3967dSJohannes Berg pmu_blink_req.complete = 1; 13770c3967dSJohannes Berg pmu_blink_req.done = pmu_req_done; 13870c3967dSJohannes Berg #ifdef CONFIG_PM 13970c3967dSJohannes Berg pmu_register_sleep_notifier(&via_pmu_led_sleep_notif); 14070c3967dSJohannes Berg #endif 14170c3967dSJohannes Berg return led_classdev_register(NULL, &pmu_led); 14270c3967dSJohannes Berg } 14370c3967dSJohannes Berg 14470c3967dSJohannes Berg late_initcall(via_pmu_led_init); 145