Merge branch 'master' of /home/sam/kernel/linux-2.6/
[linux-2.6] / arch / arm / mach-sa1100 / collie_pm.c
1 /*
2  * Based on spitz_pm.c and sharp code.
3  *
4  * Copyright (C) 2001  SHARP
5  * Copyright 2005 Pavel Machek <pavel@suse.cz>
6  *
7  * Distribute under GPLv2.
8  *
9  * Li-ion batteries are angry beasts, and they like to explode. This driver is not finished,
10  * and sometimes charges them when it should not. If it makes angry lithium to come your way...
11  * ...well, you have been warned.
12  */
13
14 #include <linux/module.h>
15 #include <linux/stat.h>
16 #include <linux/init.h>
17 #include <linux/kernel.h>
18 #include <linux/delay.h>
19 #include <linux/interrupt.h>
20 #include <linux/device.h>
21 #include <linux/platform_device.h>
22
23 #include <asm/irq.h>
24 #include <asm/mach-types.h>
25 #include <asm/hardware.h>
26 #include <asm/hardware/scoop.h>
27 #include <asm/dma.h>
28 #include <asm/arch/collie.h>
29 #include <asm/mach/sharpsl_param.h>
30 #include <asm/hardware/sharpsl_pm.h>
31
32 #include "../drivers/mfd/ucb1x00.h"
33
34 static struct ucb1x00 *ucb;
35 static int ad_revise;
36
37 #define ADCtoPower(x)          ((330 * x * 2) / 1024)
38
39 static void collie_charger_init(void)
40 {
41         int err;
42
43         if (sharpsl_param.adadj != -1) {
44                 ad_revise = sharpsl_param.adadj;
45         }
46
47         /* Register interrupt handler. */
48         if ((err = request_irq(COLLIE_IRQ_GPIO_AC_IN, sharpsl_ac_isr, IRQF_DISABLED,
49                                "ACIN", sharpsl_ac_isr))) {
50                 printk("Could not get irq %d.\n", COLLIE_IRQ_GPIO_AC_IN);
51                 return;
52         }
53         if ((err = request_irq(COLLIE_IRQ_GPIO_CO, sharpsl_chrg_full_isr, IRQF_DISABLED,
54                                "CO", sharpsl_chrg_full_isr))) {
55                 free_irq(COLLIE_IRQ_GPIO_AC_IN, sharpsl_ac_isr);
56                 printk("Could not get irq %d.\n", COLLIE_IRQ_GPIO_CO);
57                 return;
58         }
59
60         ucb1x00_io_set_dir(ucb, 0, COLLIE_TC35143_GPIO_MBAT_ON | COLLIE_TC35143_GPIO_TMP_ON |
61                                    COLLIE_TC35143_GPIO_BBAT_ON);
62         return;
63 }
64
65 static void collie_measure_temp(int on)
66 {
67         if (on)
68                 ucb1x00_io_write(ucb, COLLIE_TC35143_GPIO_TMP_ON, 0);
69         else
70                 ucb1x00_io_write(ucb, 0, COLLIE_TC35143_GPIO_TMP_ON);
71 }
72
73 static void collie_charge(int on)
74 {
75         if (on) {
76                 printk("Should start charger\n");
77         } else {
78                 printk("Should stop charger\n");
79         }
80 #ifdef I_AM_SURE
81
82         /* Zaurus seems to contain LTC1731 ; it should know when to
83          * stop charging itself, so setting charge on should be
84          * relatively harmless (as long as it is not done too often).
85          */
86 #define CF_BUF_CTRL_BASE 0xF0800000
87 #define        SCOOP_REG(adr) (*(volatile unsigned short*)(CF_BUF_CTRL_BASE+(adr)))
88 #define        SCOOP_REG_GPWR    SCOOP_REG(SCOOP_GPWR)
89
90         if (on) {
91                 set_scoop_gpio(&colliescoop_device.dev, COLLIE_SCP_CHARGE_ON);
92         } else {
93                 reset_scoop_gpio(&colliescoop_device.dev, COLLIE_SCP_CHARGE_ON);
94         }
95 #endif
96 }
97
98 static void collie_discharge(int on)
99 {
100 }
101
102 static void collie_discharge1(int on)
103 {
104 }
105
106 static void collie_presuspend(void)
107 {
108 }
109
110 static void collie_postsuspend(void)
111 {
112 }
113
114 static int collie_should_wakeup(unsigned int resume_on_alarm)
115 {
116         return 0;
117 }
118
119 static unsigned long collie_charger_wakeup(void)
120 {
121         return 0;
122 }
123
124 int collie_read_backup_battery(void)
125 {
126         int voltage;
127
128         ucb1x00_adc_enable(ucb);
129
130         /* Gives 75..130 */
131         ucb1x00_io_write(ucb, COLLIE_TC35143_GPIO_BBAT_ON, 0);
132         voltage = ucb1x00_adc_read(ucb, UCB_ADC_INP_AD1, UCB_SYNC);
133
134         ucb1x00_io_write(ucb, 0, COLLIE_TC35143_GPIO_BBAT_ON);
135         ucb1x00_adc_disable(ucb);
136
137         printk("Backup battery = %d(%d)\n", ADCtoPower(voltage), voltage);
138
139         return ADCtoPower(voltage);
140 }
141
142 int collie_read_main_battery(void)
143 {
144         int voltage, voltage_rev, voltage_volts;
145
146         ucb1x00_adc_enable(ucb);
147         ucb1x00_io_write(ucb, 0, COLLIE_TC35143_GPIO_BBAT_ON);
148         ucb1x00_io_write(ucb, COLLIE_TC35143_GPIO_MBAT_ON, 0);
149         /* gives values 160..255 with battery removed... and
150            145..255 with battery inserted. (on AC), goes as low as
151            80 on DC. */
152         voltage = ucb1x00_adc_read(ucb, UCB_ADC_INP_AD1, UCB_SYNC);
153
154         ucb1x00_io_write(ucb, 0, COLLIE_TC35143_GPIO_MBAT_ON);
155         ucb1x00_adc_disable(ucb);
156
157         voltage_rev = voltage + ((ad_revise * voltage) / 652);
158         voltage_volts = ADCtoPower(voltage_rev);
159
160         printk("Main battery = %d(%d)\n", voltage_volts, voltage);
161
162         if (voltage != -1)
163                 return voltage_volts;
164         else
165                 return voltage;
166 }
167
168 int collie_read_temp(void)
169 {
170         int voltage;
171
172         /* According to Sharp, temp must be > 973, main battery must be < 465,
173            FIXME: sharpsl_pm.c has both conditions negated? FIXME: values
174            are way out of range? */
175
176         ucb1x00_adc_enable(ucb);
177         ucb1x00_io_write(ucb, COLLIE_TC35143_GPIO_TMP_ON, 0);
178         /* >1010 = battery removed, 460 = 22C ?, higer = lower temp ? */
179         voltage = ucb1x00_adc_read(ucb, UCB_ADC_INP_AD0, UCB_SYNC);
180         ucb1x00_io_write(ucb, 0, COLLIE_TC35143_GPIO_TMP_ON);
181         ucb1x00_adc_disable(ucb);
182
183         printk("Battery temp = %d\n", voltage);
184         return voltage;
185 }
186
187 static unsigned long read_devdata(int which)
188 {
189         switch (which) {
190         case SHARPSL_BATT_VOLT:
191                 return collie_read_main_battery();
192         case SHARPSL_BATT_TEMP:
193                 return collie_read_temp();
194         case SHARPSL_ACIN_VOLT:
195                 return 0x1;
196         case SHARPSL_STATUS_ACIN: {
197                 int ret = GPLR & COLLIE_GPIO_AC_IN;
198                 printk("AC status = %d\n", ret);
199                 return ret;
200         }
201         case SHARPSL_STATUS_FATAL: {
202                 int ret = GPLR & COLLIE_GPIO_MAIN_BAT_LOW;
203                 printk("Fatal bat = %d\n", ret);
204                 return ret;
205         }
206         default:
207                 return ~0;
208         }
209 }
210
211 struct battery_thresh collie_battery_levels[] = {
212         { 368, 100},
213         { 358,  25},
214         { 356,   5},
215         {   0,   0},
216 };
217
218 struct sharpsl_charger_machinfo collie_pm_machinfo = {
219         .init             = collie_charger_init,
220         .read_devdata     = read_devdata,
221         .discharge        = collie_discharge,
222         .discharge1       = collie_discharge1,
223         .charge           = collie_charge,
224         .measure_temp     = collie_measure_temp,
225         .presuspend       = collie_presuspend,
226         .postsuspend      = collie_postsuspend,
227         .charger_wakeup   = collie_charger_wakeup,
228         .should_wakeup    = collie_should_wakeup,
229         .bat_levels       = 3,
230         .bat_levels_noac  = collie_battery_levels,
231         .bat_levels_acin  = collie_battery_levels,
232         .status_high_acin = 368,
233         .status_low_acin  = 358,
234         .status_high_noac = 368,
235         .status_low_noac  = 358,
236 };
237
238 static int __init collie_pm_ucb_add(struct ucb1x00_dev *pdev)
239 {
240         sharpsl_pm.machinfo = &collie_pm_machinfo;
241         ucb = pdev->ucb;
242         return 0;
243 }
244
245 static struct ucb1x00_driver collie_pm_ucb_driver = {
246         .add            = collie_pm_ucb_add,
247 };
248
249 static struct platform_device *collie_pm_device;
250
251 static int __init collie_pm_init(void)
252 {
253         int ret;
254
255         collie_pm_device = platform_device_alloc("sharpsl-pm", -1);
256         if (!collie_pm_device)
257                 return -ENOMEM;
258
259         collie_pm_device->dev.platform_data = &collie_pm_machinfo;
260         ret = platform_device_add(collie_pm_device);
261
262         if (ret)
263                 platform_device_put(collie_pm_device);
264
265         if (!ret)
266                 ret = ucb1x00_register_driver(&collie_pm_ucb_driver);
267
268         return ret;
269 }
270
271 static void __exit collie_pm_exit(void)
272 {
273         ucb1x00_unregister_driver(&collie_pm_ucb_driver);
274         platform_device_unregister(collie_pm_device);
275 }
276
277 module_init(collie_pm_init);
278 module_exit(collie_pm_exit);