Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Battery and Power Management code for the Sharp SL-C7xx
0004  *
0005  * Copyright (c) 2005 Richard Purdie
0006  */
0007 
0008 #include <linux/module.h>
0009 #include <linux/stat.h>
0010 #include <linux/init.h>
0011 #include <linux/kernel.h>
0012 #include <linux/delay.h>
0013 #include <linux/gpio.h>
0014 #include <linux/gpio-pxa.h>
0015 #include <linux/interrupt.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/apm-emulation.h>
0018 #include <linux/io.h>
0019 
0020 #include <asm/irq.h>
0021 #include <asm/mach-types.h>
0022 
0023 #include "corgi.h"
0024 #include "pxa2xx-regs.h"
0025 #include "sharpsl_pm.h"
0026 
0027 #include "generic.h"
0028 
0029 #define SHARPSL_CHARGE_ON_VOLT         0x99  /* 2.9V */
0030 #define SHARPSL_CHARGE_ON_TEMP         0xe0  /* 2.9V */
0031 #define SHARPSL_CHARGE_ON_ACIN_HIGH    0x9b  /* 6V */
0032 #define SHARPSL_CHARGE_ON_ACIN_LOW     0x34  /* 2V */
0033 #define SHARPSL_FATAL_ACIN_VOLT        182   /* 3.45V */
0034 #define SHARPSL_FATAL_NOACIN_VOLT      170   /* 3.40V */
0035 
0036 static struct gpio charger_gpios[] = {
0037     { CORGI_GPIO_ADC_TEMP_ON, GPIOF_OUT_INIT_LOW, "ADC Temp On" },
0038     { CORGI_GPIO_CHRG_ON,     GPIOF_OUT_INIT_LOW, "Charger On" },
0039     { CORGI_GPIO_CHRG_UKN,    GPIOF_OUT_INIT_LOW, "Charger Unknown" },
0040     { CORGI_GPIO_AC_IN,   GPIOF_IN, "Charger Detection" },
0041     { CORGI_GPIO_KEY_INT,     GPIOF_IN, "Key Interrupt" },
0042     { CORGI_GPIO_WAKEUP,      GPIOF_IN, "System wakeup notification" },
0043 };
0044 
0045 static void corgi_charger_init(void)
0046 {
0047     gpio_request_array(ARRAY_AND_SIZE(charger_gpios));
0048 }
0049 
0050 static void corgi_measure_temp(int on)
0051 {
0052     gpio_set_value(CORGI_GPIO_ADC_TEMP_ON, on);
0053 }
0054 
0055 static void corgi_charge(int on)
0056 {
0057     if (on) {
0058         if (machine_is_corgi() && (sharpsl_pm.flags & SHARPSL_SUSPENDED)) {
0059             gpio_set_value(CORGI_GPIO_CHRG_ON, 0);
0060             gpio_set_value(CORGI_GPIO_CHRG_UKN, 1);
0061         } else {
0062             gpio_set_value(CORGI_GPIO_CHRG_ON, 1);
0063             gpio_set_value(CORGI_GPIO_CHRG_UKN, 0);
0064         }
0065     } else {
0066         gpio_set_value(CORGI_GPIO_CHRG_ON, 0);
0067         gpio_set_value(CORGI_GPIO_CHRG_UKN, 0);
0068     }
0069 }
0070 
0071 static void corgi_discharge(int on)
0072 {
0073     gpio_set_value(CORGI_GPIO_DISCHARGE_ON, on);
0074 }
0075 
0076 static void corgi_presuspend(void)
0077 {
0078 }
0079 
0080 static void corgi_postsuspend(void)
0081 {
0082 }
0083 
0084 /*
0085  * Check what brought us out of the suspend.
0086  * Return: 0 to sleep, otherwise wake
0087  */
0088 static int corgi_should_wakeup(unsigned int resume_on_alarm)
0089 {
0090     int is_resume = 0;
0091 
0092     dev_dbg(sharpsl_pm.dev, "PEDR = %x, GPIO_AC_IN = %d, "
0093         "GPIO_CHRG_FULL = %d, GPIO_KEY_INT = %d, GPIO_WAKEUP = %d\n",
0094         PEDR, gpio_get_value(CORGI_GPIO_AC_IN),
0095         gpio_get_value(CORGI_GPIO_CHRG_FULL),
0096         gpio_get_value(CORGI_GPIO_KEY_INT),
0097         gpio_get_value(CORGI_GPIO_WAKEUP));
0098 
0099     if ((PEDR & GPIO_bit(CORGI_GPIO_AC_IN))) {
0100         if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) {
0101             /* charge on */
0102             dev_dbg(sharpsl_pm.dev, "ac insert\n");
0103             sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG;
0104         } else {
0105             /* charge off */
0106             dev_dbg(sharpsl_pm.dev, "ac remove\n");
0107             sharpsl_pm_led(SHARPSL_LED_OFF);
0108             sharpsl_pm.machinfo->charge(0);
0109             sharpsl_pm.charge_mode = CHRG_OFF;
0110         }
0111     }
0112 
0113     if ((PEDR & GPIO_bit(CORGI_GPIO_CHRG_FULL)))
0114         dev_dbg(sharpsl_pm.dev, "Charge full interrupt\n");
0115 
0116     if (PEDR & GPIO_bit(CORGI_GPIO_KEY_INT))
0117         is_resume |= GPIO_bit(CORGI_GPIO_KEY_INT);
0118 
0119     if (PEDR & GPIO_bit(CORGI_GPIO_WAKEUP))
0120         is_resume |= GPIO_bit(CORGI_GPIO_WAKEUP);
0121 
0122     if (resume_on_alarm && (PEDR & PWER_RTC))
0123         is_resume |= PWER_RTC;
0124 
0125     dev_dbg(sharpsl_pm.dev, "is_resume: %x\n",is_resume);
0126     return is_resume;
0127 }
0128 
0129 static bool corgi_charger_wakeup(void)
0130 {
0131     return !gpio_get_value(CORGI_GPIO_AC_IN) ||
0132         !gpio_get_value(CORGI_GPIO_KEY_INT) ||
0133         !gpio_get_value(CORGI_GPIO_WAKEUP);
0134 }
0135 
0136 unsigned long corgipm_read_devdata(int type)
0137 {
0138     switch(type) {
0139     case SHARPSL_STATUS_ACIN:
0140         return !gpio_get_value(CORGI_GPIO_AC_IN);
0141     case SHARPSL_STATUS_LOCK:
0142         return gpio_get_value(sharpsl_pm.machinfo->gpio_batlock);
0143     case SHARPSL_STATUS_CHRGFULL:
0144         return gpio_get_value(sharpsl_pm.machinfo->gpio_batfull);
0145     case SHARPSL_STATUS_FATAL:
0146         return gpio_get_value(sharpsl_pm.machinfo->gpio_fatal);
0147     case SHARPSL_ACIN_VOLT:
0148         return sharpsl_pm_pxa_read_max1111(MAX1111_ACIN_VOLT);
0149     case SHARPSL_BATT_TEMP:
0150         return sharpsl_pm_pxa_read_max1111(MAX1111_BATT_TEMP);
0151     case SHARPSL_BATT_VOLT:
0152     default:
0153         return sharpsl_pm_pxa_read_max1111(MAX1111_BATT_VOLT);
0154     }
0155 }
0156 
0157 static struct sharpsl_charger_machinfo corgi_pm_machinfo = {
0158     .init            = corgi_charger_init,
0159     .exit            = NULL,
0160     .gpio_batlock    = CORGI_GPIO_BAT_COVER,
0161     .gpio_acin       = CORGI_GPIO_AC_IN,
0162     .gpio_batfull    = CORGI_GPIO_CHRG_FULL,
0163     .discharge       = corgi_discharge,
0164     .charge          = corgi_charge,
0165     .measure_temp    = corgi_measure_temp,
0166     .presuspend      = corgi_presuspend,
0167     .postsuspend     = corgi_postsuspend,
0168     .read_devdata    = corgipm_read_devdata,
0169     .charger_wakeup  = corgi_charger_wakeup,
0170     .should_wakeup   = corgi_should_wakeup,
0171 #if defined(CONFIG_LCD_CORGI)
0172     .backlight_limit = corgi_lcd_limit_intensity,
0173 #endif
0174     .charge_on_volt   = SHARPSL_CHARGE_ON_VOLT,
0175     .charge_on_temp   = SHARPSL_CHARGE_ON_TEMP,
0176     .charge_acin_high = SHARPSL_CHARGE_ON_ACIN_HIGH,
0177     .charge_acin_low  = SHARPSL_CHARGE_ON_ACIN_LOW,
0178     .fatal_acin_volt  = SHARPSL_FATAL_ACIN_VOLT,
0179     .fatal_noacin_volt= SHARPSL_FATAL_NOACIN_VOLT,
0180     .bat_levels       = 40,
0181     .bat_levels_noac  = sharpsl_battery_levels_noac,
0182     .bat_levels_acin  = sharpsl_battery_levels_acin,
0183     .status_high_acin = 188,
0184     .status_low_acin  = 178,
0185     .status_high_noac = 185,
0186     .status_low_noac  = 175,
0187 };
0188 
0189 static struct platform_device *corgipm_device;
0190 
0191 static int corgipm_init(void)
0192 {
0193     int ret;
0194 
0195     if (!machine_is_corgi() && !machine_is_shepherd()
0196             && !machine_is_husky())
0197         return -ENODEV;
0198 
0199     corgipm_device = platform_device_alloc("sharpsl-pm", -1);
0200     if (!corgipm_device)
0201         return -ENOMEM;
0202 
0203     if (!machine_is_corgi())
0204         corgi_pm_machinfo.batfull_irq = 1;
0205 
0206     corgipm_device->dev.platform_data = &corgi_pm_machinfo;
0207     ret = platform_device_add(corgipm_device);
0208 
0209     if (ret)
0210         platform_device_put(corgipm_device);
0211 
0212     return ret;
0213 }
0214 
0215 static void corgipm_exit(void)
0216 {
0217     platform_device_unregister(corgipm_device);
0218 }
0219 
0220 module_init(corgipm_init);
0221 module_exit(corgipm_exit);