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> 28*a486e512SChristophe Leroy #include <linux/of.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 3570c3967dSJohannes Berg static void pmu_req_done(struct adb_request * req) 3670c3967dSJohannes Berg { 3770c3967dSJohannes Berg unsigned long flags; 3870c3967dSJohannes Berg 3970c3967dSJohannes Berg spin_lock_irqsave(&pmu_blink_lock, flags); 4070c3967dSJohannes Berg /* if someone requested a change in the meantime 4170c3967dSJohannes Berg * (we only see the last one which is fine) 4270c3967dSJohannes Berg * then apply it now */ 43f596575eSJohannes Berg if (requested_change != -1 && !pmu_sys_suspended) 4470c3967dSJohannes Berg pmu_request(&pmu_blink_req, NULL, 4, 0xee, 4, 0, requested_change); 4570c3967dSJohannes Berg /* reset requested change */ 4670c3967dSJohannes Berg requested_change = -1; 4770c3967dSJohannes Berg spin_unlock_irqrestore(&pmu_blink_lock, flags); 4870c3967dSJohannes Berg } 4970c3967dSJohannes Berg 5070c3967dSJohannes Berg static void pmu_led_set(struct led_classdev *led_cdev, 5170c3967dSJohannes Berg enum led_brightness brightness) 5270c3967dSJohannes Berg { 5370c3967dSJohannes Berg unsigned long flags; 5470c3967dSJohannes Berg 5570c3967dSJohannes Berg spin_lock_irqsave(&pmu_blink_lock, flags); 5670c3967dSJohannes Berg switch (brightness) { 5770c3967dSJohannes Berg case LED_OFF: 5870c3967dSJohannes Berg requested_change = 0; 5970c3967dSJohannes Berg break; 6070c3967dSJohannes Berg case LED_FULL: 6170c3967dSJohannes Berg requested_change = 1; 6270c3967dSJohannes Berg break; 6370c3967dSJohannes Berg default: 6470c3967dSJohannes Berg goto out; 6570c3967dSJohannes Berg break; 6670c3967dSJohannes Berg } 6770c3967dSJohannes Berg /* if request isn't done, then don't do anything */ 68f596575eSJohannes Berg if (pmu_blink_req.complete && !pmu_sys_suspended) 6970c3967dSJohannes Berg pmu_request(&pmu_blink_req, NULL, 4, 0xee, 4, 0, requested_change); 7070c3967dSJohannes Berg out: 7170c3967dSJohannes Berg spin_unlock_irqrestore(&pmu_blink_lock, flags); 7270c3967dSJohannes Berg } 7370c3967dSJohannes Berg 7470c3967dSJohannes Berg static struct led_classdev pmu_led = { 75db3f5207SOlaf Hering .name = "pmu-led::front", 7683e2c70eSStephan Linz #ifdef CONFIG_ADB_PMU_LED_DISK 7783e2c70eSStephan Linz .default_trigger = "disk-activity", 7870c3967dSJohannes Berg #endif 7970c3967dSJohannes Berg .brightness_set = pmu_led_set, 8070c3967dSJohannes Berg }; 8170c3967dSJohannes Berg 8270c3967dSJohannes Berg static int __init via_pmu_led_init(void) 8370c3967dSJohannes Berg { 8470c3967dSJohannes Berg struct device_node *dt; 8570c3967dSJohannes Berg const char *model; 8670c3967dSJohannes Berg 8770c3967dSJohannes Berg /* only do this on keylargo based models */ 8870c3967dSJohannes Berg if (pmu_get_model() != PMU_KEYLARGO_BASED) 8970c3967dSJohannes Berg return -ENODEV; 9070c3967dSJohannes Berg 9170c3967dSJohannes Berg dt = of_find_node_by_path("/"); 9270c3967dSJohannes Berg if (dt == NULL) 9370c3967dSJohannes Berg return -ENODEV; 9401b2726dSStephen Rothwell model = of_get_property(dt, "model", NULL); 951f7aac6eSJulia Lawall if (model == NULL) { 961f7aac6eSJulia Lawall of_node_put(dt); 9770c3967dSJohannes Berg return -ENODEV; 981f7aac6eSJulia Lawall } 9970c3967dSJohannes Berg if (strncmp(model, "PowerBook", strlen("PowerBook")) != 0 && 100e51b85dcSTony Vroon strncmp(model, "iBook", strlen("iBook")) != 0 && 101e51b85dcSTony Vroon strcmp(model, "PowerMac7,2") != 0 && 102e51b85dcSTony Vroon strcmp(model, "PowerMac7,3") != 0) { 10370c3967dSJohannes Berg of_node_put(dt); 10470c3967dSJohannes Berg /* ignore */ 10570c3967dSJohannes Berg return -ENODEV; 10670c3967dSJohannes Berg } 10770c3967dSJohannes Berg of_node_put(dt); 10870c3967dSJohannes Berg 10970c3967dSJohannes Berg spin_lock_init(&pmu_blink_lock); 11070c3967dSJohannes Berg /* no outstanding req */ 11170c3967dSJohannes Berg pmu_blink_req.complete = 1; 11270c3967dSJohannes Berg pmu_blink_req.done = pmu_req_done; 113f596575eSJohannes Berg 11470c3967dSJohannes Berg return led_classdev_register(NULL, &pmu_led); 11570c3967dSJohannes Berg } 11670c3967dSJohannes Berg 11770c3967dSJohannes Berg late_initcall(via_pmu_led_init); 118