linux/arch/sh/boards/mach-hp6xx/hp6xx_apm.c
<<
>>
Prefs
   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
  30static 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
  74static 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
  82static int __init hp6x0_apm_init(void)
  83{
  84        int ret;
  85
  86        ret = request_irq(HP680_BTN_IRQ, hp6x0_apm_interrupt,
  87                          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
  99static void __exit hp6x0_apm_exit(void)
 100{
 101        free_irq(HP680_BTN_IRQ, 0);
 102}
 103
 104module_init(hp6x0_apm_init);
 105module_exit(hp6x0_apm_exit);
 106
 107MODULE_AUTHOR("Adriy Skulysh");
 108MODULE_DESCRIPTION("hp6xx Advanced Power Management");
 109MODULE_LICENSE("GPL");
 110