162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * APM emulation for PMU-based machines 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2001 Benjamin Herrenschmidt (benh@kernel.crashing.org) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/apm-emulation.h> 1162306a36Sopenharmony_ci#include <linux/adb.h> 1262306a36Sopenharmony_ci#include <linux/pmu.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define APM_CRITICAL 10 1562306a36Sopenharmony_ci#define APM_LOW 30 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic void pmu_apm_get_power_status(struct apm_power_info *info) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci int percentage = -1; 2062306a36Sopenharmony_ci int batteries = 0; 2162306a36Sopenharmony_ci int time_units = -1; 2262306a36Sopenharmony_ci int real_count = 0; 2362306a36Sopenharmony_ci int i; 2462306a36Sopenharmony_ci char charging = 0; 2562306a36Sopenharmony_ci long charge = -1; 2662306a36Sopenharmony_ci long amperage = 0; 2762306a36Sopenharmony_ci unsigned long btype = 0; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci info->battery_status = APM_BATTERY_STATUS_UNKNOWN; 3062306a36Sopenharmony_ci info->battery_flag = APM_BATTERY_FLAG_UNKNOWN; 3162306a36Sopenharmony_ci info->units = APM_UNITS_MINS; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (pmu_power_flags & PMU_PWR_AC_PRESENT) 3462306a36Sopenharmony_ci info->ac_line_status = APM_AC_ONLINE; 3562306a36Sopenharmony_ci else 3662306a36Sopenharmony_ci info->ac_line_status = APM_AC_OFFLINE; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci for (i=0; i<pmu_battery_count; i++) { 3962306a36Sopenharmony_ci if (pmu_batteries[i].flags & PMU_BATT_PRESENT) { 4062306a36Sopenharmony_ci batteries++; 4162306a36Sopenharmony_ci if (percentage < 0) 4262306a36Sopenharmony_ci percentage = 0; 4362306a36Sopenharmony_ci if (charge < 0) 4462306a36Sopenharmony_ci charge = 0; 4562306a36Sopenharmony_ci percentage += (pmu_batteries[i].charge * 100) / 4662306a36Sopenharmony_ci pmu_batteries[i].max_charge; 4762306a36Sopenharmony_ci charge += pmu_batteries[i].charge; 4862306a36Sopenharmony_ci amperage += pmu_batteries[i].amperage; 4962306a36Sopenharmony_ci if (btype == 0) 5062306a36Sopenharmony_ci btype = (pmu_batteries[i].flags & PMU_BATT_TYPE_MASK); 5162306a36Sopenharmony_ci real_count++; 5262306a36Sopenharmony_ci if ((pmu_batteries[i].flags & PMU_BATT_CHARGING)) 5362306a36Sopenharmony_ci charging++; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci if (batteries == 0) 5762306a36Sopenharmony_ci info->ac_line_status = APM_AC_ONLINE; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (real_count) { 6062306a36Sopenharmony_ci if (amperage < 0) { 6162306a36Sopenharmony_ci if (btype == PMU_BATT_TYPE_SMART) 6262306a36Sopenharmony_ci time_units = (charge * 59) / (amperage * -1); 6362306a36Sopenharmony_ci else 6462306a36Sopenharmony_ci time_units = (charge * 16440) / (amperage * -60); 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci percentage /= real_count; 6762306a36Sopenharmony_ci if (charging > 0) { 6862306a36Sopenharmony_ci info->battery_status = APM_BATTERY_STATUS_CHARGING; 6962306a36Sopenharmony_ci info->battery_flag = APM_BATTERY_FLAG_CHARGING; 7062306a36Sopenharmony_ci } else if (percentage <= APM_CRITICAL) { 7162306a36Sopenharmony_ci info->battery_status = APM_BATTERY_STATUS_CRITICAL; 7262306a36Sopenharmony_ci info->battery_flag = APM_BATTERY_FLAG_CRITICAL; 7362306a36Sopenharmony_ci } else if (percentage <= APM_LOW) { 7462306a36Sopenharmony_ci info->battery_status = APM_BATTERY_STATUS_LOW; 7562306a36Sopenharmony_ci info->battery_flag = APM_BATTERY_FLAG_LOW; 7662306a36Sopenharmony_ci } else { 7762306a36Sopenharmony_ci info->battery_status = APM_BATTERY_STATUS_HIGH; 7862306a36Sopenharmony_ci info->battery_flag = APM_BATTERY_FLAG_HIGH; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci info->battery_life = percentage; 8362306a36Sopenharmony_ci info->time = time_units; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int __init apm_emu_init(void) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci apm_get_power_status = pmu_apm_get_power_status; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci printk(KERN_INFO "apm_emu: PMU APM Emulation initialized.\n"); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void __exit apm_emu_exit(void) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci if (apm_get_power_status == pmu_apm_get_power_status) 9862306a36Sopenharmony_ci apm_get_power_status = NULL; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci printk(KERN_INFO "apm_emu: PMU APM Emulation removed.\n"); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cimodule_init(apm_emu_init); 10462306a36Sopenharmony_cimodule_exit(apm_emu_exit); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ciMODULE_AUTHOR("Benjamin Herrenschmidt"); 10762306a36Sopenharmony_ciMODULE_DESCRIPTION("APM emulation for PowerMac"); 10862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 109