162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * via-pmu LED class device 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 762306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 862306a36Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or 962306a36Sopenharmony_ci * (at your option) any later version. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 1262306a36Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 1362306a36Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 1462306a36Sopenharmony_ci * NON INFRINGEMENT. See the GNU General Public License for more 1562306a36Sopenharmony_ci * details. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License 1862306a36Sopenharmony_ci * along with this program; if not, write to the Free Software 1962306a36Sopenharmony_ci * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci#include <linux/types.h> 2362306a36Sopenharmony_ci#include <linux/kernel.h> 2462306a36Sopenharmony_ci#include <linux/device.h> 2562306a36Sopenharmony_ci#include <linux/leds.h> 2662306a36Sopenharmony_ci#include <linux/adb.h> 2762306a36Sopenharmony_ci#include <linux/pmu.h> 2862306a36Sopenharmony_ci#include <linux/of.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic spinlock_t pmu_blink_lock; 3162306a36Sopenharmony_cistatic struct adb_request pmu_blink_req; 3262306a36Sopenharmony_ci/* -1: no change, 0: request off, 1: request on */ 3362306a36Sopenharmony_cistatic int requested_change; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic void pmu_req_done(struct adb_request * req) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci unsigned long flags; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci spin_lock_irqsave(&pmu_blink_lock, flags); 4062306a36Sopenharmony_ci /* if someone requested a change in the meantime 4162306a36Sopenharmony_ci * (we only see the last one which is fine) 4262306a36Sopenharmony_ci * then apply it now */ 4362306a36Sopenharmony_ci if (requested_change != -1 && !pmu_sys_suspended) 4462306a36Sopenharmony_ci pmu_request(&pmu_blink_req, NULL, 4, 0xee, 4, 0, requested_change); 4562306a36Sopenharmony_ci /* reset requested change */ 4662306a36Sopenharmony_ci requested_change = -1; 4762306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu_blink_lock, flags); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void pmu_led_set(struct led_classdev *led_cdev, 5162306a36Sopenharmony_ci enum led_brightness brightness) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci unsigned long flags; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci spin_lock_irqsave(&pmu_blink_lock, flags); 5662306a36Sopenharmony_ci switch (brightness) { 5762306a36Sopenharmony_ci case LED_OFF: 5862306a36Sopenharmony_ci requested_change = 0; 5962306a36Sopenharmony_ci break; 6062306a36Sopenharmony_ci case LED_FULL: 6162306a36Sopenharmony_ci requested_change = 1; 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci default: 6462306a36Sopenharmony_ci goto out; 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci /* if request isn't done, then don't do anything */ 6862306a36Sopenharmony_ci if (pmu_blink_req.complete && !pmu_sys_suspended) 6962306a36Sopenharmony_ci pmu_request(&pmu_blink_req, NULL, 4, 0xee, 4, 0, requested_change); 7062306a36Sopenharmony_ci out: 7162306a36Sopenharmony_ci spin_unlock_irqrestore(&pmu_blink_lock, flags); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic struct led_classdev pmu_led = { 7562306a36Sopenharmony_ci .name = "pmu-led::front", 7662306a36Sopenharmony_ci#ifdef CONFIG_ADB_PMU_LED_DISK 7762306a36Sopenharmony_ci .default_trigger = "disk-activity", 7862306a36Sopenharmony_ci#endif 7962306a36Sopenharmony_ci .brightness_set = pmu_led_set, 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int __init via_pmu_led_init(void) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct device_node *dt; 8562306a36Sopenharmony_ci const char *model; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* only do this on keylargo based models */ 8862306a36Sopenharmony_ci if (pmu_get_model() != PMU_KEYLARGO_BASED) 8962306a36Sopenharmony_ci return -ENODEV; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci dt = of_find_node_by_path("/"); 9262306a36Sopenharmony_ci if (dt == NULL) 9362306a36Sopenharmony_ci return -ENODEV; 9462306a36Sopenharmony_ci model = of_get_property(dt, "model", NULL); 9562306a36Sopenharmony_ci if (model == NULL) { 9662306a36Sopenharmony_ci of_node_put(dt); 9762306a36Sopenharmony_ci return -ENODEV; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci if (strncmp(model, "PowerBook", strlen("PowerBook")) != 0 && 10062306a36Sopenharmony_ci strncmp(model, "iBook", strlen("iBook")) != 0 && 10162306a36Sopenharmony_ci strcmp(model, "PowerMac7,2") != 0 && 10262306a36Sopenharmony_ci strcmp(model, "PowerMac7,3") != 0) { 10362306a36Sopenharmony_ci of_node_put(dt); 10462306a36Sopenharmony_ci /* ignore */ 10562306a36Sopenharmony_ci return -ENODEV; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci of_node_put(dt); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci spin_lock_init(&pmu_blink_lock); 11062306a36Sopenharmony_ci /* no outstanding req */ 11162306a36Sopenharmony_ci pmu_blink_req.complete = 1; 11262306a36Sopenharmony_ci pmu_blink_req.done = pmu_req_done; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return led_classdev_register(NULL, &pmu_led); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cilate_initcall(via_pmu_led_init); 118