Commit | Line | Data |
---|---|---|
d72f25b0 RP |
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> | |
e63f591a | 17 | #include <linux/gpio.h> |
d72f25b0 RP |
18 | #include <linux/interrupt.h> |
19 | #include <linux/platform_device.h> | |
14fca61a RK |
20 | #include <linux/apm-emulation.h> |
21 | ||
d72f25b0 RP |
22 | #include <asm/irq.h> |
23 | #include <asm/mach-types.h> | |
a09e64fb | 24 | #include <mach/hardware.h> |
d72f25b0 | 25 | |
a09e64fb | 26 | #include <mach/corgi.h> |
a09e64fb | 27 | #include <mach/pxa2xx-regs.h> |
04e4ad23 | 28 | #include <mach/sharpsl_pm.h> |
e63f591a EM |
29 | |
30 | #include "generic.h" | |
d72f25b0 | 31 | |
f8703dc8 RP |
32 | #define SHARPSL_CHARGE_ON_VOLT 0x99 /* 2.9V */ |
33 | #define SHARPSL_CHARGE_ON_TEMP 0xe0 /* 2.9V */ | |
34 | #define SHARPSL_CHARGE_ON_ACIN_HIGH 0x9b /* 6V */ | |
35 | #define SHARPSL_CHARGE_ON_ACIN_LOW 0x34 /* 2V */ | |
36 | #define SHARPSL_FATAL_ACIN_VOLT 182 /* 3.45V */ | |
37 | #define SHARPSL_FATAL_NOACIN_VOLT 170 /* 3.40V */ | |
38 | ||
e63f591a EM |
39 | static struct gpio charger_gpios[] = { |
40 | { CORGI_GPIO_ADC_TEMP_ON, GPIOF_OUT_INIT_LOW, "ADC Temp On" }, | |
41 | { CORGI_GPIO_CHRG_ON, GPIOF_OUT_INIT_LOW, "Charger On" }, | |
42 | { CORGI_GPIO_CHRG_UKN, GPIOF_OUT_INIT_LOW, "Charger Unknown" }, | |
43 | { CORGI_GPIO_KEY_INT, GPIOF_IN, "Key Interrupt" }, | |
44 | }; | |
45 | ||
d72f25b0 RP |
46 | static void corgi_charger_init(void) |
47 | { | |
e63f591a | 48 | gpio_request_array(ARRAY_AND_SIZE(charger_gpios)); |
d72f25b0 RP |
49 | } |
50 | ||
51 | static void corgi_measure_temp(int on) | |
52 | { | |
e63f591a | 53 | gpio_set_value(CORGI_GPIO_ADC_TEMP_ON, on); |
d72f25b0 RP |
54 | } |
55 | ||
56 | static void corgi_charge(int on) | |
57 | { | |
58 | if (on) { | |
59 | if (machine_is_corgi() && (sharpsl_pm.flags & SHARPSL_SUSPENDED)) { | |
e63f591a EM |
60 | gpio_set_value(CORGI_GPIO_CHRG_ON, 0); |
61 | gpio_set_value(CORGI_GPIO_CHRG_UKN, 1); | |
d72f25b0 | 62 | } else { |
e63f591a EM |
63 | gpio_set_value(CORGI_GPIO_CHRG_ON, 1); |
64 | gpio_set_value(CORGI_GPIO_CHRG_UKN, 0); | |
d72f25b0 RP |
65 | } |
66 | } else { | |
e63f591a EM |
67 | gpio_set_value(CORGI_GPIO_CHRG_ON, 0); |
68 | gpio_set_value(CORGI_GPIO_CHRG_UKN, 0); | |
d72f25b0 RP |
69 | } |
70 | } | |
71 | ||
72 | static void corgi_discharge(int on) | |
73 | { | |
e63f591a | 74 | gpio_set_value(CORGI_GPIO_DISCHARGE_ON, on); |
d72f25b0 RP |
75 | } |
76 | ||
77 | static void corgi_presuspend(void) | |
78 | { | |
d72f25b0 RP |
79 | } |
80 | ||
81 | static void corgi_postsuspend(void) | |
82 | { | |
83 | } | |
84 | ||
85 | /* | |
86 | * Check what brought us out of the suspend. | |
87 | * Return: 0 to sleep, otherwise wake | |
88 | */ | |
89 | static int corgi_should_wakeup(unsigned int resume_on_alarm) | |
90 | { | |
91 | int is_resume = 0; | |
92 | ||
93 | dev_dbg(sharpsl_pm.dev, "GPLR0 = %x,%x\n", GPLR0, PEDR); | |
94 | ||
95 | if ((PEDR & GPIO_bit(CORGI_GPIO_AC_IN))) { | |
b7557de4 | 96 | if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) { |
d72f25b0 RP |
97 | /* charge on */ |
98 | dev_dbg(sharpsl_pm.dev, "ac insert\n"); | |
99 | sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG; | |
100 | } else { | |
101 | /* charge off */ | |
102 | dev_dbg(sharpsl_pm.dev, "ac remove\n"); | |
b7557de4 RP |
103 | sharpsl_pm_led(SHARPSL_LED_OFF); |
104 | sharpsl_pm.machinfo->charge(0); | |
d72f25b0 RP |
105 | sharpsl_pm.charge_mode = CHRG_OFF; |
106 | } | |
107 | } | |
108 | ||
109 | if ((PEDR & GPIO_bit(CORGI_GPIO_CHRG_FULL))) | |
110 | dev_dbg(sharpsl_pm.dev, "Charge full interrupt\n"); | |
111 | ||
112 | if (PEDR & GPIO_bit(CORGI_GPIO_KEY_INT)) | |
113 | is_resume |= GPIO_bit(CORGI_GPIO_KEY_INT); | |
114 | ||
115 | if (PEDR & GPIO_bit(CORGI_GPIO_WAKEUP)) | |
116 | is_resume |= GPIO_bit(CORGI_GPIO_WAKEUP); | |
117 | ||
118 | if (resume_on_alarm && (PEDR & PWER_RTC)) | |
119 | is_resume |= PWER_RTC; | |
120 | ||
121 | dev_dbg(sharpsl_pm.dev, "is_resume: %x\n",is_resume); | |
122 | return is_resume; | |
123 | } | |
124 | ||
125 | static unsigned long corgi_charger_wakeup(void) | |
126 | { | |
127 | return ~GPLR0 & ( GPIO_bit(CORGI_GPIO_AC_IN) | GPIO_bit(CORGI_GPIO_KEY_INT) | GPIO_bit(CORGI_GPIO_WAKEUP) ); | |
128 | } | |
129 | ||
b7557de4 | 130 | unsigned long corgipm_read_devdata(int type) |
d72f25b0 | 131 | { |
b7557de4 RP |
132 | switch(type) { |
133 | case SHARPSL_STATUS_ACIN: | |
134 | return ((GPLR(CORGI_GPIO_AC_IN) & GPIO_bit(CORGI_GPIO_AC_IN)) != 0); | |
135 | case SHARPSL_STATUS_LOCK: | |
05732d7e | 136 | return gpio_get_value(sharpsl_pm.machinfo->gpio_batlock); |
b7557de4 | 137 | case SHARPSL_STATUS_CHRGFULL: |
05732d7e | 138 | return gpio_get_value(sharpsl_pm.machinfo->gpio_batfull); |
b7557de4 | 139 | case SHARPSL_STATUS_FATAL: |
05732d7e | 140 | return gpio_get_value(sharpsl_pm.machinfo->gpio_fatal); |
b7557de4 RP |
141 | case SHARPSL_ACIN_VOLT: |
142 | return sharpsl_pm_pxa_read_max1111(MAX1111_ACIN_VOLT); | |
143 | case SHARPSL_BATT_TEMP: | |
144 | return sharpsl_pm_pxa_read_max1111(MAX1111_BATT_TEMP); | |
145 | case SHARPSL_BATT_VOLT: | |
146 | default: | |
147 | return sharpsl_pm_pxa_read_max1111(MAX1111_BATT_VOLT); | |
148 | } | |
d72f25b0 RP |
149 | } |
150 | ||
151 | static struct sharpsl_charger_machinfo corgi_pm_machinfo = { | |
152 | .init = corgi_charger_init, | |
d48898a3 | 153 | .exit = NULL, |
d72f25b0 RP |
154 | .gpio_batlock = CORGI_GPIO_BAT_COVER, |
155 | .gpio_acin = CORGI_GPIO_AC_IN, | |
156 | .gpio_batfull = CORGI_GPIO_CHRG_FULL, | |
d72f25b0 RP |
157 | .discharge = corgi_discharge, |
158 | .charge = corgi_charge, | |
d72f25b0 RP |
159 | .measure_temp = corgi_measure_temp, |
160 | .presuspend = corgi_presuspend, | |
161 | .postsuspend = corgi_postsuspend, | |
b7557de4 | 162 | .read_devdata = corgipm_read_devdata, |
d72f25b0 RP |
163 | .charger_wakeup = corgi_charger_wakeup, |
164 | .should_wakeup = corgi_should_wakeup, | |
5cbff960 DB |
165 | #if defined(CONFIG_LCD_CORGI) |
166 | .backlight_limit = corgi_lcd_limit_intensity, | |
02a8e769 | 167 | #endif |
f8703dc8 RP |
168 | .charge_on_volt = SHARPSL_CHARGE_ON_VOLT, |
169 | .charge_on_temp = SHARPSL_CHARGE_ON_TEMP, | |
170 | .charge_acin_high = SHARPSL_CHARGE_ON_ACIN_HIGH, | |
171 | .charge_acin_low = SHARPSL_CHARGE_ON_ACIN_LOW, | |
172 | .fatal_acin_volt = SHARPSL_FATAL_ACIN_VOLT, | |
173 | .fatal_noacin_volt= SHARPSL_FATAL_NOACIN_VOLT, | |
174 | .bat_levels = 40, | |
0ba01ebc PM |
175 | .bat_levels_noac = sharpsl_battery_levels_noac, |
176 | .bat_levels_acin = sharpsl_battery_levels_acin, | |
d72f25b0 RP |
177 | .status_high_acin = 188, |
178 | .status_low_acin = 178, | |
179 | .status_high_noac = 185, | |
180 | .status_low_noac = 175, | |
181 | }; | |
182 | ||
183 | static struct platform_device *corgipm_device; | |
184 | ||
185 | static int __devinit corgipm_init(void) | |
186 | { | |
187 | int ret; | |
188 | ||
043fbc09 DB |
189 | if (!machine_is_corgi() && !machine_is_shepherd() |
190 | && !machine_is_husky()) | |
191 | return -ENODEV; | |
192 | ||
d72f25b0 RP |
193 | corgipm_device = platform_device_alloc("sharpsl-pm", -1); |
194 | if (!corgipm_device) | |
195 | return -ENOMEM; | |
196 | ||
f8703dc8 RP |
197 | if (!machine_is_corgi()) |
198 | corgi_pm_machinfo.batfull_irq = 1; | |
199 | ||
d72f25b0 RP |
200 | corgipm_device->dev.platform_data = &corgi_pm_machinfo; |
201 | ret = platform_device_add(corgipm_device); | |
202 | ||
203 | if (ret) | |
204 | platform_device_put(corgipm_device); | |
205 | ||
206 | return ret; | |
207 | } | |
208 | ||
209 | static void corgipm_exit(void) | |
210 | { | |
211 | platform_device_unregister(corgipm_device); | |
212 | } | |
213 | ||
214 | module_init(corgipm_init); | |
215 | module_exit(corgipm_exit); |