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