1/* 2 * Battery and Power Management code for the Sharp SL-C7xx 3 * 4 * Copyright (c) 2005 Richard Purdie 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 */ 11 12#include <linux/module.h> 13#include <linux/stat.h> 14#include <linux/init.h> 15#include <linux/kernel.h> 16#include <linux/delay.h> 17#include <linux/gpio.h> 18#include <linux/gpio-pxa.h> 19#include <linux/interrupt.h> 20#include <linux/platform_device.h> 21#include <linux/apm-emulation.h> 22#include <linux/io.h> 23 24#include <asm/irq.h> 25#include <asm/mach-types.h> 26#include <mach/hardware.h> 27 28#include <mach/corgi.h> 29#include <mach/pxa2xx-regs.h> 30#include <mach/sharpsl_pm.h> 31 32#include "generic.h" 33 34#define SHARPSL_CHARGE_ON_VOLT 0x99 /* 2.9V */ 35#define SHARPSL_CHARGE_ON_TEMP 0xe0 /* 2.9V */ 36#define SHARPSL_CHARGE_ON_ACIN_HIGH 0x9b /* 6V */ 37#define SHARPSL_CHARGE_ON_ACIN_LOW 0x34 /* 2V */ 38#define SHARPSL_FATAL_ACIN_VOLT 182 /* 3.45V */ 39#define SHARPSL_FATAL_NOACIN_VOLT 170 /* 3.40V */ 40 41static struct gpio charger_gpios[] = { 42 { CORGI_GPIO_ADC_TEMP_ON, GPIOF_OUT_INIT_LOW, "ADC Temp On" }, 43 { CORGI_GPIO_CHRG_ON, GPIOF_OUT_INIT_LOW, "Charger On" }, 44 { CORGI_GPIO_CHRG_UKN, GPIOF_OUT_INIT_LOW, "Charger Unknown" }, 45 { CORGI_GPIO_AC_IN, GPIOF_IN, "Charger Detection" }, 46 { CORGI_GPIO_KEY_INT, GPIOF_IN, "Key Interrupt" }, 47 { CORGI_GPIO_WAKEUP, GPIOF_IN, "System wakeup notification" }, 48}; 49 50static void corgi_charger_init(void) 51{ 52 gpio_request_array(ARRAY_AND_SIZE(charger_gpios)); 53} 54 55static void corgi_measure_temp(int on) 56{ 57 gpio_set_value(CORGI_GPIO_ADC_TEMP_ON, on); 58} 59 60static void corgi_charge(int on) 61{ 62 if (on) { 63 if (machine_is_corgi() && (sharpsl_pm.flags & SHARPSL_SUSPENDED)) { 64 gpio_set_value(CORGI_GPIO_CHRG_ON, 0); 65 gpio_set_value(CORGI_GPIO_CHRG_UKN, 1); 66 } else { 67 gpio_set_value(CORGI_GPIO_CHRG_ON, 1); 68 gpio_set_value(CORGI_GPIO_CHRG_UKN, 0); 69 } 70 } else { 71 gpio_set_value(CORGI_GPIO_CHRG_ON, 0); 72 gpio_set_value(CORGI_GPIO_CHRG_UKN, 0); 73 } 74} 75 76static void corgi_discharge(int on) 77{ 78 gpio_set_value(CORGI_GPIO_DISCHARGE_ON, on); 79} 80 81static void corgi_presuspend(void) 82{ 83} 84 85static void corgi_postsuspend(void) 86{ 87} 88 89/* 90 * Check what brought us out of the suspend. 91 * Return: 0 to sleep, otherwise wake 92 */ 93static int corgi_should_wakeup(unsigned int resume_on_alarm) 94{ 95 int is_resume = 0; 96 97 dev_dbg(sharpsl_pm.dev, "PEDR = %x, GPIO_AC_IN = %d, " 98 "GPIO_CHRG_FULL = %d, GPIO_KEY_INT = %d, GPIO_WAKEUP = %d\n", 99 PEDR, gpio_get_value(CORGI_GPIO_AC_IN), 100 gpio_get_value(CORGI_GPIO_CHRG_FULL), 101 gpio_get_value(CORGI_GPIO_KEY_INT), 102 gpio_get_value(CORGI_GPIO_WAKEUP)); 103 104 if ((PEDR & GPIO_bit(CORGI_GPIO_AC_IN))) { 105 if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) { 106 /* charge on */ 107 dev_dbg(sharpsl_pm.dev, "ac insert\n"); 108 sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG; 109 } else { 110 /* charge off */ 111 dev_dbg(sharpsl_pm.dev, "ac remove\n"); 112 sharpsl_pm_led(SHARPSL_LED_OFF); 113 sharpsl_pm.machinfo->charge(0); 114 sharpsl_pm.charge_mode = CHRG_OFF; 115 } 116 } 117 118 if ((PEDR & GPIO_bit(CORGI_GPIO_CHRG_FULL))) 119 dev_dbg(sharpsl_pm.dev, "Charge full interrupt\n"); 120 121 if (PEDR & GPIO_bit(CORGI_GPIO_KEY_INT)) 122 is_resume |= GPIO_bit(CORGI_GPIO_KEY_INT); 123 124 if (PEDR & GPIO_bit(CORGI_GPIO_WAKEUP)) 125 is_resume |= GPIO_bit(CORGI_GPIO_WAKEUP); 126 127 if (resume_on_alarm && (PEDR & PWER_RTC)) 128 is_resume |= PWER_RTC; 129 130 dev_dbg(sharpsl_pm.dev, "is_resume: %x\n",is_resume); 131 return is_resume; 132} 133 134static unsigned long corgi_charger_wakeup(void) 135{ 136 unsigned long ret; 137 138 ret = (!gpio_get_value(CORGI_GPIO_AC_IN) << GPIO_bit(CORGI_GPIO_AC_IN)) 139 | (!gpio_get_value(CORGI_GPIO_KEY_INT) 140 << GPIO_bit(CORGI_GPIO_KEY_INT)) 141 | (!gpio_get_value(CORGI_GPIO_WAKEUP) 142 << GPIO_bit(CORGI_GPIO_WAKEUP)); 143 return ret; 144} 145 146unsigned long corgipm_read_devdata(int type) 147{ 148 switch(type) { 149 case SHARPSL_STATUS_ACIN: 150 return !gpio_get_value(CORGI_GPIO_AC_IN); 151 case SHARPSL_STATUS_LOCK: 152 return gpio_get_value(sharpsl_pm.machinfo->gpio_batlock); 153 case SHARPSL_STATUS_CHRGFULL: 154 return gpio_get_value(sharpsl_pm.machinfo->gpio_batfull); 155 case SHARPSL_STATUS_FATAL: 156 return gpio_get_value(sharpsl_pm.machinfo->gpio_fatal); 157 case SHARPSL_ACIN_VOLT: 158 return sharpsl_pm_pxa_read_max1111(MAX1111_ACIN_VOLT); 159 case SHARPSL_BATT_TEMP: 160 return sharpsl_pm_pxa_read_max1111(MAX1111_BATT_TEMP); 161 case SHARPSL_BATT_VOLT: 162 default: 163 return sharpsl_pm_pxa_read_max1111(MAX1111_BATT_VOLT); 164 } 165} 166 167static struct sharpsl_charger_machinfo corgi_pm_machinfo = { 168 .init = corgi_charger_init, 169 .exit = NULL, 170 .gpio_batlock = CORGI_GPIO_BAT_COVER, 171 .gpio_acin = CORGI_GPIO_AC_IN, 172 .gpio_batfull = CORGI_GPIO_CHRG_FULL, 173 .discharge = corgi_discharge, 174 .charge = corgi_charge, 175 .measure_temp = corgi_measure_temp, 176 .presuspend = corgi_presuspend, 177 .postsuspend = corgi_postsuspend, 178 .read_devdata = corgipm_read_devdata, 179 .charger_wakeup = corgi_charger_wakeup, 180 .should_wakeup = corgi_should_wakeup, 181#if defined(CONFIG_LCD_CORGI) 182 .backlight_limit = corgi_lcd_limit_intensity, 183#endif 184 .charge_on_volt = SHARPSL_CHARGE_ON_VOLT, 185 .charge_on_temp = SHARPSL_CHARGE_ON_TEMP, 186 .charge_acin_high = SHARPSL_CHARGE_ON_ACIN_HIGH, 187 .charge_acin_low = SHARPSL_CHARGE_ON_ACIN_LOW, 188 .fatal_acin_volt = SHARPSL_FATAL_ACIN_VOLT, 189 .fatal_noacin_volt= SHARPSL_FATAL_NOACIN_VOLT, 190 .bat_levels = 40, 191 .bat_levels_noac = sharpsl_battery_levels_noac, 192 .bat_levels_acin = sharpsl_battery_levels_acin, 193 .status_high_acin = 188, 194 .status_low_acin = 178, 195 .status_high_noac = 185, 196 .status_low_noac = 175, 197}; 198 199static struct platform_device *corgipm_device; 200 201static int corgipm_init(void) 202{ 203 int ret; 204 205 if (!machine_is_corgi() && !machine_is_shepherd() 206 && !machine_is_husky()) 207 return -ENODEV; 208 209 corgipm_device = platform_device_alloc("sharpsl-pm", -1); 210 if (!corgipm_device) 211 return -ENOMEM; 212 213 if (!machine_is_corgi()) 214 corgi_pm_machinfo.batfull_irq = 1; 215 216 corgipm_device->dev.platform_data = &corgi_pm_machinfo; 217 ret = platform_device_add(corgipm_device); 218 219 if (ret) 220 platform_device_put(corgipm_device); 221 222 return ret; 223} 224 225static void corgipm_exit(void) 226{ 227 platform_device_unregister(corgipm_device); 228} 229 230module_init(corgipm_init); 231module_exit(corgipm_exit); 232