diff options
author | Robert Jarzmik <rjarzmik@free.fr> | 2008-08-17 04:34:30 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2008-08-26 12:01:37 -0400 |
commit | eda6f6ecbbd6bea070ca296e40c2117d80117b76 (patch) | |
tree | ad01f1de3360287c300721f8930e2c3791969c3c /arch/arm/mach-pxa/mioa701.c | |
parent | b8e6c91c74e9f0279b7c51048779b3d62da60b88 (diff) |
[ARM] 5202/1: pxa: Added Mitac Mio A701 smartphone support.
The Mio A701 support features :
- GSM support
- Bluetooth support
- GPS support
- LED/Vibrator support
- Suspend/Resume support (very dependant on Mio FlashRam)
- Screen/Backlight support
- Keys/Keyboard support
- USB udc support
- MMC/SDIO support
- Flash Memory support
- Battery/AC plugin support
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-pxa/mioa701.c')
-rw-r--r-- | arch/arm/mach-pxa/mioa701.c | 905 |
1 files changed, 905 insertions, 0 deletions
diff --git a/arch/arm/mach-pxa/mioa701.c b/arch/arm/mach-pxa/mioa701.c new file mode 100644 index 000000000000..0842c531ee4d --- /dev/null +++ b/arch/arm/mach-pxa/mioa701.c | |||
@@ -0,0 +1,905 @@ | |||
1 | /* | ||
2 | * Handles the Mitac Mio A701 Board | ||
3 | * | ||
4 | * Copyright (C) 2008 Robert Jarzmik | ||
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 as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/sysdev.h> | ||
26 | #include <linux/input.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/gpio_keys.h> | ||
29 | #include <linux/pwm_backlight.h> | ||
30 | #include <linux/rtc.h> | ||
31 | #include <linux/leds.h> | ||
32 | #include <linux/gpio.h> | ||
33 | #include <linux/interrupt.h> | ||
34 | #include <linux/irq.h> | ||
35 | #include <linux/pda_power.h> | ||
36 | #include <linux/power_supply.h> | ||
37 | #include <linux/wm97xx.h> | ||
38 | #include <linux/mtd/physmap.h> | ||
39 | |||
40 | #include <asm/mach-types.h> | ||
41 | #include <asm/mach/arch.h> | ||
42 | #include <mach/mfp-pxa27x.h> | ||
43 | #include <mach/pxa27x_keypad.h> | ||
44 | #include <mach/pxafb.h> | ||
45 | #include <mach/pxa2xx-regs.h> | ||
46 | #include <mach/mmc.h> | ||
47 | #include <mach/udc.h> | ||
48 | #include <mach/pxa27x-udc.h> | ||
49 | |||
50 | #include <mach/mioa701.h> | ||
51 | |||
52 | #include "generic.h" | ||
53 | #include "devices.h" | ||
54 | |||
55 | static unsigned long mioa701_pin_config[] = { | ||
56 | /* Mio global */ | ||
57 | MIO_CFG_OUT(GPIO9_CHARGE_nEN, AF0, DRIVE_LOW), | ||
58 | MIO_CFG_OUT(GPIO18_POWEROFF, AF0, DRIVE_LOW), | ||
59 | MFP_CFG_OUT(GPIO3, AF0, DRIVE_HIGH), | ||
60 | MFP_CFG_OUT(GPIO4, AF0, DRIVE_HIGH), | ||
61 | |||
62 | /* Backlight PWM 0 */ | ||
63 | GPIO16_PWM0_OUT, | ||
64 | |||
65 | /* MMC */ | ||
66 | GPIO32_MMC_CLK, | ||
67 | GPIO92_MMC_DAT_0, | ||
68 | GPIO109_MMC_DAT_1, | ||
69 | GPIO110_MMC_DAT_2, | ||
70 | GPIO111_MMC_DAT_3, | ||
71 | GPIO112_MMC_CMD, | ||
72 | MIO_CFG_IN(GPIO78_SDIO_RO, AF0), | ||
73 | MIO_CFG_IN(GPIO15_SDIO_INSERT, AF0), | ||
74 | MIO_CFG_OUT(GPIO91_SDIO_EN, AF0, DRIVE_LOW), | ||
75 | |||
76 | /* USB */ | ||
77 | MIO_CFG_IN(GPIO13_USB_DETECT, AF0), | ||
78 | MIO_CFG_OUT(GPIO22_USB_ENABLE, AF0, DRIVE_LOW), | ||
79 | |||
80 | /* LCD */ | ||
81 | GPIO58_LCD_LDD_0, | ||
82 | GPIO59_LCD_LDD_1, | ||
83 | GPIO60_LCD_LDD_2, | ||
84 | GPIO61_LCD_LDD_3, | ||
85 | GPIO62_LCD_LDD_4, | ||
86 | GPIO63_LCD_LDD_5, | ||
87 | GPIO64_LCD_LDD_6, | ||
88 | GPIO65_LCD_LDD_7, | ||
89 | GPIO66_LCD_LDD_8, | ||
90 | GPIO67_LCD_LDD_9, | ||
91 | GPIO68_LCD_LDD_10, | ||
92 | GPIO69_LCD_LDD_11, | ||
93 | GPIO70_LCD_LDD_12, | ||
94 | GPIO71_LCD_LDD_13, | ||
95 | GPIO72_LCD_LDD_14, | ||
96 | GPIO73_LCD_LDD_15, | ||
97 | GPIO74_LCD_FCLK, | ||
98 | GPIO75_LCD_LCLK, | ||
99 | GPIO76_LCD_PCLK, | ||
100 | |||
101 | /* Bluetooth */ | ||
102 | GPIO44_BTUART_CTS, | ||
103 | GPIO42_BTUART_RXD, | ||
104 | GPIO45_BTUART_RTS, | ||
105 | GPIO43_BTUART_TXD, | ||
106 | MIO_CFG_OUT(GPIO83_BT_ON, AF0, DRIVE_LOW), | ||
107 | |||
108 | /* GPS */ | ||
109 | MIO_CFG_OUT(GPIO23_GPS_UNKNOWN1, AF0, DRIVE_LOW), | ||
110 | MIO_CFG_OUT(GPIO26_GPS_ON, AF0, DRIVE_LOW), | ||
111 | MIO_CFG_OUT(GPIO27_GPS_RESET, AF0, DRIVE_LOW), | ||
112 | MIO_CFG_OUT(GPIO106_GPS_UNKNOWN2, AF0, DRIVE_LOW), | ||
113 | MIO_CFG_OUT(GPIO107_GPS_UNKNOWN3, AF0, DRIVE_LOW), | ||
114 | GPIO46_STUART_RXD, | ||
115 | GPIO47_STUART_TXD, | ||
116 | |||
117 | /* GSM */ | ||
118 | MIO_CFG_OUT(GPIO24_GSM_MOD_RESET_CMD, AF0, DRIVE_LOW), | ||
119 | MIO_CFG_OUT(GPIO88_GSM_nMOD_ON_CMD, AF0, DRIVE_HIGH), | ||
120 | MIO_CFG_OUT(GPIO90_GSM_nMOD_OFF_CMD, AF0, DRIVE_HIGH), | ||
121 | MIO_CFG_OUT(GPIO114_GSM_nMOD_DTE_UART_STATE, AF0, DRIVE_HIGH), | ||
122 | MIO_CFG_IN(GPIO25_GSM_MOD_ON_STATE, AF0), | ||
123 | MIO_CFG_IN(GPIO113_GSM_EVENT, AF0) | WAKEUP_ON_EDGE_BOTH, | ||
124 | GPIO34_FFUART_RXD, | ||
125 | GPIO35_FFUART_CTS, | ||
126 | GPIO36_FFUART_DCD, | ||
127 | GPIO37_FFUART_DSR, | ||
128 | GPIO39_FFUART_TXD, | ||
129 | GPIO40_FFUART_DTR, | ||
130 | GPIO41_FFUART_RTS, | ||
131 | |||
132 | /* Sound */ | ||
133 | GPIO89_AC97_SYSCLK, | ||
134 | MIO_CFG_IN(GPIO12_HPJACK_INSERT, AF0), | ||
135 | |||
136 | /* Leds */ | ||
137 | MIO_CFG_OUT(GPIO10_LED_nCharging, AF0, DRIVE_HIGH), | ||
138 | MIO_CFG_OUT(GPIO97_LED_nBlue, AF0, DRIVE_HIGH), | ||
139 | MIO_CFG_OUT(GPIO98_LED_nOrange, AF0, DRIVE_HIGH), | ||
140 | MIO_CFG_OUT(GPIO82_LED_nVibra, AF0, DRIVE_HIGH), | ||
141 | MIO_CFG_OUT(GPIO115_LED_nKeyboard, AF0, DRIVE_HIGH), | ||
142 | |||
143 | /* Keyboard */ | ||
144 | MIO_CFG_IN(GPIO0_KEY_POWER, AF0) | WAKEUP_ON_EDGE_BOTH, | ||
145 | MIO_CFG_IN(GPIO93_KEY_VOLUME_UP, AF0), | ||
146 | MIO_CFG_IN(GPIO94_KEY_VOLUME_DOWN, AF0), | ||
147 | GPIO100_KP_MKIN_0, | ||
148 | GPIO101_KP_MKIN_1, | ||
149 | GPIO102_KP_MKIN_2, | ||
150 | GPIO103_KP_MKOUT_0, | ||
151 | GPIO104_KP_MKOUT_1, | ||
152 | GPIO105_KP_MKOUT_2, | ||
153 | |||
154 | /* Unknown */ | ||
155 | MFP_CFG_IN(GPIO14, AF0), | ||
156 | MFP_CFG_IN(GPIO20, AF0), | ||
157 | MFP_CFG_IN(GPIO21, AF0), | ||
158 | MFP_CFG_IN(GPIO33, AF0), | ||
159 | MFP_CFG_OUT(GPIO49, AF0, DRIVE_HIGH), | ||
160 | MFP_CFG_OUT(GPIO57, AF0, DRIVE_HIGH), | ||
161 | MFP_CFG_OUT(GPIO77, AF0, DRIVE_HIGH), | ||
162 | MFP_CFG_IN(GPIO80, AF0), | ||
163 | MFP_CFG_OUT(GPIO86, AF0, DRIVE_HIGH), | ||
164 | MFP_CFG_IN(GPIO96, AF0), | ||
165 | MFP_CFG_OUT(GPIO116, AF0, DRIVE_HIGH), | ||
166 | }; | ||
167 | |||
168 | #define MIO_GPIO_IN(num, _desc) \ | ||
169 | { .gpio = (num), .dir = 0, .desc = (_desc) } | ||
170 | #define MIO_GPIO_OUT(num, _init, _desc) \ | ||
171 | { .gpio = (num), .dir = 1, .init = (_init), .desc = (_desc) } | ||
172 | struct gpio_ress { | ||
173 | unsigned gpio : 8; | ||
174 | unsigned dir : 1; | ||
175 | unsigned init : 1; | ||
176 | char *desc; | ||
177 | }; | ||
178 | |||
179 | static int mio_gpio_request(struct gpio_ress *gpios, int size) | ||
180 | { | ||
181 | int i, rc = 0; | ||
182 | int gpio; | ||
183 | int dir; | ||
184 | |||
185 | for (i = 0; (!rc) && (i < size); i++) { | ||
186 | gpio = gpios[i].gpio; | ||
187 | dir = gpios[i].dir; | ||
188 | rc = gpio_request(gpio, gpios[i].desc); | ||
189 | if (rc) { | ||
190 | printk(KERN_ERR "Error requesting GPIO %d(%s) : %d\n", | ||
191 | gpio, gpios[i].desc, rc); | ||
192 | continue; | ||
193 | } | ||
194 | if (dir) | ||
195 | gpio_direction_output(gpio, gpios[i].init); | ||
196 | else | ||
197 | gpio_direction_input(gpio); | ||
198 | } | ||
199 | while ((rc) && (--i >= 0)) | ||
200 | gpio_free(gpios[i].gpio); | ||
201 | return rc; | ||
202 | } | ||
203 | |||
204 | static void mio_gpio_free(struct gpio_ress *gpios, int size) | ||
205 | { | ||
206 | int i; | ||
207 | |||
208 | for (i = 0; i < size; i++) | ||
209 | gpio_free(gpios[i].gpio); | ||
210 | } | ||
211 | |||
212 | /* LCD Screen and Backlight */ | ||
213 | static struct platform_pwm_backlight_data mioa701_backlight_data = { | ||
214 | .pwm_id = 0, | ||
215 | .max_brightness = 100, | ||
216 | .dft_brightness = 50, | ||
217 | .pwm_period_ns = 4000 * 1024, /* Fl = 250kHz */ | ||
218 | }; | ||
219 | |||
220 | /* | ||
221 | * LTM0305A776C LCD panel timings | ||
222 | * | ||
223 | * see: | ||
224 | * - the LTM0305A776C datasheet, | ||
225 | * - and the PXA27x Programmers' manual | ||
226 | */ | ||
227 | static struct pxafb_mode_info mioa701_ltm0305a776c = { | ||
228 | .pixclock = 220000, /* CLK=4.545 MHz */ | ||
229 | .xres = 240, | ||
230 | .yres = 320, | ||
231 | .bpp = 16, | ||
232 | .hsync_len = 4, | ||
233 | .vsync_len = 2, | ||
234 | .left_margin = 6, | ||
235 | .right_margin = 4, | ||
236 | .upper_margin = 5, | ||
237 | .lower_margin = 3, | ||
238 | }; | ||
239 | |||
240 | static void mioa701_lcd_power(int on, struct fb_var_screeninfo *si) | ||
241 | { | ||
242 | gpio_set_value(GPIO87_LCD_POWER, on); | ||
243 | } | ||
244 | |||
245 | static struct pxafb_mach_info mioa701_pxafb_info = { | ||
246 | .modes = &mioa701_ltm0305a776c, | ||
247 | .num_modes = 1, | ||
248 | .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL, | ||
249 | .pxafb_lcd_power = mioa701_lcd_power, | ||
250 | }; | ||
251 | |||
252 | /* | ||
253 | * Keyboard configuration | ||
254 | */ | ||
255 | static unsigned int mioa701_matrix_keys[] = { | ||
256 | KEY(0, 0, KEY_UP), | ||
257 | KEY(0, 1, KEY_RIGHT), | ||
258 | KEY(0, 2, KEY_MEDIA), | ||
259 | KEY(1, 0, KEY_DOWN), | ||
260 | KEY(1, 1, KEY_ENTER), | ||
261 | KEY(1, 2, KEY_CONNECT), /* GPS key */ | ||
262 | KEY(2, 0, KEY_LEFT), | ||
263 | KEY(2, 1, KEY_PHONE), /* Phone Green key */ | ||
264 | KEY(2, 2, KEY_CAMERA) /* Camera key */ | ||
265 | }; | ||
266 | static struct pxa27x_keypad_platform_data mioa701_keypad_info = { | ||
267 | .matrix_key_rows = 3, | ||
268 | .matrix_key_cols = 3, | ||
269 | .matrix_key_map = mioa701_matrix_keys, | ||
270 | .matrix_key_map_size = ARRAY_SIZE(mioa701_matrix_keys), | ||
271 | }; | ||
272 | |||
273 | /* | ||
274 | * GPIO Key Configuration | ||
275 | */ | ||
276 | #define MIO_KEY(key, _gpio, _desc, _wakeup) \ | ||
277 | { .code = (key), .gpio = (_gpio), .active_low = 0, \ | ||
278 | .desc = (_desc), .type = EV_KEY, .wakeup = (_wakeup) } | ||
279 | static struct gpio_keys_button mioa701_button_table[] = { | ||
280 | MIO_KEY(KEY_EXIT, GPIO0_KEY_POWER, "Power button", 1), | ||
281 | MIO_KEY(KEY_VOLUMEUP, GPIO93_KEY_VOLUME_UP, "Volume up", 0), | ||
282 | MIO_KEY(KEY_VOLUMEDOWN, GPIO94_KEY_VOLUME_DOWN, "Volume down", 0), | ||
283 | MIO_KEY(KEY_HP, GPIO12_HPJACK_INSERT, "HP jack detect", 0) | ||
284 | }; | ||
285 | |||
286 | static struct gpio_keys_platform_data mioa701_gpio_keys_data = { | ||
287 | .buttons = mioa701_button_table, | ||
288 | .nbuttons = ARRAY_SIZE(mioa701_button_table), | ||
289 | }; | ||
290 | |||
291 | /* | ||
292 | * Leds and vibrator | ||
293 | */ | ||
294 | #define ONE_LED(_gpio, _name) \ | ||
295 | { .gpio = (_gpio), .name = (_name), .active_low = true } | ||
296 | static struct gpio_led gpio_leds[] = { | ||
297 | ONE_LED(GPIO10_LED_nCharging, "mioa701:charging"), | ||
298 | ONE_LED(GPIO97_LED_nBlue, "mioa701:blue"), | ||
299 | ONE_LED(GPIO98_LED_nOrange, "mioa701:orange"), | ||
300 | ONE_LED(GPIO82_LED_nVibra, "mioa701:vibra"), | ||
301 | ONE_LED(GPIO115_LED_nKeyboard, "mioa701:keyboard") | ||
302 | }; | ||
303 | |||
304 | static struct gpio_led_platform_data gpio_led_info = { | ||
305 | .leds = gpio_leds, | ||
306 | .num_leds = ARRAY_SIZE(gpio_leds), | ||
307 | }; | ||
308 | |||
309 | /* | ||
310 | * GSM Sagem XS200 chip | ||
311 | * | ||
312 | * GSM handling was purged from kernel. For history, this is the way to go : | ||
313 | * - init : GPIO24_GSM_MOD_RESET_CMD = 0, GPIO114_GSM_nMOD_DTE_UART_STATE = 1 | ||
314 | * GPIO88_GSM_nMOD_ON_CMD = 1, GPIO90_GSM_nMOD_OFF_CMD = 1 | ||
315 | * - reset : GPIO24_GSM_MOD_RESET_CMD = 1, msleep(100), | ||
316 | * GPIO24_GSM_MOD_RESET_CMD = 0 | ||
317 | * - turn on : GPIO88_GSM_nMOD_ON_CMD = 0, msleep(1000), | ||
318 | * GPIO88_GSM_nMOD_ON_CMD = 1 | ||
319 | * - turn off : GPIO90_GSM_nMOD_OFF_CMD = 0, msleep(1000), | ||
320 | * GPIO90_GSM_nMOD_OFF_CMD = 1 | ||
321 | */ | ||
322 | static int is_gsm_on(void) | ||
323 | { | ||
324 | int is_on; | ||
325 | |||
326 | is_on = !!gpio_get_value(GPIO25_GSM_MOD_ON_STATE); | ||
327 | return is_on; | ||
328 | } | ||
329 | |||
330 | irqreturn_t gsm_on_irq(int irq, void *p) | ||
331 | { | ||
332 | printk(KERN_DEBUG "Mioa701: GSM status changed to %s\n", | ||
333 | is_gsm_on() ? "on" : "off"); | ||
334 | return IRQ_HANDLED; | ||
335 | } | ||
336 | |||
337 | struct gpio_ress gsm_gpios[] = { | ||
338 | MIO_GPIO_IN(GPIO25_GSM_MOD_ON_STATE, "GSM state"), | ||
339 | MIO_GPIO_IN(GPIO113_GSM_EVENT, "GSM event"), | ||
340 | }; | ||
341 | |||
342 | static int __init gsm_init(void) | ||
343 | { | ||
344 | int rc; | ||
345 | |||
346 | rc = mio_gpio_request(ARRAY_AND_SIZE(gsm_gpios)); | ||
347 | if (rc) | ||
348 | goto err_gpio; | ||
349 | rc = request_irq(gpio_to_irq(GPIO25_GSM_MOD_ON_STATE), gsm_on_irq, | ||
350 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
351 | "GSM XS200 Power Irq", NULL); | ||
352 | if (rc) | ||
353 | goto err_irq; | ||
354 | |||
355 | gpio_set_wake(GPIO113_GSM_EVENT, 1); | ||
356 | return 0; | ||
357 | |||
358 | err_irq: | ||
359 | printk(KERN_ERR "Mioa701: Can't request GSM_ON irq\n"); | ||
360 | mio_gpio_free(ARRAY_AND_SIZE(gsm_gpios)); | ||
361 | err_gpio: | ||
362 | printk(KERN_ERR "Mioa701: gsm not available\n"); | ||
363 | return rc; | ||
364 | } | ||
365 | |||
366 | static void gsm_exit(void) | ||
367 | { | ||
368 | free_irq(gpio_to_irq(GPIO25_GSM_MOD_ON_STATE), NULL); | ||
369 | mio_gpio_free(ARRAY_AND_SIZE(gsm_gpios)); | ||
370 | } | ||
371 | |||
372 | /* | ||
373 | * Bluetooth BRF6150 chip | ||
374 | * | ||
375 | * BT handling was purged from kernel. For history, this is the way to go : | ||
376 | * - turn on : GPIO83_BT_ON = 1 | ||
377 | * - turn off : GPIO83_BT_ON = 0 | ||
378 | */ | ||
379 | |||
380 | /* | ||
381 | * GPS Sirf Star III chip | ||
382 | * | ||
383 | * GPS handling was purged from kernel. For history, this is the way to go : | ||
384 | * - init : GPIO23_GPS_UNKNOWN1 = 1, GPIO26_GPS_ON = 0, GPIO27_GPS_RESET = 0 | ||
385 | * GPIO106_GPS_UNKNOWN2 = 0, GPIO107_GPS_UNKNOWN3 = 0 | ||
386 | * - turn on : GPIO27_GPS_RESET = 1, GPIO26_GPS_ON = 1 | ||
387 | * - turn off : GPIO26_GPS_ON = 0, GPIO27_GPS_RESET = 0 | ||
388 | */ | ||
389 | |||
390 | /* | ||
391 | * USB UDC | ||
392 | */ | ||
393 | static void udc_power_command(int cmd) | ||
394 | { | ||
395 | switch (cmd) { | ||
396 | case PXA2XX_UDC_CMD_DISCONNECT: | ||
397 | gpio_set_value(GPIO22_USB_ENABLE, 0); | ||
398 | break; | ||
399 | case PXA2XX_UDC_CMD_CONNECT: | ||
400 | gpio_set_value(GPIO22_USB_ENABLE, 1); | ||
401 | break; | ||
402 | default: | ||
403 | printk(KERN_INFO "udc_control: unknown command (0x%x)!\n", cmd); | ||
404 | break; | ||
405 | } | ||
406 | } | ||
407 | |||
408 | static int is_usb_connected(void) | ||
409 | { | ||
410 | return !!gpio_get_value(GPIO13_USB_DETECT); | ||
411 | } | ||
412 | |||
413 | static struct pxa2xx_udc_mach_info mioa701_udc_info = { | ||
414 | .udc_is_connected = is_usb_connected, | ||
415 | .udc_command = udc_power_command, | ||
416 | }; | ||
417 | |||
418 | struct gpio_ress udc_gpios[] = { | ||
419 | MIO_GPIO_OUT(GPIO22_USB_ENABLE, 0, "USB Vbus enable") | ||
420 | }; | ||
421 | |||
422 | static int __init udc_init(void) | ||
423 | { | ||
424 | pxa_set_udc_info(&mioa701_udc_info); | ||
425 | return mio_gpio_request(ARRAY_AND_SIZE(udc_gpios)); | ||
426 | } | ||
427 | |||
428 | static void udc_exit(void) | ||
429 | { | ||
430 | mio_gpio_free(ARRAY_AND_SIZE(udc_gpios)); | ||
431 | } | ||
432 | |||
433 | /* | ||
434 | * SDIO/MMC Card controller | ||
435 | */ | ||
436 | static void mci_setpower(struct device *dev, unsigned int vdd) | ||
437 | { | ||
438 | struct pxamci_platform_data *p_d = dev->platform_data; | ||
439 | |||
440 | if ((1 << vdd) & p_d->ocr_mask) | ||
441 | gpio_set_value(GPIO91_SDIO_EN, 1); /* enable SDIO power */ | ||
442 | else | ||
443 | gpio_set_value(GPIO91_SDIO_EN, 0); /* disable SDIO power */ | ||
444 | } | ||
445 | |||
446 | static int mci_get_ro(struct device *dev) | ||
447 | { | ||
448 | return gpio_get_value(GPIO78_SDIO_RO); | ||
449 | } | ||
450 | |||
451 | struct gpio_ress mci_gpios[] = { | ||
452 | MIO_GPIO_IN(GPIO78_SDIO_RO, "SDIO readonly detect"), | ||
453 | MIO_GPIO_IN(GPIO15_SDIO_INSERT, "SDIO insertion detect"), | ||
454 | MIO_GPIO_OUT(GPIO91_SDIO_EN, 0, "SDIO power enable") | ||
455 | }; | ||
456 | |||
457 | static void mci_exit(struct device *dev, void *data) | ||
458 | { | ||
459 | mio_gpio_free(ARRAY_AND_SIZE(mci_gpios)); | ||
460 | free_irq(gpio_to_irq(GPIO15_SDIO_INSERT), data); | ||
461 | } | ||
462 | |||
463 | static struct pxamci_platform_data mioa701_mci_info; | ||
464 | |||
465 | /** | ||
466 | * The card detect interrupt isn't debounced so we delay it by 250ms | ||
467 | * to give the card a chance to fully insert/eject. | ||
468 | */ | ||
469 | static int mci_init(struct device *dev, irq_handler_t detect_int, void *data) | ||
470 | { | ||
471 | int rc; | ||
472 | int irq = gpio_to_irq(GPIO15_SDIO_INSERT); | ||
473 | |||
474 | rc = mio_gpio_request(ARRAY_AND_SIZE(mci_gpios)); | ||
475 | if (rc) | ||
476 | goto err_gpio; | ||
477 | /* enable RE/FE interrupt on card insertion and removal */ | ||
478 | rc = request_irq(irq, detect_int, | ||
479 | IRQF_DISABLED | IRQF_TRIGGER_RISING | | ||
480 | IRQF_TRIGGER_FALLING, | ||
481 | "MMC card detect", data); | ||
482 | if (rc) | ||
483 | goto err_irq; | ||
484 | |||
485 | mioa701_mci_info.detect_delay = msecs_to_jiffies(250); | ||
486 | return 0; | ||
487 | |||
488 | err_irq: | ||
489 | dev_err(dev, "mioa701_mci_init: MMC/SD:" | ||
490 | " can't request MMC card detect IRQ\n"); | ||
491 | mio_gpio_free(ARRAY_AND_SIZE(mci_gpios)); | ||
492 | err_gpio: | ||
493 | return rc; | ||
494 | } | ||
495 | |||
496 | static struct pxamci_platform_data mioa701_mci_info = { | ||
497 | .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, | ||
498 | .init = mci_init, | ||
499 | .get_ro = mci_get_ro, | ||
500 | .setpower = mci_setpower, | ||
501 | .exit = mci_exit, | ||
502 | }; | ||
503 | |||
504 | /* FlashRAM */ | ||
505 | static struct resource strataflash_resource = { | ||
506 | .start = PXA_CS0_PHYS, | ||
507 | .end = PXA_CS0_PHYS + SZ_64M - 1, | ||
508 | .flags = IORESOURCE_MEM, | ||
509 | }; | ||
510 | |||
511 | static struct physmap_flash_data strataflash_data = { | ||
512 | .width = 2, | ||
513 | /* .set_vpp = mioa701_set_vpp, */ | ||
514 | }; | ||
515 | |||
516 | static struct platform_device strataflash = { | ||
517 | .name = "physmap-flash", | ||
518 | .id = -1, | ||
519 | .resource = &strataflash_resource, | ||
520 | .num_resources = 1, | ||
521 | .dev = { | ||
522 | .platform_data = &strataflash_data, | ||
523 | }, | ||
524 | }; | ||
525 | |||
526 | /* | ||
527 | * Suspend/Resume bootstrap management | ||
528 | * | ||
529 | * MIO A701 reboot sequence is highly ROM dependant. From the one dissassembled, | ||
530 | * this sequence is as follows : | ||
531 | * - disables interrupts | ||
532 | * - initialize SDRAM (self refresh RAM into active RAM) | ||
533 | * - initialize GPIOs (depends on value at 0xa020b020) | ||
534 | * - initialize coprossessors | ||
535 | * - if edge detect on PWR_SCL(GPIO3), then proceed to cold start | ||
536 | * - or if value at 0xa020b000 not equal to 0x0f0f0f0f, proceed to cold start | ||
537 | * - else do a resume, ie. jump to addr 0xa0100000 | ||
538 | */ | ||
539 | #define RESUME_ENABLE_ADDR 0xa020b000 | ||
540 | #define RESUME_ENABLE_VAL 0x0f0f0f0f | ||
541 | #define RESUME_BT_ADDR 0xa020b020 | ||
542 | #define RESUME_UNKNOWN_ADDR 0xa020b024 | ||
543 | #define RESUME_VECTOR_ADDR 0xa0100000 | ||
544 | #define BOOTSTRAP_WORDS mioa701_bootstrap_lg/4 | ||
545 | |||
546 | static u32 *save_buffer; | ||
547 | |||
548 | static void install_bootstrap(void) | ||
549 | { | ||
550 | int i; | ||
551 | u32 *rom_bootstrap = phys_to_virt(RESUME_VECTOR_ADDR); | ||
552 | u32 *src = &mioa701_bootstrap; | ||
553 | |||
554 | for (i = 0; i < BOOTSTRAP_WORDS; i++) | ||
555 | rom_bootstrap[i] = src[i]; | ||
556 | } | ||
557 | |||
558 | |||
559 | static int mioa701_sys_suspend(struct sys_device *sysdev, pm_message_t state) | ||
560 | { | ||
561 | int i = 0, is_bt_on; | ||
562 | u32 *mem_resume_vector = phys_to_virt(RESUME_VECTOR_ADDR); | ||
563 | u32 *mem_resume_enabler = phys_to_virt(RESUME_ENABLE_ADDR); | ||
564 | u32 *mem_resume_bt = phys_to_virt(RESUME_BT_ADDR); | ||
565 | u32 *mem_resume_unknown = phys_to_virt(RESUME_UNKNOWN_ADDR); | ||
566 | |||
567 | /* Devices prepare suspend */ | ||
568 | is_bt_on = gpio_get_value(GPIO83_BT_ON); | ||
569 | pxa2xx_mfp_set_lpm(GPIO83_BT_ON, | ||
570 | is_bt_on ? MFP_LPM_DRIVE_HIGH : MFP_LPM_DRIVE_LOW); | ||
571 | |||
572 | for (i = 0; i < BOOTSTRAP_WORDS; i++) | ||
573 | save_buffer[i] = mem_resume_vector[i]; | ||
574 | save_buffer[i++] = *mem_resume_enabler; | ||
575 | save_buffer[i++] = *mem_resume_bt; | ||
576 | save_buffer[i++] = *mem_resume_unknown; | ||
577 | |||
578 | *mem_resume_enabler = RESUME_ENABLE_VAL; | ||
579 | *mem_resume_bt = is_bt_on; | ||
580 | |||
581 | install_bootstrap(); | ||
582 | return 0; | ||
583 | } | ||
584 | |||
585 | static int mioa701_sys_resume(struct sys_device *sysdev) | ||
586 | { | ||
587 | int i = 0; | ||
588 | u32 *mem_resume_vector = phys_to_virt(RESUME_VECTOR_ADDR); | ||
589 | u32 *mem_resume_enabler = phys_to_virt(RESUME_ENABLE_ADDR); | ||
590 | u32 *mem_resume_bt = phys_to_virt(RESUME_BT_ADDR); | ||
591 | u32 *mem_resume_unknown = phys_to_virt(RESUME_UNKNOWN_ADDR); | ||
592 | |||
593 | for (i = 0; i < BOOTSTRAP_WORDS; i++) | ||
594 | mem_resume_vector[i] = save_buffer[i]; | ||
595 | *mem_resume_enabler = save_buffer[i++]; | ||
596 | *mem_resume_bt = save_buffer[i++]; | ||
597 | *mem_resume_unknown = save_buffer[i++]; | ||
598 | |||
599 | return 0; | ||
600 | } | ||
601 | |||
602 | static struct sysdev_class mioa701_sysclass = { | ||
603 | .name = "mioa701", | ||
604 | }; | ||
605 | |||
606 | static struct sys_device sysdev_bootstrap = { | ||
607 | .cls = &mioa701_sysclass, | ||
608 | }; | ||
609 | |||
610 | static struct sysdev_driver driver_bootstrap = { | ||
611 | .suspend = &mioa701_sys_suspend, | ||
612 | .resume = &mioa701_sys_resume, | ||
613 | }; | ||
614 | |||
615 | static int __init bootstrap_init(void) | ||
616 | { | ||
617 | int rc; | ||
618 | int save_size = mioa701_bootstrap_lg + (sizeof(u32) * 3); | ||
619 | |||
620 | rc = sysdev_class_register(&mioa701_sysclass); | ||
621 | if (rc) { | ||
622 | printk(KERN_ERR "Failed registering mioa701 sys class\n"); | ||
623 | return -ENODEV; | ||
624 | } | ||
625 | rc = sysdev_register(&sysdev_bootstrap); | ||
626 | if (rc) { | ||
627 | printk(KERN_ERR "Failed registering mioa701 sys device\n"); | ||
628 | return -ENODEV; | ||
629 | } | ||
630 | rc = sysdev_driver_register(&mioa701_sysclass, &driver_bootstrap); | ||
631 | if (rc) { | ||
632 | printk(KERN_ERR "Failed registering PMU sys driver\n"); | ||
633 | return -ENODEV; | ||
634 | } | ||
635 | |||
636 | save_buffer = kmalloc(save_size, GFP_KERNEL); | ||
637 | if (!save_buffer) | ||
638 | return -ENOMEM; | ||
639 | printk(KERN_INFO "MioA701: allocated %d bytes for bootstrap\n", | ||
640 | save_size); | ||
641 | return 0; | ||
642 | } | ||
643 | |||
644 | static void bootstrap_exit(void) | ||
645 | { | ||
646 | kfree(save_buffer); | ||
647 | sysdev_driver_unregister(&mioa701_sysclass, &driver_bootstrap); | ||
648 | sysdev_unregister(&sysdev_bootstrap); | ||
649 | sysdev_class_unregister(&mioa701_sysclass); | ||
650 | |||
651 | printk(KERN_CRIT "Unregistering mioa701 suspend will hang next" | ||
652 | "resume !!!\n"); | ||
653 | } | ||
654 | |||
655 | /* | ||
656 | * Power Supply | ||
657 | */ | ||
658 | static char *supplicants[] = { | ||
659 | "mioa701_battery" | ||
660 | }; | ||
661 | |||
662 | static void mioa701_set_charge(int flags) | ||
663 | { | ||
664 | gpio_set_value(GPIO9_CHARGE_nEN, !flags); | ||
665 | } | ||
666 | |||
667 | static struct pda_power_pdata power_pdata = { | ||
668 | .is_ac_online = is_usb_connected, | ||
669 | .set_charge = mioa701_set_charge, | ||
670 | .supplied_to = supplicants, | ||
671 | .num_supplicants = ARRAY_SIZE(supplicants), | ||
672 | }; | ||
673 | |||
674 | static struct resource power_resources[] = { | ||
675 | [0] = { | ||
676 | .name = "ac", | ||
677 | .start = gpio_to_irq(GPIO13_USB_DETECT), | ||
678 | .end = gpio_to_irq(GPIO13_USB_DETECT), | ||
679 | .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | | ||
680 | IORESOURCE_IRQ_LOWEDGE, | ||
681 | }, | ||
682 | }; | ||
683 | |||
684 | static struct platform_device power_dev = { | ||
685 | .name = "pda-power", | ||
686 | .id = -1, | ||
687 | .resource = power_resources, | ||
688 | .num_resources = ARRAY_SIZE(power_resources), | ||
689 | .dev = { | ||
690 | .platform_data = &power_pdata, | ||
691 | }, | ||
692 | }; | ||
693 | |||
694 | #if defined(CONFIG_PDA_POWER) && defined(CONFIG_TOUCHSCREEN_WM97XX) | ||
695 | static struct wm97xx *battery_wm; | ||
696 | |||
697 | static enum power_supply_property battery_props[] = { | ||
698 | POWER_SUPPLY_PROP_STATUS, | ||
699 | POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, | ||
700 | POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, | ||
701 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
702 | POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, /* Necessary for apm */ | ||
703 | }; | ||
704 | |||
705 | static int get_battery_voltage(void) | ||
706 | { | ||
707 | int adc = -1; | ||
708 | |||
709 | if (battery_wm) | ||
710 | adc = wm97xx_read_aux_adc(battery_wm, WM97XX_AUX_ID1); | ||
711 | return adc; | ||
712 | } | ||
713 | |||
714 | static int get_battery_status(struct power_supply *b) | ||
715 | { | ||
716 | int status; | ||
717 | |||
718 | if (is_usb_connected()) | ||
719 | status = POWER_SUPPLY_STATUS_CHARGING; | ||
720 | else | ||
721 | status = POWER_SUPPLY_STATUS_DISCHARGING; | ||
722 | |||
723 | return status; | ||
724 | } | ||
725 | |||
726 | static int get_property(struct power_supply *b, | ||
727 | enum power_supply_property psp, | ||
728 | union power_supply_propval *val) | ||
729 | { | ||
730 | int rc = 0; | ||
731 | |||
732 | switch (psp) { | ||
733 | case POWER_SUPPLY_PROP_STATUS: | ||
734 | val->intval = get_battery_status(b); | ||
735 | break; | ||
736 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | ||
737 | val->intval = 0xfd0; | ||
738 | break; | ||
739 | case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: | ||
740 | val->intval = 0xc00; | ||
741 | break; | ||
742 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
743 | val->intval = get_battery_voltage(); | ||
744 | break; | ||
745 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | ||
746 | val->intval = 100; | ||
747 | break; | ||
748 | default: | ||
749 | val->intval = -1; | ||
750 | rc = -1; | ||
751 | } | ||
752 | |||
753 | return rc; | ||
754 | }; | ||
755 | |||
756 | static struct power_supply battery_ps = { | ||
757 | .name = "mioa701_battery", | ||
758 | .type = POWER_SUPPLY_TYPE_BATTERY, | ||
759 | .get_property = get_property, | ||
760 | .properties = battery_props, | ||
761 | .num_properties = ARRAY_SIZE(battery_props), | ||
762 | }; | ||
763 | |||
764 | static int battery_probe(struct platform_device *pdev) | ||
765 | { | ||
766 | struct wm97xx *wm = platform_get_drvdata(pdev); | ||
767 | int rc; | ||
768 | |||
769 | battery_wm = wm; | ||
770 | |||
771 | rc = power_supply_register(NULL, &battery_ps); | ||
772 | if (rc) | ||
773 | dev_err(&pdev->dev, | ||
774 | "Could not register mioa701 battery -> %d\n", rc); | ||
775 | return rc; | ||
776 | } | ||
777 | |||
778 | static int battery_remove(struct platform_device *pdev) | ||
779 | { | ||
780 | battery_wm = NULL; | ||
781 | return 0; | ||
782 | } | ||
783 | |||
784 | static struct platform_driver mioa701_battery_driver = { | ||
785 | .driver = { | ||
786 | .name = "wm97xx-battery", | ||
787 | }, | ||
788 | .probe = battery_probe, | ||
789 | .remove = battery_remove | ||
790 | }; | ||
791 | |||
792 | static int __init mioa701_battery_init(void) | ||
793 | { | ||
794 | int rc; | ||
795 | |||
796 | rc = platform_driver_register(&mioa701_battery_driver); | ||
797 | if (rc) | ||
798 | printk(KERN_ERR "Could not register mioa701 battery driver\n"); | ||
799 | return rc; | ||
800 | } | ||
801 | |||
802 | #else | ||
803 | static int __init mioa701_battery_init(void) | ||
804 | { | ||
805 | return 0; | ||
806 | } | ||
807 | #endif | ||
808 | |||
809 | /* | ||
810 | * Mio global | ||
811 | */ | ||
812 | |||
813 | /* Devices */ | ||
814 | #define MIO_PARENT_DEV(var, strname, tparent, pdata) \ | ||
815 | static struct platform_device var = { \ | ||
816 | .name = strname, \ | ||
817 | .id = -1, \ | ||
818 | .dev = { \ | ||
819 | .platform_data = pdata, \ | ||
820 | .parent = tparent, \ | ||
821 | }, \ | ||
822 | }; | ||
823 | #define MIO_SIMPLE_DEV(var, strname, pdata) \ | ||
824 | MIO_PARENT_DEV(var, strname, NULL, pdata) | ||
825 | |||
826 | MIO_SIMPLE_DEV(mioa701_gpio_keys, "gpio-keys", &mioa701_gpio_keys_data) | ||
827 | MIO_PARENT_DEV(mioa701_backlight, "pwm-backlight", &pxa27x_device_pwm0.dev, | ||
828 | &mioa701_backlight_data); | ||
829 | MIO_SIMPLE_DEV(mioa701_led, "leds-gpio", &gpio_led_info) | ||
830 | MIO_SIMPLE_DEV(pxa2xx_pcm, "pxa2xx-pcm", NULL) | ||
831 | MIO_SIMPLE_DEV(pxa2xx_ac97, "pxa2xx-ac97", NULL) | ||
832 | MIO_PARENT_DEV(mio_wm9713_codec, "wm9713-codec", &pxa2xx_ac97.dev, NULL) | ||
833 | MIO_SIMPLE_DEV(mioa701_sound, "mioa701-wm9713", NULL) | ||
834 | MIO_SIMPLE_DEV(mioa701_board, "mioa701-board", NULL) | ||
835 | |||
836 | static struct platform_device *devices[] __initdata = { | ||
837 | &mioa701_gpio_keys, | ||
838 | &mioa701_backlight, | ||
839 | &mioa701_led, | ||
840 | &pxa2xx_pcm, | ||
841 | &pxa2xx_ac97, | ||
842 | &mio_wm9713_codec, | ||
843 | &mioa701_sound, | ||
844 | &power_dev, | ||
845 | &strataflash, | ||
846 | &mioa701_board | ||
847 | }; | ||
848 | |||
849 | static void mioa701_machine_exit(void); | ||
850 | |||
851 | static void mioa701_poweroff(void) | ||
852 | { | ||
853 | mioa701_machine_exit(); | ||
854 | gpio_set_value(GPIO18_POWEROFF, 1); | ||
855 | } | ||
856 | |||
857 | static void mioa701_restart(char c) | ||
858 | { | ||
859 | mioa701_machine_exit(); | ||
860 | arm_machine_restart(c); | ||
861 | } | ||
862 | |||
863 | struct gpio_ress global_gpios[] = { | ||
864 | MIO_GPIO_OUT(GPIO9_CHARGE_nEN, 1, "Charger enable"), | ||
865 | MIO_GPIO_OUT(GPIO18_POWEROFF, 0, "Power Off"), | ||
866 | MIO_GPIO_OUT(GPIO87_LCD_POWER, 0, "LCD Power") | ||
867 | }; | ||
868 | |||
869 | static void __init mioa701_machine_init(void) | ||
870 | { | ||
871 | PSLR = 0xff100000; /* SYSDEL=125ms, PWRDEL=125ms, PSLR_SL_ROD=1 */ | ||
872 | PCFR = PCFR_DC_EN | PCFR_GPR_EN | PCFR_OPDE; | ||
873 | RTTR = 32768 - 1; /* Reset crazy WinCE value */ | ||
874 | UP2OCR = UP2OCR_HXOE; | ||
875 | |||
876 | pxa2xx_mfp_config(ARRAY_AND_SIZE(mioa701_pin_config)); | ||
877 | mio_gpio_request(ARRAY_AND_SIZE(global_gpios)); | ||
878 | bootstrap_init(); | ||
879 | set_pxa_fb_info(&mioa701_pxafb_info); | ||
880 | pxa_set_mci_info(&mioa701_mci_info); | ||
881 | pxa_set_keypad_info(&mioa701_keypad_info); | ||
882 | udc_init(); | ||
883 | pm_power_off = mioa701_poweroff; | ||
884 | arm_pm_restart = mioa701_restart; | ||
885 | platform_add_devices(devices, ARRAY_SIZE(devices)); | ||
886 | gsm_init(); | ||
887 | mioa701_battery_init(); | ||
888 | } | ||
889 | |||
890 | static void mioa701_machine_exit(void) | ||
891 | { | ||
892 | udc_exit(); | ||
893 | bootstrap_exit(); | ||
894 | gsm_exit(); | ||
895 | } | ||
896 | |||
897 | MACHINE_START(MIOA701, "MIO A701") | ||
898 | .phys_io = 0x40000000, | ||
899 | .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc, | ||
900 | .boot_params = 0xa0000100, | ||
901 | .map_io = &pxa_map_io, | ||
902 | .init_irq = &pxa27x_init_irq, | ||
903 | .init_machine = mioa701_machine_init, | ||
904 | .timer = &pxa_timer, | ||
905 | MACHINE_END | ||