This patch adds support for the handshake with the BP (baseband processor) of the Motorola EZX smartphones. Signed-off-by: Daniel Ribeiro Index: linux-2.6.24/arch/arm/mach-pxa/ezx.c =================================================================== --- linux-2.6.24.orig/arch/arm/mach-pxa/ezx.c +++ linux-2.6.24/arch/arm/mach-pxa/ezx.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "generic.h" @@ -86,8 +87,25 @@ .init = ezx_ohci_init, }; +/* BP */ +static struct ezxbp_config ezxbp_data = { + .gpio_reset = GPIO_BB_RESET, + .gpio_wdi = GPIO_BB_WDI, + .gpio_wdi2 = GPIO_BB_WDI2, + .gpio_rdy = GPIO_BP_RDY, + .gpio_mcu_int_sw = GPIO_MCU_INT_SW, +}; + +static struct platform_device ezxbp_device = { + .name = "ezx-bp", + .dev = { + .platform_data = &ezxbp_data, + }, + .id = -1, +}; static struct platform_device *devices[] __initdata = { + &ezxbp_device, }; static int __init ezx_init(void) Index: linux-2.6.24/arch/arm/mach-pxa/Kconfig =================================================================== --- linux-2.6.24.orig/arch/arm/mach-pxa/Kconfig +++ linux-2.6.24/arch/arm/mach-pxa/Kconfig @@ -133,6 +133,12 @@ endchoice +config EZX_BP + bool "Baseband Processor control for EZX platform" + help + This enables control code for the BP (baseband processor) found in + Motorola's EZX smartphone platform. + endif endmenu Index: linux-2.6.24/arch/arm/mach-pxa/ezx-bp.c =================================================================== --- /dev/null +++ linux-2.6.24/arch/arm/mach-pxa/ezx-bp.c @@ -0,0 +1,261 @@ +/* + * BP handshake code for Motorola EZX phones + * + * Copyright (c) 2007 Daniel Ribeiro + * + * Based on Motorola's a780.c Copyright (c) 2003-2005 Motorola + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +/* BP Handshake */ +#define FIRST_STEP 2 +#define LAST_STEP 3 +#define BP_RDY_TIMEOUT 0x000c0000 + +#if 1 +#define DEBUGP(x, args ...) printk(x, ##args) +#else +#define DEBUGP(x, args ...) +#endif + +extern void usb_send_readurb(void); + +static struct ezxbp_config *bp; + +/* check power down condition */ +static inline void check_power_off(void) +{ + if (pxa_gpio_get_value(bp->gpio_wdi2) == 0) { + DEBUGP("BP request poweroff!\n"); + /* + * It is correct to power off here, the following line is + * commented out because e680 lowers WDI2 when BP is in + * flash mode, otherwise WDI2 is used to detect low + * battery. You can safely uncomment this line if you are + * using this kernel with BP in normal mode. + */ +#if 0 /* some versions of BP firmware doesnt honor this */ + pm_power_off(); +#endif + } +} + +static int step = FIRST_STEP; + +inline int bp_handshake_passed(void) +{ + return (step > LAST_STEP); +} +EXPORT_SYMBOL_GPL(bp_handshake_passed); + +static void handshake(void) +{ + /* step 1: check MCU_INT_SW or BP_RDY is low (now it is checked in apboot) */ + DEBUGP("bp handshake entered!\n"); + if (step == 1) { + int timeout = BP_RDY_TIMEOUT; + + /* config MCU_INT_SW, BP_RDY as input */ + pxa_gpio_mode(bp->gpio_mcu_int_sw | GPIO_IN); + pxa_gpio_mode(bp->gpio_rdy | GPIO_IN); + + while (timeout--) { + if (pxa_gpio_get_value(bp->gpio_mcu_int_sw) == 0 + || pxa_gpio_get_value(bp->gpio_rdy) == 0) { + step++; + break; + } + + check_power_off(); + } + DEBUGP("ezx-bp: handshake step 1\n"); + } + + /* step 2: wait BP_RDY is low */ + if (step == 2) { + if (pxa_gpio_get_value(bp->gpio_rdy) == 0) { + /* config MCU_INT_SW as output */ + pxa_gpio_mode(bp->gpio_mcu_int_sw | GPIO_OUT); + pxa_gpio_set_value(bp->gpio_mcu_int_sw, 0); + + step++; + DEBUGP("ezx-bp: handshake step 2\n"); + } + } + + /* step 3: wait BP_RDY is high */ + else if (step == 3) { + if (pxa_gpio_get_value(bp->gpio_rdy)) { + step++; + /* FIXME delay_bklight(); */ + pxa_gpio_set_value(bp->gpio_mcu_int_sw, 1); + printk(KERN_NOTICE "ezx-bp: handshake passed\n"); + } + } +} + +irqreturn_t bp_wdi_handler(int irq, void *dev_id) +{ + DEBUGP("BP Lowered WDI line. This is not good :(\n"); + /* + * this means that BP is not responsive. + * we could try to reset BP and then handshake again + * but i doubt its possible to bring it up again. + */ + return IRQ_HANDLED; +} + +static irqreturn_t bp_rdy_handler(int irq, void *dev_id) +{ +// struct bp *bp = dev_id; + DEBUGP("BP rdy irq\n"); + if (!bp_handshake_passed()) { + handshake(); + if (bp_handshake_passed()) { + /* FIXME: (test) try to not disable irq_wdi2 and drain battery */ + disable_irq(IRQ_GPIO(bp->gpio_wdi2)); + + /* set bp_rdy handle for usb ipc */ + set_irq_type(IRQ_GPIO(bp->gpio_rdy), IRQT_FALLING); + } + } +#ifdef CONFIG_TS0710_MUX_USB + else usb_send_readurb(); +#endif + return IRQ_HANDLED; +} + +/* BP request for poweroff */ +static irqreturn_t bp_wdi2_handler(int irq, void *dev_id) +{ + DEBUGP("BP request poweroff!\n"); + /* same case as check_power_off() */ +#ifndef CONFIG_PXA_EZX_E680 + pm_power_off(); +#endif + return IRQ_HANDLED; +} + +static int __init ezxbp_probe(struct platform_device *pdev) +{ + int ret; + bp = pdev->dev.platform_data; + +// bp = kzalloc(sizeof(*bp), GFP_KERNEL); +// if (!bp) +// return -ENOMEM; + +/* bp->irq_rdy = platform_get_irq(dev, 0); + if (bp->irq_rdy < 0) { + ret = bp->irq_rdy; + goto fail; + } + + bp->irq_wdi2 = platform_get_irq(dev, 1); + if (bp->irq_wdi2 < 0) { + ret = bp->irq_wdi2; + goto fail; + } + + bp->irq_wdi = platform_get_irq(dev, 2); + if (bp->irq_wdi < 0) { + ret = bp->irq_wdi; + goto fail; + } +*/ + + set_irq_type(IRQ_GPIO(bp->gpio_wdi), IRQT_FALLING); + request_irq(IRQ_GPIO(bp->gpio_wdi), bp_wdi_handler, IRQF_DISABLED, + "bp wdi", bp); + + set_irq_type(IRQ_GPIO(bp->gpio_rdy), IRQT_BOTHEDGE); + request_irq(IRQ_GPIO(bp->gpio_rdy), bp_rdy_handler, IRQF_DISABLED, + "bp rdy", bp); + + set_irq_type(IRQ_GPIO(bp->gpio_wdi2), IRQT_FALLING); + request_irq(IRQ_GPIO(bp->gpio_wdi2), bp_wdi2_handler, IRQF_DISABLED, + "bp wdi2", bp); + + /* turn on BP */ + pxa_gpio_mode(bp->gpio_reset|GPIO_OUT); + pxa_gpio_set_value(bp->gpio_reset, 1); + + check_power_off(); + handshake(); + + return 0; +fail: + kfree(bp); + return ret; +} + +static int ezxbp_remove(struct platform_device *dev) +{ +// struct bp *bp = platform_get_drvdata(dev); + + free_irq(IRQ_GPIO(bp->gpio_wdi), bp); + free_irq(IRQ_GPIO(bp->gpio_wdi2), bp); + free_irq(IRQ_GPIO(bp->gpio_rdy), bp); + kfree(bp); + + return 0; +} + +static int ezxbp_suspend(struct platform_device *dev, pm_message_t state) +{ + DEBUGP("bp suspend!\n"); +/* pxa_gpio_set_value(bp->gpio_mcu_int_sw, 0); */ + return 0; +} + +static int ezxbp_resume(struct platform_device *dev) +{ + DEBUGP("bp resume!\n"); +/* pxa_gpio_set_value(bp->gpio_mcu_int_sw, 1); */ + return 0; +} +static struct platform_driver ezxbp_driver = { + .probe = ezxbp_probe, + .remove = ezxbp_remove, +#warning FIXME: missing suspend/resume support + .suspend = ezxbp_suspend, + .resume = ezxbp_resume, + .driver = { + .name = "ezx-bp", + .owner = THIS_MODULE, + }, +}; + +int __init ezxbp_init(void) +{ + return platform_driver_register(&ezxbp_driver); +} + +void ezxbp_fini(void) +{ + return platform_driver_unregister(&ezxbp_driver); +} + +module_init(ezxbp_init); +module_exit(ezxbp_fini); + +MODULE_DESCRIPTION("Motorola BP Control driver"); +MODULE_AUTHOR("Daniel Ribeiro "); +MODULE_LICENSE("GPL"); + Index: linux-2.6.24/arch/arm/mach-pxa/Makefile =================================================================== --- linux-2.6.24.orig/arch/arm/mach-pxa/Makefile +++ linux-2.6.24/arch/arm/mach-pxa/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_PXA_EZX_E2) += ezx-e2.o obj-$(CONFIG_PXA_EZX_A1200) += ezx-a1200.o obj-$(CONFIG_PXA_EZX_E6) += ezx-e6.o +obj-$(CONFIG_EZX_BP) += ezx-bp.o # Support for blinky lights led-y := leds.o Index: linux-2.6.24/include/asm-arm/arch-pxa/ezx-bp.h =================================================================== --- /dev/null +++ linux-2.6.24/include/asm-arm/arch-pxa/ezx-bp.h @@ -0,0 +1,8 @@ +struct ezxbp_config { + /* gpios for handshake */ + int gpio_reset; + int gpio_wdi; + int gpio_wdi2; + int gpio_rdy; + int gpio_mcu_int_sw; +};