1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * bios-less APM driver for hp680 |
4 | * |
5 | * Copyright 2005 (c) Andriy Skulysh <askulysh@gmail.com> |
6 | * Copyright 2008 (c) Kristoffer Ericson <kristoffer.ericson@gmail.com> |
7 | */ |
8 | #include <linux/module.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/init.h> |
11 | #include <linux/interrupt.h> |
12 | #include <linux/apm-emulation.h> |
13 | #include <linux/io.h> |
14 | #include <asm/adc.h> |
15 | #include <mach/hp6xx.h> |
16 | |
17 | /* percentage values */ |
18 | #define APM_CRITICAL 10 |
19 | #define APM_LOW 30 |
20 | |
21 | /* resonably sane values */ |
22 | #define HP680_BATTERY_MAX 898 |
23 | #define HP680_BATTERY_MIN 486 |
24 | #define HP680_BATTERY_AC_ON 1023 |
25 | |
26 | #define MODNAME "hp6x0_apm" |
27 | |
28 | #define PGDR 0xa400012c |
29 | |
30 | static void hp6x0_apm_get_power_status(struct apm_power_info *info) |
31 | { |
32 | int battery, backup, charging, percentage; |
33 | u8 pgdr; |
34 | |
35 | battery = adc_single(ADC_CHANNEL_BATTERY); |
36 | backup = adc_single(ADC_CHANNEL_BACKUP); |
37 | charging = adc_single(ADC_CHANNEL_CHARGE); |
38 | |
39 | percentage = 100 * (battery - HP680_BATTERY_MIN) / |
40 | (HP680_BATTERY_MAX - HP680_BATTERY_MIN); |
41 | |
42 | /* % of full battery */ |
43 | info->battery_life = percentage; |
44 | |
45 | /* We want our estimates in minutes */ |
46 | info->units = 0; |
47 | |
48 | /* Extremely(!!) rough estimate, we will replace this with a datalist later on */ |
49 | info->time = (2 * battery); |
50 | |
51 | info->ac_line_status = (battery > HP680_BATTERY_AC_ON) ? |
52 | APM_AC_ONLINE : APM_AC_OFFLINE; |
53 | |
54 | pgdr = __raw_readb(PGDR); |
55 | if (pgdr & PGDR_MAIN_BATTERY_OUT) { |
56 | info->battery_status = APM_BATTERY_STATUS_NOT_PRESENT; |
57 | info->battery_flag = 0x80; |
58 | } else if (charging < 8) { |
59 | info->battery_status = APM_BATTERY_STATUS_CHARGING; |
60 | info->battery_flag = 0x08; |
61 | info->ac_line_status = 0x01; |
62 | } else if (percentage <= APM_CRITICAL) { |
63 | info->battery_status = APM_BATTERY_STATUS_CRITICAL; |
64 | info->battery_flag = 0x04; |
65 | } else if (percentage <= APM_LOW) { |
66 | info->battery_status = APM_BATTERY_STATUS_LOW; |
67 | info->battery_flag = 0x02; |
68 | } else { |
69 | info->battery_status = APM_BATTERY_STATUS_HIGH; |
70 | info->battery_flag = 0x01; |
71 | } |
72 | } |
73 | |
74 | static irqreturn_t hp6x0_apm_interrupt(int irq, void *dev) |
75 | { |
76 | if (!APM_DISABLED) |
77 | apm_queue_event(APM_USER_SUSPEND); |
78 | |
79 | return IRQ_HANDLED; |
80 | } |
81 | |
82 | static int __init hp6x0_apm_init(void) |
83 | { |
84 | int ret; |
85 | |
86 | ret = request_irq(irq: HP680_BTN_IRQ, handler: hp6x0_apm_interrupt, |
87 | flags: 0, MODNAME, NULL); |
88 | if (unlikely(ret < 0)) { |
89 | printk(KERN_ERR MODNAME ": IRQ %d request failed\n" , |
90 | HP680_BTN_IRQ); |
91 | return ret; |
92 | } |
93 | |
94 | apm_get_power_status = hp6x0_apm_get_power_status; |
95 | |
96 | return ret; |
97 | } |
98 | |
99 | static void __exit hp6x0_apm_exit(void) |
100 | { |
101 | free_irq(HP680_BTN_IRQ, 0); |
102 | } |
103 | |
104 | module_init(hp6x0_apm_init); |
105 | module_exit(hp6x0_apm_exit); |
106 | |
107 | MODULE_AUTHOR("Adriy Skulysh" ); |
108 | MODULE_DESCRIPTION("hp6xx Advanced Power Management" ); |
109 | MODULE_LICENSE("GPL" ); |
110 | |