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/interrupt.h> 18#include <linux/platform_device.h> 19#include <linux/apm-emulation.h> 20 21#include <asm/irq.h> 22#include <asm/mach-types.h> 23#include <asm/hardware.h> 24#include <asm/hardware/scoop.h> 25 26#include <asm/arch/sharpsl.h> 27#include <asm/arch/corgi.h> 28#include <asm/arch/pxa-regs.h> 29#include "sharpsl.h" 30 31#define SHARPSL_CHARGE_ON_VOLT 0x99 /* 2.9V */ 32#define SHARPSL_CHARGE_ON_TEMP 0xe0 /* 2.9V */ 33#define SHARPSL_CHARGE_ON_ACIN_HIGH 0x9b /* 6V */ 34#define SHARPSL_CHARGE_ON_ACIN_LOW 0x34 /* 2V */ 35#define SHARPSL_FATAL_ACIN_VOLT 182 /* 3.45V */ 36#define SHARPSL_FATAL_NOACIN_VOLT 170 /* 3.40V */ 37 38static void corgi_charger_init(void) 39{ 40 pxa_gpio_mode(CORGI_GPIO_ADC_TEMP_ON | GPIO_OUT); 41 pxa_gpio_mode(CORGI_GPIO_CHRG_ON | GPIO_OUT); 42 pxa_gpio_mode(CORGI_GPIO_CHRG_UKN | GPIO_OUT); 43 pxa_gpio_mode(CORGI_GPIO_KEY_INT | GPIO_IN); 44 sharpsl_pm_pxa_init(); 45} 46 47static void corgi_measure_temp(int on) 48{ 49 if (on) 50 GPSR(CORGI_GPIO_ADC_TEMP_ON) = GPIO_bit(CORGI_GPIO_ADC_TEMP_ON); 51 else 52 GPCR(CORGI_GPIO_ADC_TEMP_ON) = GPIO_bit(CORGI_GPIO_ADC_TEMP_ON); 53} 54 55static void corgi_charge(int on) 56{ 57 if (on) { 58 if (machine_is_corgi() && (sharpsl_pm.flags & SHARPSL_SUSPENDED)) { 59 GPCR(CORGI_GPIO_CHRG_ON) = GPIO_bit(CORGI_GPIO_CHRG_ON); 60 GPSR(CORGI_GPIO_CHRG_UKN) = GPIO_bit(CORGI_GPIO_CHRG_UKN); 61 } else { 62 GPSR(CORGI_GPIO_CHRG_ON) = GPIO_bit(CORGI_GPIO_CHRG_ON); 63 GPCR(CORGI_GPIO_CHRG_UKN) = GPIO_bit(CORGI_GPIO_CHRG_UKN); 64 } 65 } else { 66 GPCR(CORGI_GPIO_CHRG_ON) = GPIO_bit(CORGI_GPIO_CHRG_ON); 67 GPCR(CORGI_GPIO_CHRG_UKN) = GPIO_bit(CORGI_GPIO_CHRG_UKN); 68 } 69} 70 71static void corgi_discharge(int on) 72{ 73 if (on) 74 GPSR(CORGI_GPIO_DISCHARGE_ON) = GPIO_bit(CORGI_GPIO_DISCHARGE_ON); 75 else 76 GPCR(CORGI_GPIO_DISCHARGE_ON) = GPIO_bit(CORGI_GPIO_DISCHARGE_ON); 77} 78 79static void corgi_presuspend(void) 80{ 81 int i; 82 unsigned long wakeup_mask; 83 84 /* charging , so CHARGE_ON bit is HIGH during OFF. */ 85 if (READ_GPIO_BIT(CORGI_GPIO_CHRG_ON)) 86 PGSR1 |= GPIO_bit(CORGI_GPIO_CHRG_ON); 87 else 88 PGSR1 &= ~GPIO_bit(CORGI_GPIO_CHRG_ON); 89 90 if (READ_GPIO_BIT(CORGI_GPIO_LED_ORANGE)) 91 PGSR0 |= GPIO_bit(CORGI_GPIO_LED_ORANGE); 92 else 93 PGSR0 &= ~GPIO_bit(CORGI_GPIO_LED_ORANGE); 94 95 if (READ_GPIO_BIT(CORGI_GPIO_CHRG_UKN)) 96 PGSR1 |= GPIO_bit(CORGI_GPIO_CHRG_UKN); 97 else 98 PGSR1 &= ~GPIO_bit(CORGI_GPIO_CHRG_UKN); 99 100 /* Resume on keyboard power key */ 101 PGSR2 = (PGSR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(0); 102 103 wakeup_mask = GPIO_bit(CORGI_GPIO_KEY_INT) | GPIO_bit(CORGI_GPIO_WAKEUP) | GPIO_bit(CORGI_GPIO_AC_IN) | GPIO_bit(CORGI_GPIO_CHRG_FULL); 104 105 if (!machine_is_corgi()) 106 wakeup_mask |= GPIO_bit(CORGI_GPIO_MAIN_BAT_LOW); 107 108 PWER = wakeup_mask | PWER_RTC; 109 PRER = wakeup_mask; 110 PFER = wakeup_mask; 111 112 for (i = 0; i <=15; i++) { 113 if (PRER & PFER & GPIO_bit(i)) { 114 if (GPLR0 & GPIO_bit(i) ) 115 PRER &= ~GPIO_bit(i); 116 else 117 PFER &= ~GPIO_bit(i); 118 } 119 } 120} 121 122static void corgi_postsuspend(void) 123{ 124} 125 126/* 127 * Check what brought us out of the suspend. 128 * Return: 0 to sleep, otherwise wake 129 */ 130static int corgi_should_wakeup(unsigned int resume_on_alarm) 131{ 132 int is_resume = 0; 133 134 dev_dbg(sharpsl_pm.dev, "GPLR0 = %x,%x\n", GPLR0, PEDR); 135 136 if ((PEDR & GPIO_bit(CORGI_GPIO_AC_IN))) { 137 if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) { 138 /* charge on */ 139 dev_dbg(sharpsl_pm.dev, "ac insert\n"); 140 sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG; 141 } else { 142 /* charge off */ 143 dev_dbg(sharpsl_pm.dev, "ac remove\n"); 144 sharpsl_pm_led(SHARPSL_LED_OFF); 145 sharpsl_pm.machinfo->charge(0); 146 sharpsl_pm.charge_mode = CHRG_OFF; 147 } 148 } 149 150 if ((PEDR & GPIO_bit(CORGI_GPIO_CHRG_FULL))) 151 dev_dbg(sharpsl_pm.dev, "Charge full interrupt\n"); 152 153 if (PEDR & GPIO_bit(CORGI_GPIO_KEY_INT)) 154 is_resume |= GPIO_bit(CORGI_GPIO_KEY_INT); 155 156 if (PEDR & GPIO_bit(CORGI_GPIO_WAKEUP)) 157 is_resume |= GPIO_bit(CORGI_GPIO_WAKEUP); 158 159 if (resume_on_alarm && (PEDR & PWER_RTC)) 160 is_resume |= PWER_RTC; 161 162 dev_dbg(sharpsl_pm.dev, "is_resume: %x\n",is_resume); 163 return is_resume; 164} 165 166static unsigned long corgi_charger_wakeup(void) 167{ 168 return ~GPLR0 & ( GPIO_bit(CORGI_GPIO_AC_IN) | GPIO_bit(CORGI_GPIO_KEY_INT) | GPIO_bit(CORGI_GPIO_WAKEUP) ); 169} 170 171unsigned long corgipm_read_devdata(int type) 172{ 173 switch(type) { 174 case SHARPSL_STATUS_ACIN: 175 return ((GPLR(CORGI_GPIO_AC_IN) & GPIO_bit(CORGI_GPIO_AC_IN)) != 0); 176 case SHARPSL_STATUS_LOCK: 177 return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batlock); 178 case SHARPSL_STATUS_CHRGFULL: 179 return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batfull); 180 case SHARPSL_STATUS_FATAL: 181 return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_fatal); 182 case SHARPSL_ACIN_VOLT: 183 return sharpsl_pm_pxa_read_max1111(MAX1111_ACIN_VOLT); 184 case SHARPSL_BATT_TEMP: 185 return sharpsl_pm_pxa_read_max1111(MAX1111_BATT_TEMP); 186 case SHARPSL_BATT_VOLT: 187 default: 188 return sharpsl_pm_pxa_read_max1111(MAX1111_BATT_VOLT); 189 } 190} 191 192static struct sharpsl_charger_machinfo corgi_pm_machinfo = { 193 .init = corgi_charger_init, 194 .exit = sharpsl_pm_pxa_remove, 195 .gpio_batlock = CORGI_GPIO_BAT_COVER, 196 .gpio_acin = CORGI_GPIO_AC_IN, 197 .gpio_batfull = CORGI_GPIO_CHRG_FULL, 198 .discharge = corgi_discharge, 199 .charge = corgi_charge, 200 .measure_temp = corgi_measure_temp, 201 .presuspend = corgi_presuspend, 202 .postsuspend = corgi_postsuspend, 203 .read_devdata = corgipm_read_devdata, 204 .charger_wakeup = corgi_charger_wakeup, 205 .should_wakeup = corgi_should_wakeup, 206 .backlight_limit = corgibl_limit_intensity, 207 .charge_on_volt = SHARPSL_CHARGE_ON_VOLT, 208 .charge_on_temp = SHARPSL_CHARGE_ON_TEMP, 209 .charge_acin_high = SHARPSL_CHARGE_ON_ACIN_HIGH, 210 .charge_acin_low = SHARPSL_CHARGE_ON_ACIN_LOW, 211 .fatal_acin_volt = SHARPSL_FATAL_ACIN_VOLT, 212 .fatal_noacin_volt= SHARPSL_FATAL_NOACIN_VOLT, 213 .bat_levels = 40, 214 .bat_levels_noac = spitz_battery_levels_noac, 215 .bat_levels_acin = spitz_battery_levels_acin, 216 .status_high_acin = 188, 217 .status_low_acin = 178, 218 .status_high_noac = 185, 219 .status_low_noac = 175, 220}; 221 222static struct platform_device *corgipm_device; 223 224static int __devinit corgipm_init(void) 225{ 226 int ret; 227 228 corgipm_device = platform_device_alloc("sharpsl-pm", -1); 229 if (!corgipm_device) 230 return -ENOMEM; 231 232 if (!machine_is_corgi()) 233 corgi_pm_machinfo.batfull_irq = 1; 234 235 corgipm_device->dev.platform_data = &corgi_pm_machinfo; 236 ret = platform_device_add(corgipm_device); 237 238 if (ret) 239 platform_device_put(corgipm_device); 240 241 return ret; 242} 243 244static void corgipm_exit(void) 245{ 246 platform_device_unregister(corgipm_device); 247} 248 249module_init(corgipm_init); 250module_exit(corgipm_exit); 251