1*3e0a4e85SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2b3028878SJohannes Berg /* 3b3028878SJohannes Berg * APM emulation for PMU-based machines 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Copyright 2001 Benjamin Herrenschmidt (benh@kernel.crashing.org) 61da177e4SLinus Torvalds */ 71da177e4SLinus Torvalds 81da177e4SLinus Torvalds #include <linux/kernel.h> 9b3028878SJohannes Berg #include <linux/module.h> 10b3028878SJohannes Berg #include <linux/apm-emulation.h> 111da177e4SLinus Torvalds #include <linux/adb.h> 121da177e4SLinus Torvalds #include <linux/pmu.h> 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds #define APM_CRITICAL 10 151da177e4SLinus Torvalds #define APM_LOW 30 161da177e4SLinus Torvalds pmu_apm_get_power_status(struct apm_power_info * info)17b3028878SJohannes Bergstatic void pmu_apm_get_power_status(struct apm_power_info *info) 181da177e4SLinus Torvalds { 191da177e4SLinus Torvalds int percentage = -1; 20b3028878SJohannes Berg int batteries = 0; 211da177e4SLinus Torvalds int time_units = -1; 221da177e4SLinus Torvalds int real_count = 0; 231da177e4SLinus Torvalds int i; 241da177e4SLinus Torvalds char charging = 0; 251da177e4SLinus Torvalds long charge = -1; 261da177e4SLinus Torvalds long amperage = 0; 271da177e4SLinus Torvalds unsigned long btype = 0; 281da177e4SLinus Torvalds 29b3028878SJohannes Berg info->battery_status = APM_BATTERY_STATUS_UNKNOWN; 30b3028878SJohannes Berg info->battery_flag = APM_BATTERY_FLAG_UNKNOWN; 31b3028878SJohannes Berg info->units = APM_UNITS_MINS; 32b3028878SJohannes Berg 33b3028878SJohannes Berg if (pmu_power_flags & PMU_PWR_AC_PRESENT) 34b3028878SJohannes Berg info->ac_line_status = APM_AC_ONLINE; 35b3028878SJohannes Berg else 36b3028878SJohannes Berg info->ac_line_status = APM_AC_OFFLINE; 37b3028878SJohannes Berg 381da177e4SLinus Torvalds for (i=0; i<pmu_battery_count; i++) { 391da177e4SLinus Torvalds if (pmu_batteries[i].flags & PMU_BATT_PRESENT) { 40b3028878SJohannes Berg batteries++; 411da177e4SLinus Torvalds if (percentage < 0) 421da177e4SLinus Torvalds percentage = 0; 431da177e4SLinus Torvalds if (charge < 0) 441da177e4SLinus Torvalds charge = 0; 451da177e4SLinus Torvalds percentage += (pmu_batteries[i].charge * 100) / 461da177e4SLinus Torvalds pmu_batteries[i].max_charge; 471da177e4SLinus Torvalds charge += pmu_batteries[i].charge; 481da177e4SLinus Torvalds amperage += pmu_batteries[i].amperage; 491da177e4SLinus Torvalds if (btype == 0) 501da177e4SLinus Torvalds btype = (pmu_batteries[i].flags & PMU_BATT_TYPE_MASK); 511da177e4SLinus Torvalds real_count++; 521da177e4SLinus Torvalds if ((pmu_batteries[i].flags & PMU_BATT_CHARGING)) 531da177e4SLinus Torvalds charging++; 541da177e4SLinus Torvalds } 551da177e4SLinus Torvalds } 56b3028878SJohannes Berg if (batteries == 0) 57b3028878SJohannes Berg info->ac_line_status = APM_AC_ONLINE; 58b3028878SJohannes Berg 591da177e4SLinus Torvalds if (real_count) { 601da177e4SLinus Torvalds if (amperage < 0) { 611da177e4SLinus Torvalds if (btype == PMU_BATT_TYPE_SMART) 621da177e4SLinus Torvalds time_units = (charge * 59) / (amperage * -1); 631da177e4SLinus Torvalds else 641da177e4SLinus Torvalds time_units = (charge * 16440) / (amperage * -60); 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds percentage /= real_count; 671da177e4SLinus Torvalds if (charging > 0) { 68b3028878SJohannes Berg info->battery_status = APM_BATTERY_STATUS_CHARGING; 69b3028878SJohannes Berg info->battery_flag = APM_BATTERY_FLAG_CHARGING; 701da177e4SLinus Torvalds } else if (percentage <= APM_CRITICAL) { 71b3028878SJohannes Berg info->battery_status = APM_BATTERY_STATUS_CRITICAL; 72b3028878SJohannes Berg info->battery_flag = APM_BATTERY_FLAG_CRITICAL; 731da177e4SLinus Torvalds } else if (percentage <= APM_LOW) { 74b3028878SJohannes Berg info->battery_status = APM_BATTERY_STATUS_LOW; 75b3028878SJohannes Berg info->battery_flag = APM_BATTERY_FLAG_LOW; 761da177e4SLinus Torvalds } else { 77b3028878SJohannes Berg info->battery_status = APM_BATTERY_STATUS_HIGH; 78b3028878SJohannes Berg info->battery_flag = APM_BATTERY_FLAG_HIGH; 791da177e4SLinus Torvalds } 801da177e4SLinus Torvalds } 811da177e4SLinus Torvalds 82b3028878SJohannes Berg info->battery_life = percentage; 83b3028878SJohannes Berg info->time = time_units; 841da177e4SLinus Torvalds } 851da177e4SLinus Torvalds apm_emu_init(void)861da177e4SLinus Torvaldsstatic int __init apm_emu_init(void) 871da177e4SLinus Torvalds { 88b3028878SJohannes Berg apm_get_power_status = pmu_apm_get_power_status; 891da177e4SLinus Torvalds 90b3028878SJohannes Berg printk(KERN_INFO "apm_emu: PMU APM Emulation initialized.\n"); 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds return 0; 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds apm_emu_exit(void)951da177e4SLinus Torvaldsstatic void __exit apm_emu_exit(void) 961da177e4SLinus Torvalds { 97b3028878SJohannes Berg if (apm_get_power_status == pmu_apm_get_power_status) 98b3028878SJohannes Berg apm_get_power_status = NULL; 991da177e4SLinus Torvalds 100b3028878SJohannes Berg printk(KERN_INFO "apm_emu: PMU APM Emulation removed.\n"); 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds module_init(apm_emu_init); 1041da177e4SLinus Torvalds module_exit(apm_emu_exit); 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds MODULE_AUTHOR("Benjamin Herrenschmidt"); 107b3028878SJohannes Berg MODULE_DESCRIPTION("APM emulation for PowerMac"); 1081da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 109