From eda6f6ecbbd6bea070ca296e40c2117d80117b76 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sun, 17 Aug 2008 09:34:30 +0100 Subject: [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 Signed-off-by: Russell King --- arch/arm/mach-pxa/Kconfig | 12 + arch/arm/mach-pxa/Makefile | 1 + arch/arm/mach-pxa/include/mach/mioa701.h | 67 +++ arch/arm/mach-pxa/mioa701.c | 905 +++++++++++++++++++++++++++++++ arch/arm/mach-pxa/mioa701_bootresume.S | 36 ++ 5 files changed, 1021 insertions(+) create mode 100644 arch/arm/mach-pxa/include/mach/mioa701.h create mode 100644 arch/arm/mach-pxa/mioa701.c create mode 100644 arch/arm/mach-pxa/mioa701_bootresume.S (limited to 'arch/arm') diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig index e8ee7ec..9a38763 100644 --- a/arch/arm/mach-pxa/Kconfig +++ b/arch/arm/mach-pxa/Kconfig @@ -216,6 +216,18 @@ config MACH_MAGICIAN select PXA27x select IWMMXT +config MACH_MIOA701 + bool "Mitac Mio A701 Support" + select PXA27x + select IWMMXT + select LEDS_GPIO + select HAVE_PWM + select GPIO_SYSFS + help + Say Y here if you intend to run this kernel on a + MIO A701. Currently there is only basic support + for this PDA. + config MACH_PCM027 bool "Phytec phyCORE-PXA270 CPU module (PCM-027)" select PXA27x diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile index 99ecbe7..0e5fc7e 100644 --- a/arch/arm/mach-pxa/Makefile +++ b/arch/arm/mach-pxa/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_MACH_PCM990_BASEBOARD) += pcm990-baseboard.o obj-$(CONFIG_MACH_TOSA) += tosa.o obj-$(CONFIG_MACH_EM_X270) += em-x270.o obj-$(CONFIG_MACH_MAGICIAN) += magician.o +obj-$(CONFIG_MACH_MIOA701) += mioa701.o mioa701_bootresume.o obj-$(CONFIG_ARCH_PXA_ESERIES) += eseries.o eseries_udc.o obj-$(CONFIG_MACH_E740) += e740_lcd.o obj-$(CONFIG_MACH_E750) += e750_lcd.o diff --git a/arch/arm/mach-pxa/include/mach/mioa701.h b/arch/arm/mach-pxa/include/mach/mioa701.h new file mode 100644 index 0000000..8483cb5 --- /dev/null +++ b/arch/arm/mach-pxa/include/mach/mioa701.h @@ -0,0 +1,67 @@ +#ifndef _MIOA701_H_ +#define _MIOA701_H_ + +#define MIO_CFG_IN(pin, af) \ + ((MFP_CFG_DEFAULT & ~(MFP_AF_MASK | MFP_DIR_MASK)) |\ + (MFP_PIN(pin) | MFP_##af | MFP_DIR_IN)) + +#define MIO_CFG_OUT(pin, af, state) \ + ((MFP_CFG_DEFAULT & ~(MFP_AF_MASK | MFP_DIR_MASK | MFP_LPM_STATE_MASK)) |\ + (MFP_PIN(pin) | MFP_##af | MFP_DIR_OUT | MFP_LPM_##state)) + +/* Global GPIOs */ +#define GPIO9_CHARGE_nEN 9 +#define GPIO18_POWEROFF 18 +#define GPIO87_LCD_POWER 87 + +/* USB */ +#define GPIO13_USB_DETECT 13 +#define GPIO22_USB_ENABLE 22 + +/* SDIO bits */ +#define GPIO78_SDIO_RO 78 +#define GPIO15_SDIO_INSERT 15 +#define GPIO91_SDIO_EN 91 + +/* Bluetooth */ +#define GPIO83_BT_ON 83 + +/* GPS */ +#define GPIO23_GPS_UNKNOWN1 23 +#define GPIO26_GPS_ON 26 +#define GPIO27_GPS_RESET 27 +#define GPIO106_GPS_UNKNOWN2 106 +#define GPIO107_GPS_UNKNOWN3 107 + +/* GSM */ +#define GPIO24_GSM_MOD_RESET_CMD 24 +#define GPIO88_GSM_nMOD_ON_CMD 88 +#define GPIO90_GSM_nMOD_OFF_CMD 90 +#define GPIO114_GSM_nMOD_DTE_UART_STATE 114 +#define GPIO25_GSM_MOD_ON_STATE 25 +#define GPIO113_GSM_EVENT 113 + +/* SOUND */ +#define GPIO12_HPJACK_INSERT 12 + +/* LEDS */ +#define GPIO10_LED_nCharging 10 +#define GPIO97_LED_nBlue 97 +#define GPIO98_LED_nOrange 98 +#define GPIO82_LED_nVibra 82 +#define GPIO115_LED_nKeyboard 115 + +/* Keyboard */ +#define GPIO0_KEY_POWER 0 +#define GPIO93_KEY_VOLUME_UP 93 +#define GPIO94_KEY_VOLUME_DOWN 94 + +extern struct input_dev *mioa701_evdev; +extern void mioa701_gpio_lpm_set(unsigned long mfp_pin); + +/* Assembler externals mioa701_bootresume.S */ +extern u32 mioa701_bootstrap; +extern u32 mioa701_jumpaddr; +extern u32 mioa701_bootstrap_lg; + +#endif /* _MIOA701_H */ diff --git a/arch/arm/mach-pxa/mioa701.c b/arch/arm/mach-pxa/mioa701.c new file mode 100644 index 0000000..0842c53 --- /dev/null +++ b/arch/arm/mach-pxa/mioa701.c @@ -0,0 +1,905 @@ +/* + * Handles the Mitac Mio A701 Board + * + * Copyright (C) 2008 Robert Jarzmik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "generic.h" +#include "devices.h" + +static unsigned long mioa701_pin_config[] = { + /* Mio global */ + MIO_CFG_OUT(GPIO9_CHARGE_nEN, AF0, DRIVE_LOW), + MIO_CFG_OUT(GPIO18_POWEROFF, AF0, DRIVE_LOW), + MFP_CFG_OUT(GPIO3, AF0, DRIVE_HIGH), + MFP_CFG_OUT(GPIO4, AF0, DRIVE_HIGH), + + /* Backlight PWM 0 */ + GPIO16_PWM0_OUT, + + /* MMC */ + GPIO32_MMC_CLK, + GPIO92_MMC_DAT_0, + GPIO109_MMC_DAT_1, + GPIO110_MMC_DAT_2, + GPIO111_MMC_DAT_3, + GPIO112_MMC_CMD, + MIO_CFG_IN(GPIO78_SDIO_RO, AF0), + MIO_CFG_IN(GPIO15_SDIO_INSERT, AF0), + MIO_CFG_OUT(GPIO91_SDIO_EN, AF0, DRIVE_LOW), + + /* USB */ + MIO_CFG_IN(GPIO13_USB_DETECT, AF0), + MIO_CFG_OUT(GPIO22_USB_ENABLE, AF0, DRIVE_LOW), + + /* LCD */ + GPIO58_LCD_LDD_0, + GPIO59_LCD_LDD_1, + GPIO60_LCD_LDD_2, + GPIO61_LCD_LDD_3, + GPIO62_LCD_LDD_4, + GPIO63_LCD_LDD_5, + GPIO64_LCD_LDD_6, + GPIO65_LCD_LDD_7, + GPIO66_LCD_LDD_8, + GPIO67_LCD_LDD_9, + GPIO68_LCD_LDD_10, + GPIO69_LCD_LDD_11, + GPIO70_LCD_LDD_12, + GPIO71_LCD_LDD_13, + GPIO72_LCD_LDD_14, + GPIO73_LCD_LDD_15, + GPIO74_LCD_FCLK, + GPIO75_LCD_LCLK, + GPIO76_LCD_PCLK, + + /* Bluetooth */ + GPIO44_BTUART_CTS, + GPIO42_BTUART_RXD, + GPIO45_BTUART_RTS, + GPIO43_BTUART_TXD, + MIO_CFG_OUT(GPIO83_BT_ON, AF0, DRIVE_LOW), + + /* GPS */ + MIO_CFG_OUT(GPIO23_GPS_UNKNOWN1, AF0, DRIVE_LOW), + MIO_CFG_OUT(GPIO26_GPS_ON, AF0, DRIVE_LOW), + MIO_CFG_OUT(GPIO27_GPS_RESET, AF0, DRIVE_LOW), + MIO_CFG_OUT(GPIO106_GPS_UNKNOWN2, AF0, DRIVE_LOW), + MIO_CFG_OUT(GPIO107_GPS_UNKNOWN3, AF0, DRIVE_LOW), + GPIO46_STUART_RXD, + GPIO47_STUART_TXD, + + /* GSM */ + MIO_CFG_OUT(GPIO24_GSM_MOD_RESET_CMD, AF0, DRIVE_LOW), + MIO_CFG_OUT(GPIO88_GSM_nMOD_ON_CMD, AF0, DRIVE_HIGH), + MIO_CFG_OUT(GPIO90_GSM_nMOD_OFF_CMD, AF0, DRIVE_HIGH), + MIO_CFG_OUT(GPIO114_GSM_nMOD_DTE_UART_STATE, AF0, DRIVE_HIGH), + MIO_CFG_IN(GPIO25_GSM_MOD_ON_STATE, AF0), + MIO_CFG_IN(GPIO113_GSM_EVENT, AF0) | WAKEUP_ON_EDGE_BOTH, + GPIO34_FFUART_RXD, + GPIO35_FFUART_CTS, + GPIO36_FFUART_DCD, + GPIO37_FFUART_DSR, + GPIO39_FFUART_TXD, + GPIO40_FFUART_DTR, + GPIO41_FFUART_RTS, + + /* Sound */ + GPIO89_AC97_SYSCLK, + MIO_CFG_IN(GPIO12_HPJACK_INSERT, AF0), + + /* Leds */ + MIO_CFG_OUT(GPIO10_LED_nCharging, AF0, DRIVE_HIGH), + MIO_CFG_OUT(GPIO97_LED_nBlue, AF0, DRIVE_HIGH), + MIO_CFG_OUT(GPIO98_LED_nOrange, AF0, DRIVE_HIGH), + MIO_CFG_OUT(GPIO82_LED_nVibra, AF0, DRIVE_HIGH), + MIO_CFG_OUT(GPIO115_LED_nKeyboard, AF0, DRIVE_HIGH), + + /* Keyboard */ + MIO_CFG_IN(GPIO0_KEY_POWER, AF0) | WAKEUP_ON_EDGE_BOTH, + MIO_CFG_IN(GPIO93_KEY_VOLUME_UP, AF0), + MIO_CFG_IN(GPIO94_KEY_VOLUME_DOWN, AF0), + GPIO100_KP_MKIN_0, + GPIO101_KP_MKIN_1, + GPIO102_KP_MKIN_2, + GPIO103_KP_MKOUT_0, + GPIO104_KP_MKOUT_1, + GPIO105_KP_MKOUT_2, + + /* Unknown */ + MFP_CFG_IN(GPIO14, AF0), + MFP_CFG_IN(GPIO20, AF0), + MFP_CFG_IN(GPIO21, AF0), + MFP_CFG_IN(GPIO33, AF0), + MFP_CFG_OUT(GPIO49, AF0, DRIVE_HIGH), + MFP_CFG_OUT(GPIO57, AF0, DRIVE_HIGH), + MFP_CFG_OUT(GPIO77, AF0, DRIVE_HIGH), + MFP_CFG_IN(GPIO80, AF0), + MFP_CFG_OUT(GPIO86, AF0, DRIVE_HIGH), + MFP_CFG_IN(GPIO96, AF0), + MFP_CFG_OUT(GPIO116, AF0, DRIVE_HIGH), +}; + +#define MIO_GPIO_IN(num, _desc) \ + { .gpio = (num), .dir = 0, .desc = (_desc) } +#define MIO_GPIO_OUT(num, _init, _desc) \ + { .gpio = (num), .dir = 1, .init = (_init), .desc = (_desc) } +struct gpio_ress { + unsigned gpio : 8; + unsigned dir : 1; + unsigned init : 1; + char *desc; +}; + +static int mio_gpio_request(struct gpio_ress *gpios, int size) +{ + int i, rc = 0; + int gpio; + int dir; + + for (i = 0; (!rc) && (i < size); i++) { + gpio = gpios[i].gpio; + dir = gpios[i].dir; + rc = gpio_request(gpio, gpios[i].desc); + if (rc) { + printk(KERN_ERR "Error requesting GPIO %d(%s) : %d\n", + gpio, gpios[i].desc, rc); + continue; + } + if (dir) + gpio_direction_output(gpio, gpios[i].init); + else + gpio_direction_input(gpio); + } + while ((rc) && (--i >= 0)) + gpio_free(gpios[i].gpio); + return rc; +} + +static void mio_gpio_free(struct gpio_ress *gpios, int size) +{ + int i; + + for (i = 0; i < size; i++) + gpio_free(gpios[i].gpio); +} + +/* LCD Screen and Backlight */ +static struct platform_pwm_backlight_data mioa701_backlight_data = { + .pwm_id = 0, + .max_brightness = 100, + .dft_brightness = 50, + .pwm_period_ns = 4000 * 1024, /* Fl = 250kHz */ +}; + +/* + * LTM0305A776C LCD panel timings + * + * see: + * - the LTM0305A776C datasheet, + * - and the PXA27x Programmers' manual + */ +static struct pxafb_mode_info mioa701_ltm0305a776c = { + .pixclock = 220000, /* CLK=4.545 MHz */ + .xres = 240, + .yres = 320, + .bpp = 16, + .hsync_len = 4, + .vsync_len = 2, + .left_margin = 6, + .right_margin = 4, + .upper_margin = 5, + .lower_margin = 3, +}; + +static void mioa701_lcd_power(int on, struct fb_var_screeninfo *si) +{ + gpio_set_value(GPIO87_LCD_POWER, on); +} + +static struct pxafb_mach_info mioa701_pxafb_info = { + .modes = &mioa701_ltm0305a776c, + .num_modes = 1, + .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL, + .pxafb_lcd_power = mioa701_lcd_power, +}; + +/* + * Keyboard configuration + */ +static unsigned int mioa701_matrix_keys[] = { + KEY(0, 0, KEY_UP), + KEY(0, 1, KEY_RIGHT), + KEY(0, 2, KEY_MEDIA), + KEY(1, 0, KEY_DOWN), + KEY(1, 1, KEY_ENTER), + KEY(1, 2, KEY_CONNECT), /* GPS key */ + KEY(2, 0, KEY_LEFT), + KEY(2, 1, KEY_PHONE), /* Phone Green key */ + KEY(2, 2, KEY_CAMERA) /* Camera key */ +}; +static struct pxa27x_keypad_platform_data mioa701_keypad_info = { + .matrix_key_rows = 3, + .matrix_key_cols = 3, + .matrix_key_map = mioa701_matrix_keys, + .matrix_key_map_size = ARRAY_SIZE(mioa701_matrix_keys), +}; + +/* + * GPIO Key Configuration + */ +#define MIO_KEY(key, _gpio, _desc, _wakeup) \ + { .code = (key), .gpio = (_gpio), .active_low = 0, \ + .desc = (_desc), .type = EV_KEY, .wakeup = (_wakeup) } +static struct gpio_keys_button mioa701_button_table[] = { + MIO_KEY(KEY_EXIT, GPIO0_KEY_POWER, "Power button", 1), + MIO_KEY(KEY_VOLUMEUP, GPIO93_KEY_VOLUME_UP, "Volume up", 0), + MIO_KEY(KEY_VOLUMEDOWN, GPIO94_KEY_VOLUME_DOWN, "Volume down", 0), + MIO_KEY(KEY_HP, GPIO12_HPJACK_INSERT, "HP jack detect", 0) +}; + +static struct gpio_keys_platform_data mioa701_gpio_keys_data = { + .buttons = mioa701_button_table, + .nbuttons = ARRAY_SIZE(mioa701_button_table), +}; + +/* + * Leds and vibrator + */ +#define ONE_LED(_gpio, _name) \ +{ .gpio = (_gpio), .name = (_name), .active_low = true } +static struct gpio_led gpio_leds[] = { + ONE_LED(GPIO10_LED_nCharging, "mioa701:charging"), + ONE_LED(GPIO97_LED_nBlue, "mioa701:blue"), + ONE_LED(GPIO98_LED_nOrange, "mioa701:orange"), + ONE_LED(GPIO82_LED_nVibra, "mioa701:vibra"), + ONE_LED(GPIO115_LED_nKeyboard, "mioa701:keyboard") +}; + +static struct gpio_led_platform_data gpio_led_info = { + .leds = gpio_leds, + .num_leds = ARRAY_SIZE(gpio_leds), +}; + +/* + * GSM Sagem XS200 chip + * + * GSM handling was purged from kernel. For history, this is the way to go : + * - init : GPIO24_GSM_MOD_RESET_CMD = 0, GPIO114_GSM_nMOD_DTE_UART_STATE = 1 + * GPIO88_GSM_nMOD_ON_CMD = 1, GPIO90_GSM_nMOD_OFF_CMD = 1 + * - reset : GPIO24_GSM_MOD_RESET_CMD = 1, msleep(100), + * GPIO24_GSM_MOD_RESET_CMD = 0 + * - turn on : GPIO88_GSM_nMOD_ON_CMD = 0, msleep(1000), + * GPIO88_GSM_nMOD_ON_CMD = 1 + * - turn off : GPIO90_GSM_nMOD_OFF_CMD = 0, msleep(1000), + * GPIO90_GSM_nMOD_OFF_CMD = 1 + */ +static int is_gsm_on(void) +{ + int is_on; + + is_on = !!gpio_get_value(GPIO25_GSM_MOD_ON_STATE); + return is_on; +} + +irqreturn_t gsm_on_irq(int irq, void *p) +{ + printk(KERN_DEBUG "Mioa701: GSM status changed to %s\n", + is_gsm_on() ? "on" : "off"); + return IRQ_HANDLED; +} + +struct gpio_ress gsm_gpios[] = { + MIO_GPIO_IN(GPIO25_GSM_MOD_ON_STATE, "GSM state"), + MIO_GPIO_IN(GPIO113_GSM_EVENT, "GSM event"), +}; + +static int __init gsm_init(void) +{ + int rc; + + rc = mio_gpio_request(ARRAY_AND_SIZE(gsm_gpios)); + if (rc) + goto err_gpio; + rc = request_irq(gpio_to_irq(GPIO25_GSM_MOD_ON_STATE), gsm_on_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "GSM XS200 Power Irq", NULL); + if (rc) + goto err_irq; + + gpio_set_wake(GPIO113_GSM_EVENT, 1); + return 0; + +err_irq: + printk(KERN_ERR "Mioa701: Can't request GSM_ON irq\n"); + mio_gpio_free(ARRAY_AND_SIZE(gsm_gpios)); +err_gpio: + printk(KERN_ERR "Mioa701: gsm not available\n"); + return rc; +} + +static void gsm_exit(void) +{ + free_irq(gpio_to_irq(GPIO25_GSM_MOD_ON_STATE), NULL); + mio_gpio_free(ARRAY_AND_SIZE(gsm_gpios)); +} + +/* + * Bluetooth BRF6150 chip + * + * BT handling was purged from kernel. For history, this is the way to go : + * - turn on : GPIO83_BT_ON = 1 + * - turn off : GPIO83_BT_ON = 0 + */ + +/* + * GPS Sirf Star III chip + * + * GPS handling was purged from kernel. For history, this is the way to go : + * - init : GPIO23_GPS_UNKNOWN1 = 1, GPIO26_GPS_ON = 0, GPIO27_GPS_RESET = 0 + * GPIO106_GPS_UNKNOWN2 = 0, GPIO107_GPS_UNKNOWN3 = 0 + * - turn on : GPIO27_GPS_RESET = 1, GPIO26_GPS_ON = 1 + * - turn off : GPIO26_GPS_ON = 0, GPIO27_GPS_RESET = 0 + */ + +/* + * USB UDC + */ +static void udc_power_command(int cmd) +{ + switch (cmd) { + case PXA2XX_UDC_CMD_DISCONNECT: + gpio_set_value(GPIO22_USB_ENABLE, 0); + break; + case PXA2XX_UDC_CMD_CONNECT: + gpio_set_value(GPIO22_USB_ENABLE, 1); + break; + default: + printk(KERN_INFO "udc_control: unknown command (0x%x)!\n", cmd); + break; + } +} + +static int is_usb_connected(void) +{ + return !!gpio_get_value(GPIO13_USB_DETECT); +} + +static struct pxa2xx_udc_mach_info mioa701_udc_info = { + .udc_is_connected = is_usb_connected, + .udc_command = udc_power_command, +}; + +struct gpio_ress udc_gpios[] = { + MIO_GPIO_OUT(GPIO22_USB_ENABLE, 0, "USB Vbus enable") +}; + +static int __init udc_init(void) +{ + pxa_set_udc_info(&mioa701_udc_info); + return mio_gpio_request(ARRAY_AND_SIZE(udc_gpios)); +} + +static void udc_exit(void) +{ + mio_gpio_free(ARRAY_AND_SIZE(udc_gpios)); +} + +/* + * SDIO/MMC Card controller + */ +static void mci_setpower(struct device *dev, unsigned int vdd) +{ + struct pxamci_platform_data *p_d = dev->platform_data; + + if ((1 << vdd) & p_d->ocr_mask) + gpio_set_value(GPIO91_SDIO_EN, 1); /* enable SDIO power */ + else + gpio_set_value(GPIO91_SDIO_EN, 0); /* disable SDIO power */ +} + +static int mci_get_ro(struct device *dev) +{ + return gpio_get_value(GPIO78_SDIO_RO); +} + +struct gpio_ress mci_gpios[] = { + MIO_GPIO_IN(GPIO78_SDIO_RO, "SDIO readonly detect"), + MIO_GPIO_IN(GPIO15_SDIO_INSERT, "SDIO insertion detect"), + MIO_GPIO_OUT(GPIO91_SDIO_EN, 0, "SDIO power enable") +}; + +static void mci_exit(struct device *dev, void *data) +{ + mio_gpio_free(ARRAY_AND_SIZE(mci_gpios)); + free_irq(gpio_to_irq(GPIO15_SDIO_INSERT), data); +} + +static struct pxamci_platform_data mioa701_mci_info; + +/** + * The card detect interrupt isn't debounced so we delay it by 250ms + * to give the card a chance to fully insert/eject. + */ +static int mci_init(struct device *dev, irq_handler_t detect_int, void *data) +{ + int rc; + int irq = gpio_to_irq(GPIO15_SDIO_INSERT); + + rc = mio_gpio_request(ARRAY_AND_SIZE(mci_gpios)); + if (rc) + goto err_gpio; + /* enable RE/FE interrupt on card insertion and removal */ + rc = request_irq(irq, detect_int, + IRQF_DISABLED | IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + "MMC card detect", data); + if (rc) + goto err_irq; + + mioa701_mci_info.detect_delay = msecs_to_jiffies(250); + return 0; + +err_irq: + dev_err(dev, "mioa701_mci_init: MMC/SD:" + " can't request MMC card detect IRQ\n"); + mio_gpio_free(ARRAY_AND_SIZE(mci_gpios)); +err_gpio: + return rc; +} + +static struct pxamci_platform_data mioa701_mci_info = { + .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, + .init = mci_init, + .get_ro = mci_get_ro, + .setpower = mci_setpower, + .exit = mci_exit, +}; + +/* FlashRAM */ +static struct resource strataflash_resource = { + .start = PXA_CS0_PHYS, + .end = PXA_CS0_PHYS + SZ_64M - 1, + .flags = IORESOURCE_MEM, +}; + +static struct physmap_flash_data strataflash_data = { + .width = 2, + /* .set_vpp = mioa701_set_vpp, */ +}; + +static struct platform_device strataflash = { + .name = "physmap-flash", + .id = -1, + .resource = &strataflash_resource, + .num_resources = 1, + .dev = { + .platform_data = &strataflash_data, + }, +}; + +/* + * Suspend/Resume bootstrap management + * + * MIO A701 reboot sequence is highly ROM dependant. From the one dissassembled, + * this sequence is as follows : + * - disables interrupts + * - initialize SDRAM (self refresh RAM into active RAM) + * - initialize GPIOs (depends on value at 0xa020b020) + * - initialize coprossessors + * - if edge detect on PWR_SCL(GPIO3), then proceed to cold start + * - or if value at 0xa020b000 not equal to 0x0f0f0f0f, proceed to cold start + * - else do a resume, ie. jump to addr 0xa0100000 + */ +#define RESUME_ENABLE_ADDR 0xa020b000 +#define RESUME_ENABLE_VAL 0x0f0f0f0f +#define RESUME_BT_ADDR 0xa020b020 +#define RESUME_UNKNOWN_ADDR 0xa020b024 +#define RESUME_VECTOR_ADDR 0xa0100000 +#define BOOTSTRAP_WORDS mioa701_bootstrap_lg/4 + +static u32 *save_buffer; + +static void install_bootstrap(void) +{ + int i; + u32 *rom_bootstrap = phys_to_virt(RESUME_VECTOR_ADDR); + u32 *src = &mioa701_bootstrap; + + for (i = 0; i < BOOTSTRAP_WORDS; i++) + rom_bootstrap[i] = src[i]; +} + + +static int mioa701_sys_suspend(struct sys_device *sysdev, pm_message_t state) +{ + int i = 0, is_bt_on; + u32 *mem_resume_vector = phys_to_virt(RESUME_VECTOR_ADDR); + u32 *mem_resume_enabler = phys_to_virt(RESUME_ENABLE_ADDR); + u32 *mem_resume_bt = phys_to_virt(RESUME_BT_ADDR); + u32 *mem_resume_unknown = phys_to_virt(RESUME_UNKNOWN_ADDR); + + /* Devices prepare suspend */ + is_bt_on = gpio_get_value(GPIO83_BT_ON); + pxa2xx_mfp_set_lpm(GPIO83_BT_ON, + is_bt_on ? MFP_LPM_DRIVE_HIGH : MFP_LPM_DRIVE_LOW); + + for (i = 0; i < BOOTSTRAP_WORDS; i++) + save_buffer[i] = mem_resume_vector[i]; + save_buffer[i++] = *mem_resume_enabler; + save_buffer[i++] = *mem_resume_bt; + save_buffer[i++] = *mem_resume_unknown; + + *mem_resume_enabler = RESUME_ENABLE_VAL; + *mem_resume_bt = is_bt_on; + + install_bootstrap(); + return 0; +} + +static int mioa701_sys_resume(struct sys_device *sysdev) +{ + int i = 0; + u32 *mem_resume_vector = phys_to_virt(RESUME_VECTOR_ADDR); + u32 *mem_resume_enabler = phys_to_virt(RESUME_ENABLE_ADDR); + u32 *mem_resume_bt = phys_to_virt(RESUME_BT_ADDR); + u32 *mem_resume_unknown = phys_to_virt(RESUME_UNKNOWN_ADDR); + + for (i = 0; i < BOOTSTRAP_WORDS; i++) + mem_resume_vector[i] = save_buffer[i]; + *mem_resume_enabler = save_buffer[i++]; + *mem_resume_bt = save_buffer[i++]; + *mem_resume_unknown = save_buffer[i++]; + + return 0; +} + +static struct sysdev_class mioa701_sysclass = { + .name = "mioa701", +}; + +static struct sys_device sysdev_bootstrap = { + .cls = &mioa701_sysclass, +}; + +static struct sysdev_driver driver_bootstrap = { + .suspend = &mioa701_sys_suspend, + .resume = &mioa701_sys_resume, +}; + +static int __init bootstrap_init(void) +{ + int rc; + int save_size = mioa701_bootstrap_lg + (sizeof(u32) * 3); + + rc = sysdev_class_register(&mioa701_sysclass); + if (rc) { + printk(KERN_ERR "Failed registering mioa701 sys class\n"); + return -ENODEV; + } + rc = sysdev_register(&sysdev_bootstrap); + if (rc) { + printk(KERN_ERR "Failed registering mioa701 sys device\n"); + return -ENODEV; + } + rc = sysdev_driver_register(&mioa701_sysclass, &driver_bootstrap); + if (rc) { + printk(KERN_ERR "Failed registering PMU sys driver\n"); + return -ENODEV; + } + + save_buffer = kmalloc(save_size, GFP_KERNEL); + if (!save_buffer) + return -ENOMEM; + printk(KERN_INFO "MioA701: allocated %d bytes for bootstrap\n", + save_size); + return 0; +} + +static void bootstrap_exit(void) +{ + kfree(save_buffer); + sysdev_driver_unregister(&mioa701_sysclass, &driver_bootstrap); + sysdev_unregister(&sysdev_bootstrap); + sysdev_class_unregister(&mioa701_sysclass); + + printk(KERN_CRIT "Unregistering mioa701 suspend will hang next" + "resume !!!\n"); +} + +/* + * Power Supply + */ +static char *supplicants[] = { + "mioa701_battery" +}; + +static void mioa701_set_charge(int flags) +{ + gpio_set_value(GPIO9_CHARGE_nEN, !flags); +} + +static struct pda_power_pdata power_pdata = { + .is_ac_online = is_usb_connected, + .set_charge = mioa701_set_charge, + .supplied_to = supplicants, + .num_supplicants = ARRAY_SIZE(supplicants), +}; + +static struct resource power_resources[] = { + [0] = { + .name = "ac", + .start = gpio_to_irq(GPIO13_USB_DETECT), + .end = gpio_to_irq(GPIO13_USB_DETECT), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | + IORESOURCE_IRQ_LOWEDGE, + }, +}; + +static struct platform_device power_dev = { + .name = "pda-power", + .id = -1, + .resource = power_resources, + .num_resources = ARRAY_SIZE(power_resources), + .dev = { + .platform_data = &power_pdata, + }, +}; + +#if defined(CONFIG_PDA_POWER) && defined(CONFIG_TOUCHSCREEN_WM97XX) +static struct wm97xx *battery_wm; + +static enum power_supply_property battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, /* Necessary for apm */ +}; + +static int get_battery_voltage(void) +{ + int adc = -1; + + if (battery_wm) + adc = wm97xx_read_aux_adc(battery_wm, WM97XX_AUX_ID1); + return adc; +} + +static int get_battery_status(struct power_supply *b) +{ + int status; + + if (is_usb_connected()) + status = POWER_SUPPLY_STATUS_CHARGING; + else + status = POWER_SUPPLY_STATUS_DISCHARGING; + + return status; +} + +static int get_property(struct power_supply *b, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = get_battery_status(b); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = 0xfd0; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = 0xc00; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = get_battery_voltage(); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = 100; + break; + default: + val->intval = -1; + rc = -1; + } + + return rc; +}; + +static struct power_supply battery_ps = { + .name = "mioa701_battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = get_property, + .properties = battery_props, + .num_properties = ARRAY_SIZE(battery_props), +}; + +static int battery_probe(struct platform_device *pdev) +{ + struct wm97xx *wm = platform_get_drvdata(pdev); + int rc; + + battery_wm = wm; + + rc = power_supply_register(NULL, &battery_ps); + if (rc) + dev_err(&pdev->dev, + "Could not register mioa701 battery -> %d\n", rc); + return rc; +} + +static int battery_remove(struct platform_device *pdev) +{ + battery_wm = NULL; + return 0; +} + +static struct platform_driver mioa701_battery_driver = { + .driver = { + .name = "wm97xx-battery", + }, + .probe = battery_probe, + .remove = battery_remove +}; + +static int __init mioa701_battery_init(void) +{ + int rc; + + rc = platform_driver_register(&mioa701_battery_driver); + if (rc) + printk(KERN_ERR "Could not register mioa701 battery driver\n"); + return rc; +} + +#else +static int __init mioa701_battery_init(void) +{ + return 0; +} +#endif + +/* + * Mio global + */ + +/* Devices */ +#define MIO_PARENT_DEV(var, strname, tparent, pdata) \ +static struct platform_device var = { \ + .name = strname, \ + .id = -1, \ + .dev = { \ + .platform_data = pdata, \ + .parent = tparent, \ + }, \ +}; +#define MIO_SIMPLE_DEV(var, strname, pdata) \ + MIO_PARENT_DEV(var, strname, NULL, pdata) + +MIO_SIMPLE_DEV(mioa701_gpio_keys, "gpio-keys", &mioa701_gpio_keys_data) +MIO_PARENT_DEV(mioa701_backlight, "pwm-backlight", &pxa27x_device_pwm0.dev, + &mioa701_backlight_data); +MIO_SIMPLE_DEV(mioa701_led, "leds-gpio", &gpio_led_info) +MIO_SIMPLE_DEV(pxa2xx_pcm, "pxa2xx-pcm", NULL) +MIO_SIMPLE_DEV(pxa2xx_ac97, "pxa2xx-ac97", NULL) +MIO_PARENT_DEV(mio_wm9713_codec, "wm9713-codec", &pxa2xx_ac97.dev, NULL) +MIO_SIMPLE_DEV(mioa701_sound, "mioa701-wm9713", NULL) +MIO_SIMPLE_DEV(mioa701_board, "mioa701-board", NULL) + +static struct platform_device *devices[] __initdata = { + &mioa701_gpio_keys, + &mioa701_backlight, + &mioa701_led, + &pxa2xx_pcm, + &pxa2xx_ac97, + &mio_wm9713_codec, + &mioa701_sound, + &power_dev, + &strataflash, + &mioa701_board +}; + +static void mioa701_machine_exit(void); + +static void mioa701_poweroff(void) +{ + mioa701_machine_exit(); + gpio_set_value(GPIO18_POWEROFF, 1); +} + +static void mioa701_restart(char c) +{ + mioa701_machine_exit(); + arm_machine_restart(c); +} + +struct gpio_ress global_gpios[] = { + MIO_GPIO_OUT(GPIO9_CHARGE_nEN, 1, "Charger enable"), + MIO_GPIO_OUT(GPIO18_POWEROFF, 0, "Power Off"), + MIO_GPIO_OUT(GPIO87_LCD_POWER, 0, "LCD Power") +}; + +static void __init mioa701_machine_init(void) +{ + PSLR = 0xff100000; /* SYSDEL=125ms, PWRDEL=125ms, PSLR_SL_ROD=1 */ + PCFR = PCFR_DC_EN | PCFR_GPR_EN | PCFR_OPDE; + RTTR = 32768 - 1; /* Reset crazy WinCE value */ + UP2OCR = UP2OCR_HXOE; + + pxa2xx_mfp_config(ARRAY_AND_SIZE(mioa701_pin_config)); + mio_gpio_request(ARRAY_AND_SIZE(global_gpios)); + bootstrap_init(); + set_pxa_fb_info(&mioa701_pxafb_info); + pxa_set_mci_info(&mioa701_mci_info); + pxa_set_keypad_info(&mioa701_keypad_info); + udc_init(); + pm_power_off = mioa701_poweroff; + arm_pm_restart = mioa701_restart; + platform_add_devices(devices, ARRAY_SIZE(devices)); + gsm_init(); + mioa701_battery_init(); +} + +static void mioa701_machine_exit(void) +{ + udc_exit(); + bootstrap_exit(); + gsm_exit(); +} + +MACHINE_START(MIOA701, "MIO A701") + .phys_io = 0x40000000, + .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc, + .boot_params = 0xa0000100, + .map_io = &pxa_map_io, + .init_irq = &pxa27x_init_irq, + .init_machine = mioa701_machine_init, + .timer = &pxa_timer, +MACHINE_END diff --git a/arch/arm/mach-pxa/mioa701_bootresume.S b/arch/arm/mach-pxa/mioa701_bootresume.S new file mode 100644 index 0000000..a647693 --- /dev/null +++ b/arch/arm/mach-pxa/mioa701_bootresume.S @@ -0,0 +1,36 @@ +/* Bootloader to resume MIO A701 + * + * 2007-1-12 Robert Jarzmik + * + * This code is licenced under the GPLv2. +*/ + +#include +#include + +/* + * Note: Yes, part of the following code is located into the .data section. + * This is to allow jumpaddr to be accessed with a relative load + * while we can't rely on any MMU translation. We could have put + * sleep_save_sp in the .text section as well, but some setups might + * insist on it to be truly read-only. + */ + .data +ENTRY(mioa701_bootstrap) +0: + b 1f +ENTRY(mioa701_jumpaddr) + .word 0x40f00008 @ PSPR in no-MMU mode +1: + mov r0, #0xa0000000 @ Don't suppose memory access works + orr r0, r0, #0x00200000 @ even if it's supposed to + mov r1, #0 + str r1, [r0] @ Early disable resume for next boot + ldr r0, mioa701_jumpaddr @ (Murphy's Law) + ldr r0, [r0] + mov pc, r0 +2: + +ENTRY(mioa701_bootstrap_lg) + .data + .word 2b-0b -- cgit v1.1