xref: /openbmc/linux/arch/arm/mach-pxa/spitz_pm.c (revision e0bf102e)
1e8b6f7f4SRichard Purdie /*
2e8b6f7f4SRichard Purdie  * Battery and Power Management code for the Sharp SL-Cxx00
3e8b6f7f4SRichard Purdie  *
4e8b6f7f4SRichard Purdie  * Copyright (c) 2005 Richard Purdie
5e8b6f7f4SRichard Purdie  *
6e8b6f7f4SRichard Purdie  * This program is free software; you can redistribute it and/or modify
7e8b6f7f4SRichard Purdie  * it under the terms of the GNU General Public License version 2 as
8e8b6f7f4SRichard Purdie  * published by the Free Software Foundation.
9e8b6f7f4SRichard Purdie  *
10e8b6f7f4SRichard Purdie  */
11e8b6f7f4SRichard Purdie 
12e8b6f7f4SRichard Purdie #include <linux/module.h>
13e8b6f7f4SRichard Purdie #include <linux/stat.h>
14e8b6f7f4SRichard Purdie #include <linux/init.h>
15e8b6f7f4SRichard Purdie #include <linux/kernel.h>
16e8b6f7f4SRichard Purdie #include <linux/delay.h>
17158f1e95SRandy Dunlap #include <linux/gpio.h>
18e0bf102eSHaojian Zhuang #include <linux/gpio-pxa.h>
19e8b6f7f4SRichard Purdie #include <linux/interrupt.h>
20e8b6f7f4SRichard Purdie #include <linux/platform_device.h>
2114fca61aSRussell King #include <linux/apm-emulation.h>
2214fca61aSRussell King 
23e8b6f7f4SRichard Purdie #include <asm/irq.h>
24e8b6f7f4SRichard Purdie #include <asm/mach-types.h>
25a09e64fbSRussell King #include <mach/hardware.h>
26e8b6f7f4SRichard Purdie 
27a09e64fbSRussell King #include <mach/spitz.h>
28b6eede11SEric Miao #include <mach/pxa27x.h>
2904e4ad23SEric Miao #include <mach/sharpsl_pm.h>
30b6eede11SEric Miao 
31b6eede11SEric Miao #include "generic.h"
32e8b6f7f4SRichard Purdie 
33f8703dc8SRichard Purdie #define SHARPSL_CHARGE_ON_VOLT         0x99  /* 2.9V */
34f8703dc8SRichard Purdie #define SHARPSL_CHARGE_ON_TEMP         0xe0  /* 2.9V */
35f8703dc8SRichard Purdie #define SHARPSL_CHARGE_ON_ACIN_HIGH    0x9b  /* 6V */
36f8703dc8SRichard Purdie #define SHARPSL_CHARGE_ON_ACIN_LOW     0x34  /* 2V */
37f8703dc8SRichard Purdie #define SHARPSL_FATAL_ACIN_VOLT        182   /* 3.45V */
38f8703dc8SRichard Purdie #define SHARPSL_FATAL_NOACIN_VOLT      170   /* 3.40V */
39f8703dc8SRichard Purdie 
40e8b6f7f4SRichard Purdie static int spitz_last_ac_status;
41e8b6f7f4SRichard Purdie 
42b6eede11SEric Miao static struct gpio spitz_charger_gpios[] = {
43b6eede11SEric Miao 	{ SPITZ_GPIO_KEY_INT,	GPIOF_IN, "Keyboard Interrupt" },
44b6eede11SEric Miao 	{ SPITZ_GPIO_SYNC,	GPIOF_IN, "Sync" },
459bf448c6SHaojian Zhuang 	{ SPITZ_GPIO_AC_IN,     GPIOF_IN, "Charger Detection" },
46b6eede11SEric Miao 	{ SPITZ_GPIO_ADC_TEMP_ON, GPIOF_OUT_INIT_LOW, "ADC Temp On" },
47b6eede11SEric Miao 	{ SPITZ_GPIO_JK_B,	  GPIOF_OUT_INIT_LOW, "JK B" },
48b6eede11SEric Miao 	{ SPITZ_GPIO_CHRG_ON,	  GPIOF_OUT_INIT_LOW, "Charger On" },
49b6eede11SEric Miao };
50b6eede11SEric Miao 
51e8b6f7f4SRichard Purdie static void spitz_charger_init(void)
52e8b6f7f4SRichard Purdie {
53b6eede11SEric Miao 	gpio_request_array(ARRAY_AND_SIZE(spitz_charger_gpios));
54e8b6f7f4SRichard Purdie }
55e8b6f7f4SRichard Purdie 
56e8b6f7f4SRichard Purdie static void spitz_measure_temp(int on)
57e8b6f7f4SRichard Purdie {
58fff14720SEric Miao 	gpio_set_value(SPITZ_GPIO_ADC_TEMP_ON, on);
59e8b6f7f4SRichard Purdie }
60e8b6f7f4SRichard Purdie 
61e8b6f7f4SRichard Purdie static void spitz_charge(int on)
62e8b6f7f4SRichard Purdie {
63e8b6f7f4SRichard Purdie 	if (on) {
64e8b6f7f4SRichard Purdie 		if (sharpsl_pm.flags & SHARPSL_SUSPENDED) {
65fff14720SEric Miao 			gpio_set_value(SPITZ_GPIO_JK_B, 1);
66fff14720SEric Miao 			gpio_set_value(SPITZ_GPIO_CHRG_ON, 0);
67e8b6f7f4SRichard Purdie 		} else {
68fff14720SEric Miao 			gpio_set_value(SPITZ_GPIO_JK_B, 0);
69fff14720SEric Miao 			gpio_set_value(SPITZ_GPIO_CHRG_ON, 0);
70e8b6f7f4SRichard Purdie 		}
71e8b6f7f4SRichard Purdie 	} else {
72fff14720SEric Miao 		gpio_set_value(SPITZ_GPIO_JK_B, 0);
73fff14720SEric Miao 		gpio_set_value(SPITZ_GPIO_CHRG_ON, 1);
74e8b6f7f4SRichard Purdie 	}
75e8b6f7f4SRichard Purdie }
76e8b6f7f4SRichard Purdie 
77e8b6f7f4SRichard Purdie static void spitz_discharge(int on)
78e8b6f7f4SRichard Purdie {
79fff14720SEric Miao 	gpio_set_value(SPITZ_GPIO_JK_A, on);
80e8b6f7f4SRichard Purdie }
81e8b6f7f4SRichard Purdie 
82e8b6f7f4SRichard Purdie /* HACK - For unknown reasons, accurate voltage readings are only made with a load
83e8b6f7f4SRichard Purdie    on the power bus which the green led on spitz provides */
84e8b6f7f4SRichard Purdie static void spitz_discharge1(int on)
85e8b6f7f4SRichard Purdie {
86fff14720SEric Miao 	gpio_set_value(SPITZ_GPIO_LED_GREEN, on);
87e8b6f7f4SRichard Purdie }
88e8b6f7f4SRichard Purdie 
89b6eede11SEric Miao static unsigned long gpio18_config[] = {
90b6eede11SEric Miao 	GPIO18_RDY,
91b6eede11SEric Miao 	GPIO18_GPIO,
92b6eede11SEric Miao };
93b6eede11SEric Miao 
94e8b6f7f4SRichard Purdie static void spitz_presuspend(void)
95e8b6f7f4SRichard Purdie {
96b7557de4SRichard Purdie 	spitz_last_ac_status = sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN);
97e8b6f7f4SRichard Purdie 
98e8b6f7f4SRichard Purdie 	/* GPIO Sleep Register */
99e8b6f7f4SRichard Purdie 	PGSR0 = 0x00144018;
100e8b6f7f4SRichard Purdie 	PGSR1 = 0x00EF0000;
101e8b6f7f4SRichard Purdie 	if (machine_is_akita()) {
102e8b6f7f4SRichard Purdie 		PGSR2 = 0x2121C000;
103e8b6f7f4SRichard Purdie 		PGSR3 = 0x00600400;
104e8b6f7f4SRichard Purdie 	} else {
105e8b6f7f4SRichard Purdie 		PGSR2 = 0x0121C000;
106e8b6f7f4SRichard Purdie 		PGSR3 = 0x00600000;
107e8b6f7f4SRichard Purdie 	}
108e8b6f7f4SRichard Purdie 
109e8b6f7f4SRichard Purdie 	PGSR0 &= ~SPITZ_GPIO_G0_STROBE_BIT;
110e8b6f7f4SRichard Purdie 	PGSR1 &= ~SPITZ_GPIO_G1_STROBE_BIT;
111e8b6f7f4SRichard Purdie 	PGSR2 &= ~SPITZ_GPIO_G2_STROBE_BIT;
112e8b6f7f4SRichard Purdie 	PGSR3 &= ~SPITZ_GPIO_G3_STROBE_BIT;
113e8b6f7f4SRichard Purdie 	PGSR2 |= GPIO_bit(SPITZ_GPIO_KEY_STROBE0);
114e8b6f7f4SRichard Purdie 
115b6eede11SEric Miao 	pxa2xx_mfp_config(&gpio18_config[0], 1);
116b6eede11SEric Miao 	gpio_request_one(18, GPIOF_OUT_INIT_HIGH, "Unknown");
117b6eede11SEric Miao 	gpio_free(18);
118e8b6f7f4SRichard Purdie 
119e8b6f7f4SRichard Purdie 	PRER = GPIO_bit(SPITZ_GPIO_KEY_INT);
120e8b6f7f4SRichard Purdie 	PFER = GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(SPITZ_GPIO_RESET);
121e8b6f7f4SRichard Purdie 	PWER = GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(SPITZ_GPIO_RESET) | PWER_RTC;
122e8b6f7f4SRichard Purdie 	PKWR = GPIO_bit(SPITZ_GPIO_SYNC) | GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(SPITZ_GPIO_RESET);
123b64b0b76SPavel Machek 	PKSR = 0xffffffff; /* clear */
124e8b6f7f4SRichard Purdie 
125e8b6f7f4SRichard Purdie 	/* nRESET_OUT Disable */
126e8b6f7f4SRichard Purdie 	PSLR |= PSLR_SL_ROD;
127e8b6f7f4SRichard Purdie 
128e8b6f7f4SRichard Purdie 	/* Stop 3.6MHz and drive HIGH to PCMCIA and CS */
129e8b6f7f4SRichard Purdie 	PCFR = PCFR_GPR_EN | PCFR_OPDE;
130e8b6f7f4SRichard Purdie }
131e8b6f7f4SRichard Purdie 
132e8b6f7f4SRichard Purdie static void spitz_postsuspend(void)
133e8b6f7f4SRichard Purdie {
134b6eede11SEric Miao 	pxa2xx_mfp_config(&gpio18_config[1], 1);
135e8b6f7f4SRichard Purdie }
136e8b6f7f4SRichard Purdie 
137e8b6f7f4SRichard Purdie static int spitz_should_wakeup(unsigned int resume_on_alarm)
138e8b6f7f4SRichard Purdie {
139e8b6f7f4SRichard Purdie 	int is_resume = 0;
140b7557de4SRichard Purdie 	int acin = sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN);
141e8b6f7f4SRichard Purdie 
142e8b6f7f4SRichard Purdie 	if (spitz_last_ac_status != acin) {
143e8b6f7f4SRichard Purdie 		if (acin) {
144e8b6f7f4SRichard Purdie 			/* charge on */
145e8b6f7f4SRichard Purdie 			sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG;
146e8b6f7f4SRichard Purdie 			dev_dbg(sharpsl_pm.dev, "AC Inserted\n");
147e8b6f7f4SRichard Purdie 		} else {
148e8b6f7f4SRichard Purdie 			/* charge off */
149e8b6f7f4SRichard Purdie 			dev_dbg(sharpsl_pm.dev, "AC Removed\n");
150b7557de4SRichard Purdie 			sharpsl_pm_led(SHARPSL_LED_OFF);
151b7557de4SRichard Purdie 			sharpsl_pm.machinfo->charge(0);
152e8b6f7f4SRichard Purdie 			sharpsl_pm.charge_mode = CHRG_OFF;
153e8b6f7f4SRichard Purdie 		}
154e8b6f7f4SRichard Purdie 		spitz_last_ac_status = acin;
155e8b6f7f4SRichard Purdie 		/* Return to suspend as this must be what we were woken for */
156e8b6f7f4SRichard Purdie 		return 0;
157e8b6f7f4SRichard Purdie 	}
158e8b6f7f4SRichard Purdie 
159e8b6f7f4SRichard Purdie 	if (PEDR & GPIO_bit(SPITZ_GPIO_KEY_INT))
160e8b6f7f4SRichard Purdie 		is_resume |= GPIO_bit(SPITZ_GPIO_KEY_INT);
161e8b6f7f4SRichard Purdie 
162e8b6f7f4SRichard Purdie 	if (PKSR & GPIO_bit(SPITZ_GPIO_SYNC))
163e8b6f7f4SRichard Purdie 		is_resume |= GPIO_bit(SPITZ_GPIO_SYNC);
164e8b6f7f4SRichard Purdie 
165e8b6f7f4SRichard Purdie 	if (resume_on_alarm && (PEDR & PWER_RTC))
166e8b6f7f4SRichard Purdie 		is_resume |= PWER_RTC;
167e8b6f7f4SRichard Purdie 
168e8b6f7f4SRichard Purdie 	dev_dbg(sharpsl_pm.dev, "is_resume: %x\n", is_resume);
169e8b6f7f4SRichard Purdie 	return is_resume;
170e8b6f7f4SRichard Purdie }
171e8b6f7f4SRichard Purdie 
172e8b6f7f4SRichard Purdie static unsigned long spitz_charger_wakeup(void)
173e8b6f7f4SRichard Purdie {
1749bf448c6SHaojian Zhuang 	unsigned long ret;
1759bf448c6SHaojian Zhuang 	ret = (!gpio_get_value(SPITZ_GPIO_KEY_INT)
1769bf448c6SHaojian Zhuang 		<< GPIO_bit(SPITZ_GPIO_KEY_INT))
1779bf448c6SHaojian Zhuang 		| (!gpio_get_value(SPITZ_GPIO_SYNC)
1789bf448c6SHaojian Zhuang 		<< GPIO_bit(SPITZ_GPIO_SYNC));
1799bf448c6SHaojian Zhuang 	return ret;
180e8b6f7f4SRichard Purdie }
181e8b6f7f4SRichard Purdie 
182b7557de4SRichard Purdie unsigned long spitzpm_read_devdata(int type)
183e8b6f7f4SRichard Purdie {
184b7557de4SRichard Purdie 	switch (type) {
185b7557de4SRichard Purdie 	case SHARPSL_STATUS_ACIN:
1869bf448c6SHaojian Zhuang 		return !gpio_get_value(SPITZ_GPIO_AC_IN);
187b7557de4SRichard Purdie 	case SHARPSL_STATUS_LOCK:
18805732d7eSEric Miao 		return gpio_get_value(sharpsl_pm.machinfo->gpio_batlock);
189b7557de4SRichard Purdie 	case SHARPSL_STATUS_CHRGFULL:
19005732d7eSEric Miao 		return gpio_get_value(sharpsl_pm.machinfo->gpio_batfull);
191b7557de4SRichard Purdie 	case SHARPSL_STATUS_FATAL:
19205732d7eSEric Miao 		return gpio_get_value(sharpsl_pm.machinfo->gpio_fatal);
193b7557de4SRichard Purdie 	case SHARPSL_ACIN_VOLT:
194b7557de4SRichard Purdie 		return sharpsl_pm_pxa_read_max1111(MAX1111_ACIN_VOLT);
195b7557de4SRichard Purdie 	case SHARPSL_BATT_TEMP:
196b7557de4SRichard Purdie 		return sharpsl_pm_pxa_read_max1111(MAX1111_BATT_TEMP);
197b7557de4SRichard Purdie 	case SHARPSL_BATT_VOLT:
198b7557de4SRichard Purdie 	default:
199b7557de4SRichard Purdie 		return sharpsl_pm_pxa_read_max1111(MAX1111_BATT_VOLT);
200b7557de4SRichard Purdie 	}
201e8b6f7f4SRichard Purdie }
202e8b6f7f4SRichard Purdie 
203e8b6f7f4SRichard Purdie struct sharpsl_charger_machinfo spitz_pm_machinfo = {
204e8b6f7f4SRichard Purdie 	.init             = spitz_charger_init,
205d48898a3SDmitry Eremin-Solenikov 	.exit             = NULL,
206e8b6f7f4SRichard Purdie 	.gpio_batlock     = SPITZ_GPIO_BAT_COVER,
207e8b6f7f4SRichard Purdie 	.gpio_acin        = SPITZ_GPIO_AC_IN,
208e8b6f7f4SRichard Purdie 	.gpio_batfull     = SPITZ_GPIO_CHRG_FULL,
209f8703dc8SRichard Purdie 	.batfull_irq	  = 1,
210e8b6f7f4SRichard Purdie 	.gpio_fatal       = SPITZ_GPIO_FATAL_BAT,
211e8b6f7f4SRichard Purdie 	.discharge        = spitz_discharge,
212e8b6f7f4SRichard Purdie 	.discharge1       = spitz_discharge1,
213e8b6f7f4SRichard Purdie 	.charge           = spitz_charge,
214e8b6f7f4SRichard Purdie 	.measure_temp     = spitz_measure_temp,
215e8b6f7f4SRichard Purdie 	.presuspend       = spitz_presuspend,
216e8b6f7f4SRichard Purdie 	.postsuspend      = spitz_postsuspend,
217b7557de4SRichard Purdie 	.read_devdata     = spitzpm_read_devdata,
218e8b6f7f4SRichard Purdie 	.charger_wakeup   = spitz_charger_wakeup,
219e8b6f7f4SRichard Purdie 	.should_wakeup    = spitz_should_wakeup,
2205cbff960SDmitry Baryshkov #if defined(CONFIG_LCD_CORGI)
2215cbff960SDmitry Baryshkov 	.backlight_limit = corgi_lcd_limit_intensity,
22202a8e769SRussell King #endif
223f8703dc8SRichard Purdie 	.charge_on_volt	  = SHARPSL_CHARGE_ON_VOLT,
224f8703dc8SRichard Purdie 	.charge_on_temp	  = SHARPSL_CHARGE_ON_TEMP,
225f8703dc8SRichard Purdie 	.charge_acin_high = SHARPSL_CHARGE_ON_ACIN_HIGH,
226f8703dc8SRichard Purdie 	.charge_acin_low  = SHARPSL_CHARGE_ON_ACIN_LOW,
227f8703dc8SRichard Purdie 	.fatal_acin_volt  = SHARPSL_FATAL_ACIN_VOLT,
228f8703dc8SRichard Purdie 	.fatal_noacin_volt= SHARPSL_FATAL_NOACIN_VOLT,
229e8b6f7f4SRichard Purdie 	.bat_levels       = 40,
2300ba01ebcSPavel Machek 	.bat_levels_noac  = sharpsl_battery_levels_noac,
2310ba01ebcSPavel Machek 	.bat_levels_acin  = sharpsl_battery_levels_acin,
232e8b6f7f4SRichard Purdie 	.status_high_acin = 188,
233e8b6f7f4SRichard Purdie 	.status_low_acin  = 178,
234e8b6f7f4SRichard Purdie 	.status_high_noac = 185,
235e8b6f7f4SRichard Purdie 	.status_low_noac  = 175,
236e8b6f7f4SRichard Purdie };
237e8b6f7f4SRichard Purdie 
238e8b6f7f4SRichard Purdie static struct platform_device *spitzpm_device;
239e8b6f7f4SRichard Purdie 
240e8b6f7f4SRichard Purdie static int __devinit spitzpm_init(void)
241e8b6f7f4SRichard Purdie {
242e8b6f7f4SRichard Purdie 	int ret;
243e8b6f7f4SRichard Purdie 
2445806d8abSDmitry Baryshkov 	if (!machine_is_spitz() && !machine_is_akita()
2455806d8abSDmitry Baryshkov 			&& !machine_is_borzoi())
2465806d8abSDmitry Baryshkov 		return -ENODEV;
2475806d8abSDmitry Baryshkov 
248e8b6f7f4SRichard Purdie 	spitzpm_device = platform_device_alloc("sharpsl-pm", -1);
249e8b6f7f4SRichard Purdie 	if (!spitzpm_device)
250e8b6f7f4SRichard Purdie 		return -ENOMEM;
251e8b6f7f4SRichard Purdie 
252e8b6f7f4SRichard Purdie 	spitzpm_device->dev.platform_data = &spitz_pm_machinfo;
253e8b6f7f4SRichard Purdie 	ret = platform_device_add(spitzpm_device);
254e8b6f7f4SRichard Purdie 
255e8b6f7f4SRichard Purdie 	if (ret)
256e8b6f7f4SRichard Purdie 		platform_device_put(spitzpm_device);
257e8b6f7f4SRichard Purdie 
258e8b6f7f4SRichard Purdie 	return ret;
259e8b6f7f4SRichard Purdie }
260e8b6f7f4SRichard Purdie 
261e8b6f7f4SRichard Purdie static void spitzpm_exit(void)
262e8b6f7f4SRichard Purdie {
263e8b6f7f4SRichard Purdie 	platform_device_unregister(spitzpm_device);
264e8b6f7f4SRichard Purdie }
265e8b6f7f4SRichard Purdie 
266e8b6f7f4SRichard Purdie module_init(spitzpm_init);
267e8b6f7f4SRichard Purdie module_exit(spitzpm_exit);
268