1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later 2*4882a593Smuzhiyun /* 3*4882a593Smuzhiyun * APM emulation for PMU-based machines 4*4882a593Smuzhiyun * 5*4882a593Smuzhiyun * Copyright 2001 Benjamin Herrenschmidt (benh@kernel.crashing.org) 6*4882a593Smuzhiyun */ 7*4882a593Smuzhiyun 8*4882a593Smuzhiyun #include <linux/kernel.h> 9*4882a593Smuzhiyun #include <linux/module.h> 10*4882a593Smuzhiyun #include <linux/apm-emulation.h> 11*4882a593Smuzhiyun #include <linux/adb.h> 12*4882a593Smuzhiyun #include <linux/pmu.h> 13*4882a593Smuzhiyun 14*4882a593Smuzhiyun #define APM_CRITICAL 10 15*4882a593Smuzhiyun #define APM_LOW 30 16*4882a593Smuzhiyun pmu_apm_get_power_status(struct apm_power_info * info)17*4882a593Smuzhiyunstatic void pmu_apm_get_power_status(struct apm_power_info *info) 18*4882a593Smuzhiyun { 19*4882a593Smuzhiyun int percentage = -1; 20*4882a593Smuzhiyun int batteries = 0; 21*4882a593Smuzhiyun int time_units = -1; 22*4882a593Smuzhiyun int real_count = 0; 23*4882a593Smuzhiyun int i; 24*4882a593Smuzhiyun char charging = 0; 25*4882a593Smuzhiyun long charge = -1; 26*4882a593Smuzhiyun long amperage = 0; 27*4882a593Smuzhiyun unsigned long btype = 0; 28*4882a593Smuzhiyun 29*4882a593Smuzhiyun info->battery_status = APM_BATTERY_STATUS_UNKNOWN; 30*4882a593Smuzhiyun info->battery_flag = APM_BATTERY_FLAG_UNKNOWN; 31*4882a593Smuzhiyun info->units = APM_UNITS_MINS; 32*4882a593Smuzhiyun 33*4882a593Smuzhiyun if (pmu_power_flags & PMU_PWR_AC_PRESENT) 34*4882a593Smuzhiyun info->ac_line_status = APM_AC_ONLINE; 35*4882a593Smuzhiyun else 36*4882a593Smuzhiyun info->ac_line_status = APM_AC_OFFLINE; 37*4882a593Smuzhiyun 38*4882a593Smuzhiyun for (i=0; i<pmu_battery_count; i++) { 39*4882a593Smuzhiyun if (pmu_batteries[i].flags & PMU_BATT_PRESENT) { 40*4882a593Smuzhiyun batteries++; 41*4882a593Smuzhiyun if (percentage < 0) 42*4882a593Smuzhiyun percentage = 0; 43*4882a593Smuzhiyun if (charge < 0) 44*4882a593Smuzhiyun charge = 0; 45*4882a593Smuzhiyun percentage += (pmu_batteries[i].charge * 100) / 46*4882a593Smuzhiyun pmu_batteries[i].max_charge; 47*4882a593Smuzhiyun charge += pmu_batteries[i].charge; 48*4882a593Smuzhiyun amperage += pmu_batteries[i].amperage; 49*4882a593Smuzhiyun if (btype == 0) 50*4882a593Smuzhiyun btype = (pmu_batteries[i].flags & PMU_BATT_TYPE_MASK); 51*4882a593Smuzhiyun real_count++; 52*4882a593Smuzhiyun if ((pmu_batteries[i].flags & PMU_BATT_CHARGING)) 53*4882a593Smuzhiyun charging++; 54*4882a593Smuzhiyun } 55*4882a593Smuzhiyun } 56*4882a593Smuzhiyun if (batteries == 0) 57*4882a593Smuzhiyun info->ac_line_status = APM_AC_ONLINE; 58*4882a593Smuzhiyun 59*4882a593Smuzhiyun if (real_count) { 60*4882a593Smuzhiyun if (amperage < 0) { 61*4882a593Smuzhiyun if (btype == PMU_BATT_TYPE_SMART) 62*4882a593Smuzhiyun time_units = (charge * 59) / (amperage * -1); 63*4882a593Smuzhiyun else 64*4882a593Smuzhiyun time_units = (charge * 16440) / (amperage * -60); 65*4882a593Smuzhiyun } 66*4882a593Smuzhiyun percentage /= real_count; 67*4882a593Smuzhiyun if (charging > 0) { 68*4882a593Smuzhiyun info->battery_status = APM_BATTERY_STATUS_CHARGING; 69*4882a593Smuzhiyun info->battery_flag = APM_BATTERY_FLAG_CHARGING; 70*4882a593Smuzhiyun } else if (percentage <= APM_CRITICAL) { 71*4882a593Smuzhiyun info->battery_status = APM_BATTERY_STATUS_CRITICAL; 72*4882a593Smuzhiyun info->battery_flag = APM_BATTERY_FLAG_CRITICAL; 73*4882a593Smuzhiyun } else if (percentage <= APM_LOW) { 74*4882a593Smuzhiyun info->battery_status = APM_BATTERY_STATUS_LOW; 75*4882a593Smuzhiyun info->battery_flag = APM_BATTERY_FLAG_LOW; 76*4882a593Smuzhiyun } else { 77*4882a593Smuzhiyun info->battery_status = APM_BATTERY_STATUS_HIGH; 78*4882a593Smuzhiyun info->battery_flag = APM_BATTERY_FLAG_HIGH; 79*4882a593Smuzhiyun } 80*4882a593Smuzhiyun } 81*4882a593Smuzhiyun 82*4882a593Smuzhiyun info->battery_life = percentage; 83*4882a593Smuzhiyun info->time = time_units; 84*4882a593Smuzhiyun } 85*4882a593Smuzhiyun apm_emu_init(void)86*4882a593Smuzhiyunstatic int __init apm_emu_init(void) 87*4882a593Smuzhiyun { 88*4882a593Smuzhiyun apm_get_power_status = pmu_apm_get_power_status; 89*4882a593Smuzhiyun 90*4882a593Smuzhiyun printk(KERN_INFO "apm_emu: PMU APM Emulation initialized.\n"); 91*4882a593Smuzhiyun 92*4882a593Smuzhiyun return 0; 93*4882a593Smuzhiyun } 94*4882a593Smuzhiyun apm_emu_exit(void)95*4882a593Smuzhiyunstatic void __exit apm_emu_exit(void) 96*4882a593Smuzhiyun { 97*4882a593Smuzhiyun if (apm_get_power_status == pmu_apm_get_power_status) 98*4882a593Smuzhiyun apm_get_power_status = NULL; 99*4882a593Smuzhiyun 100*4882a593Smuzhiyun printk(KERN_INFO "apm_emu: PMU APM Emulation removed.\n"); 101*4882a593Smuzhiyun } 102*4882a593Smuzhiyun 103*4882a593Smuzhiyun module_init(apm_emu_init); 104*4882a593Smuzhiyun module_exit(apm_emu_exit); 105*4882a593Smuzhiyun 106*4882a593Smuzhiyun MODULE_AUTHOR("Benjamin Herrenschmidt"); 107*4882a593Smuzhiyun MODULE_DESCRIPTION("APM emulation for PowerMac"); 108*4882a593Smuzhiyun MODULE_LICENSE("GPL"); 109