162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * bios-less APM driver for hp680
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2005 (c) Andriy Skulysh <askulysh@gmail.com>
662306a36Sopenharmony_ci * Copyright 2008 (c) Kristoffer Ericson <kristoffer.ericson@gmail.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/interrupt.h>
1262306a36Sopenharmony_ci#include <linux/apm-emulation.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <asm/adc.h>
1562306a36Sopenharmony_ci#include <mach/hp6xx.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* percentage values */
1862306a36Sopenharmony_ci#define APM_CRITICAL			10
1962306a36Sopenharmony_ci#define APM_LOW				30
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* resonably sane values */
2262306a36Sopenharmony_ci#define HP680_BATTERY_MAX		898
2362306a36Sopenharmony_ci#define HP680_BATTERY_MIN		486
2462306a36Sopenharmony_ci#define HP680_BATTERY_AC_ON		1023
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define MODNAME "hp6x0_apm"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define PGDR	0xa400012c
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic void hp6x0_apm_get_power_status(struct apm_power_info *info)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	int battery, backup, charging, percentage;
3362306a36Sopenharmony_ci	u8 pgdr;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	battery		= adc_single(ADC_CHANNEL_BATTERY);
3662306a36Sopenharmony_ci	backup		= adc_single(ADC_CHANNEL_BACKUP);
3762306a36Sopenharmony_ci	charging	= adc_single(ADC_CHANNEL_CHARGE);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	percentage = 100 * (battery - HP680_BATTERY_MIN) /
4062306a36Sopenharmony_ci			   (HP680_BATTERY_MAX - HP680_BATTERY_MIN);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/* % of full battery */
4362306a36Sopenharmony_ci	info->battery_life = percentage;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/* We want our estimates in minutes */
4662306a36Sopenharmony_ci	info->units = 0;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/* Extremely(!!) rough estimate, we will replace this with a datalist later on */
4962306a36Sopenharmony_ci	info->time = (2 * battery);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	info->ac_line_status = (battery > HP680_BATTERY_AC_ON) ?
5262306a36Sopenharmony_ci			 APM_AC_ONLINE : APM_AC_OFFLINE;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	pgdr = __raw_readb(PGDR);
5562306a36Sopenharmony_ci	if (pgdr & PGDR_MAIN_BATTERY_OUT) {
5662306a36Sopenharmony_ci		info->battery_status	= APM_BATTERY_STATUS_NOT_PRESENT;
5762306a36Sopenharmony_ci		info->battery_flag	= 0x80;
5862306a36Sopenharmony_ci	} else if (charging < 8) {
5962306a36Sopenharmony_ci		info->battery_status	= APM_BATTERY_STATUS_CHARGING;
6062306a36Sopenharmony_ci		info->battery_flag	= 0x08;
6162306a36Sopenharmony_ci		info->ac_line_status	= 0x01;
6262306a36Sopenharmony_ci	} else if (percentage <= APM_CRITICAL) {
6362306a36Sopenharmony_ci		info->battery_status	= APM_BATTERY_STATUS_CRITICAL;
6462306a36Sopenharmony_ci		info->battery_flag	= 0x04;
6562306a36Sopenharmony_ci	} else if (percentage <= APM_LOW) {
6662306a36Sopenharmony_ci		info->battery_status	= APM_BATTERY_STATUS_LOW;
6762306a36Sopenharmony_ci		info->battery_flag	= 0x02;
6862306a36Sopenharmony_ci	} else {
6962306a36Sopenharmony_ci		info->battery_status	= APM_BATTERY_STATUS_HIGH;
7062306a36Sopenharmony_ci		info->battery_flag	= 0x01;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic irqreturn_t hp6x0_apm_interrupt(int irq, void *dev)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	if (!APM_DISABLED)
7762306a36Sopenharmony_ci		apm_queue_event(APM_USER_SUSPEND);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return IRQ_HANDLED;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic int __init hp6x0_apm_init(void)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	int ret;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	ret = request_irq(HP680_BTN_IRQ, hp6x0_apm_interrupt,
8762306a36Sopenharmony_ci			  0, MODNAME, NULL);
8862306a36Sopenharmony_ci	if (unlikely(ret < 0)) {
8962306a36Sopenharmony_ci		printk(KERN_ERR MODNAME ": IRQ %d request failed\n",
9062306a36Sopenharmony_ci		       HP680_BTN_IRQ);
9162306a36Sopenharmony_ci		return ret;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	apm_get_power_status = hp6x0_apm_get_power_status;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return ret;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void __exit hp6x0_apm_exit(void)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	free_irq(HP680_BTN_IRQ, 0);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cimodule_init(hp6x0_apm_init);
10562306a36Sopenharmony_cimodule_exit(hp6x0_apm_exit);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ciMODULE_AUTHOR("Adriy Skulysh");
10862306a36Sopenharmony_ciMODULE_DESCRIPTION("hp6xx Advanced Power Management");
10962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
110