From: luowei Date: Wed, 24 Oct 2012 10:11:05 +0000 (+0800) Subject: rk30_phonepad:add auto modem driver support X-Git-Tag: firefly_0821_release~8326 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=534cc41268545f31aa3eb33eb2225bb3d2eaea96;p=firefly-linux-kernel-4.4.55.git rk30_phonepad:add auto modem driver support --- diff --git a/drivers/misc/bp/Kconfig b/drivers/misc/bp/Kconfig new file mode 100755 index 000000000000..a3b8411163d7 --- /dev/null +++ b/drivers/misc/bp/Kconfig @@ -0,0 +1,13 @@ +# +# all auto modem control drivers configuration +# + +menuconfig BP_AUTO + bool "auto modem control driver support" + default n + +if BP_AUTO + + source "drivers/misc/bp/chips/Kconfig" + +endif diff --git a/drivers/misc/bp/Makefile b/drivers/misc/bp/Makefile new file mode 100755 index 000000000000..d32f5e16fae4 --- /dev/null +++ b/drivers/misc/bp/Makefile @@ -0,0 +1,3 @@ +# auto modem control drivers +obj-$(CONFIG_BP_AUTO) += chips/ +obj-$(CONFIG_BP_AUTO) += bp-auto.o \ No newline at end of file diff --git a/drivers/misc/bp/bp-auto.c b/drivers/misc/bp/bp-auto.c new file mode 100755 index 000000000000..16af64fe8414 --- /dev/null +++ b/drivers/misc/bp/bp-auto.c @@ -0,0 +1,665 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if 0 +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +struct bp_private_data *g_bp; +static struct class *g_bp_class; +static struct bp_operate *g_bp_ops[BP_ID_NUM]; +struct class *bp_class = NULL; + +static void ap_wakeup_bp(struct bp_private_data *bp, int wake) +{ + if(bp->ops->ap_wake_bp) + bp->ops->ap_wake_bp(bp, wake); + +} + +static int bp_request_gpio(struct bp_private_data *bp) +{ + int result = 0; + + if(bp->pdata->gpio_valid) + { + if(bp->pdata->bp_power > 0) + { + bp->ops->bp_power = bp->pdata->bp_power; + } + + if(bp->pdata->bp_en > 0) + { + bp->ops->bp_en = bp->pdata->bp_en; + } + + if(bp->pdata->bp_reset > 0) + { + bp->ops->bp_reset = bp->pdata->bp_reset; + } + + if(bp->pdata->ap_ready > 0) + { + bp->ops->ap_ready = bp->pdata->ap_ready; + } + + if(bp->pdata->bp_ready > 0) + { + bp->ops->bp_ready = bp->pdata->bp_ready; + } + + if(bp->pdata->ap_wakeup_bp > 0) + { + bp->ops->ap_wakeup_bp = bp->pdata->ap_wakeup_bp; + } + + if(bp->pdata->bp_wakeup_ap > 0) + { + bp->ops->bp_wakeup_ap = bp->pdata->bp_wakeup_ap; + } + + if(bp->pdata->bp_usb_en > 0) + { + bp->ops->bp_usb_en = bp->pdata->bp_usb_en; + } + + if(bp->pdata->bp_uart_en > 0) + { + bp->ops->bp_uart_en = bp->pdata->bp_uart_en; + } + + } + + if(bp->ops->bp_power != BP_UNKNOW_DATA) + { + result = gpio_request(bp->ops->bp_power, "bp_power"); + if(result) + { + printk("%s:fail to request gpio %d\n",__func__, bp->ops->bp_power); + //return -1; + } + } + + if(bp->ops->bp_en != BP_UNKNOW_DATA) + { + result = gpio_request(bp->ops->bp_en, "bp_en"); + if(result) + { + printk("%s:fail to request gpio %d\n",__func__, bp->ops->bp_en); + //return -1; + } + } + + + if(bp->ops->bp_reset != BP_UNKNOW_DATA) + { + result = gpio_request(bp->ops->bp_reset, "bp_reset"); + if(result) + { + printk("%s:fail to request gpio %d\n",__func__, bp->ops->bp_reset); + //return -1; + } + } + + + if(bp->ops->ap_ready != BP_UNKNOW_DATA) + { + result = gpio_request(bp->ops->ap_ready, "ap_ready"); + if(result) + { + printk("%s:fail to request gpio %d\n",__func__, bp->ops->ap_ready); + //return -1; + } + } + + + if(bp->ops->bp_ready != BP_UNKNOW_DATA) + { + result = gpio_request(bp->ops->bp_ready, "bp_ready"); + if(result) + { + printk("%s:fail to request gpio %d\n",__func__, bp->ops->bp_ready); + //return -1; + } + } + + + if(bp->ops->ap_wakeup_bp != BP_UNKNOW_DATA) + { + result = gpio_request(bp->ops->ap_wakeup_bp, "ap_wakeup_bp"); + if(result) + { + printk("%s:fail to request gpio %d\n",__func__, bp->ops->ap_wakeup_bp); + //return -1; + } + } + + if(bp->ops->bp_wakeup_ap != BP_UNKNOW_DATA) + { + result = gpio_request(bp->ops->bp_wakeup_ap, "bp_wakeup_ap"); + if(result) + { + printk("%s:fail to request gpio %d\n",__func__, bp->ops->bp_wakeup_ap); + //return -1; + } + } + + if(bp->ops->bp_usb_en != BP_UNKNOW_DATA) + { + result = gpio_request(bp->ops->bp_usb_en, "bp_usb_en"); + if(result) + { + printk("%s:fail to request gpio %d\n",__func__, bp->ops->bp_usb_en); + //return -1; + } + } + + if(bp->ops->bp_uart_en != BP_UNKNOW_DATA) + { + result = gpio_request(bp->ops->bp_uart_en, "bp_uart_en"); + if(result) + { + printk("%s:fail to request gpio %d\n",__func__, bp->ops->bp_uart_en); + //return -1; + } + } + + return result; +} + + +static irqreturn_t bp_wake_up_irq(int irq, void *dev_id) +{ + + struct bp_private_data *bp = dev_id; + if(bp->ops->bp_wake_ap) + bp->ops->bp_wake_ap(bp); + + return IRQ_HANDLED; +} + +static int bp_id_open(struct inode *inode, struct file *file) +{ + struct bp_private_data *bp = g_bp; + + return 0; +} + +static int bp_id_release(struct inode *inode, struct file *file) +{ + + return 0; +} + +static long bp_id_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct bp_private_data *bp = g_bp; + void __user *argp = (void __user *)arg; + int result = 0; + + switch(cmd) + { + case BP_IOCTL_SET_PVID: + + break; + + case BP_IOCTL_GET_BPID: + if (copy_to_user(argp, &bp->ops->bp_id, sizeof(bp->ops->bp_id))) + { + printk("%s:failed to copy status to user space.\n",__FUNCTION__); + return -EFAULT; + } + + break; + + default: + break; + } + + return 0; +} + + +static int bp_dev_open(struct inode *inode, struct file *file) +{ + struct bp_private_data *bp = g_bp; + device_init_wakeup(bp->dev, 1); + return 0; +} +static ssize_t bp_dev_write(struct file *file, const char __user *buf,size_t len, loff_t *off) +{ + static char cmd[2]; + struct bp_private_data *bp = g_bp; + + int ret = 0; + if (len > 2) + { + return -EINVAL; + } + ret = copy_from_user(&cmd, buf, len); + if (ret != 0) { + return -EFAULT; + } + printk(" received cmd = %c\n",cmd[0]); + switch(bp->ops->bp_id) + { + case BP_ID_MT6229: + if (cmd[0] == '0') + { + gpio_direction_output(bp->ops->ap_ready, GPIO_LOW); + } + if (cmd[0] == '1') + { + gpio_direction_output(bp->ops->ap_ready, GPIO_HIGH); + } + if (cmd[0] == '2') + { + gpio_direction_output(bp->ops->bp_uart_en, GPIO_LOW); + } + if (cmd[0] == '3') + { + gpio_direction_output(bp->ops->bp_uart_en, GPIO_HIGH); + } + if (cmd[0] == '4') + { + gpio_direction_output(bp->ops->bp_usb_en, GPIO_HIGH); + }if (cmd[0] == '5') + { + gpio_direction_output(bp->ops->bp_usb_en, GPIO_LOW); + } + break; + + case BP_ID_MU509: + break; + + default: + break; + + } + return len; +} +static int bp_dev_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static long bp_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct bp_private_data *bp = g_bp; + void __user *argp = (void __user *)arg; + int result = 0; + + switch(cmd) + { + case BP_IOCTL_RESET: + if(bp->ops->reset) + { + bp->ops->reset(bp); + } + else if(bp->ops->active) + { + bp->ops->active(bp, 0); + msleep(100); + bp->ops->active(bp, 1); + } + break; + + case BP_IOCTL_POWON: + if(bp->ops->active) + bp->ops->active(bp, 1); + break; + + case BP_IOCTL_POWOFF: + if(bp->ops->active) + bp->ops->active(bp, 0); + break; + + case BP_IOCTL_WRITE_STATUS: + + break; + + case BP_IOCTL_GET_STATUS: + + break; + + case BP_IOCTL_SET_PVID: + + break; + + case BP_IOCTL_GET_BPID: + if (copy_to_user(argp, &bp->ops->bp_id, sizeof(bp->ops->bp_id))) + { + printk("%s:failed to copy status to user space.\n",__FUNCTION__); + return -EFAULT; + } + + break; + + default: + break; + } + + return 0; +} + +static ssize_t bp_status_read(struct class *cls, struct class_attribute *attr, char *_buf) +{ + struct bp_private_data *bp = g_bp; + + return sprintf(_buf, "%d\n", bp->status); + +} + +static ssize_t bp_status_write(struct class *cls, struct class_attribute *attr, const char *_buf, size_t _count) +{ + struct bp_private_data *bp = g_bp; + int result = 0; + int status = 0; + + status = simple_strtoul(_buf, NULL, 16); + if(status == bp->status) + return _count; + + bp->status = status; + + if(bp->ops->write_status) + result = bp->ops->write_status(bp); + + return result; +} +static CLASS_ATTR(bp_status, 0777, bp_status_read, bp_status_write); +static int bp_probe(struct platform_device *pdev) +{ + struct bp_platform_data *pdata = pdev->dev.platform_data; + struct bp_private_data *bp = NULL; + int i = 0, result; + + if(!pdata) + return -1; + + DBG("%s:init start\n",__func__); + + if(pdata->init_platform_hw) + pdata->init_platform_hw(); + + bp = kzalloc(sizeof(struct bp_private_data), GFP_KERNEL); + if(bp == NULL) + { + printk("%s:fail malloc bp data\n",__func__); + return -1; + } + + bp->pdata = pdata; + bp->dev = &pdev->dev; + + //select modem acccording to pdata defaultly + if((pdata->bp_id > BP_ID_INVALID) && (pdata->bp_id < BP_ID_NUM)) + { + if(g_bp_ops[pdata->bp_id]) + { + bp->ops = g_bp_ops[pdata->bp_id]; + printk("%s:bp_id=%d\n",__func__,bp->ops->bp_id); + } + else + { + printk("%s:error:g_bp_ops[%d] = 0x%p\n",__func__, pdata->bp_id, g_bp_ops[pdata->bp_id]); + } + + } + else + { + printk("%s:bp_id=%d is out of range\n",__func__, pdata->bp_id); + } + + bp_request_gpio(bp); + + if((bp->ops->bp_wakeup_ap) && (bp->ops->trig != BP_UNKNOW_DATA)) + { + result = request_irq(bp->ops->bp_wakeup_ap, bp_wake_up_irq, bp->ops->trig, "bp_wakeup_ap", bp); + if (result < 0) { + printk("%s: request_irq(%d) failed\n", __func__, bp->ops->bp_wakeup_ap); + gpio_free(pdata->bp_wakeup_ap); + return result; + } + } + + if(bp->ops->init) + bp->ops->init(bp); + + enable_irq_wake(bp->ops->bp_wakeup_ap); + wake_lock_init(&bp->bp_wakelock, WAKE_LOCK_SUSPEND, "bp_wakelock"); + + bp->status = BP_OFF; + + if(!bp->ops->private_miscdev) + { + bp->fops.owner = THIS_MODULE; + bp->fops.open = bp_dev_open; + bp->fops.write = bp_dev_write; + bp->fops.release = bp_dev_release; + bp->fops.unlocked_ioctl = bp_dev_ioctl; + + bp->miscdev.minor = MISC_DYNAMIC_MINOR; + if(bp->ops->misc_name) + bp->miscdev.name = bp->ops->misc_name; + else + bp->miscdev.name = "bp-auto"; + bp->miscdev.fops = &bp->fops; + } + else + { + memcpy(&bp->miscdev, bp->ops->private_miscdev, sizeof(*bp->ops->private_miscdev)); + + } + + result = misc_register(&bp->miscdev); + if (result < 0) { + printk("misc_register err\n"); + return result; + } + + bp->id_fops.owner = THIS_MODULE; + bp->id_fops.open = bp_id_open; + bp->id_fops.release = bp_id_release; + bp->id_fops.unlocked_ioctl = bp_id_ioctl; + + bp->id_miscdev.minor = MISC_DYNAMIC_MINOR; + bp->id_miscdev.name = "bp_id"; + bp->id_miscdev.fops = &bp->id_fops; + result = misc_register(&bp->id_miscdev); + if (result < 0) { + printk("misc_register err\n"); + return result; + } + + g_bp = bp; + + platform_set_drvdata(pdev, bp); + + printk("%s:init success\n",__func__); + return result; + +} + +int bp_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct bp_private_data *bp = platform_get_drvdata(pdev); + + if(bp->ops->suspend) + bp->ops->suspend(bp); + + return 0; +} + +int bp_resume(struct platform_device *pdev) +{ + struct bp_private_data *bp = platform_get_drvdata(pdev); + + if(bp->ops->resume) + bp->ops->resume(bp); + + return 0; +} + +void bp_shutdown(struct platform_device *pdev) +{ + struct bp_private_data *bp = platform_get_drvdata(pdev); + + if(bp->ops->shutdown) + bp->ops->shutdown(bp); + + if(bp->ops->bp_power != BP_UNKNOW_DATA) + { + gpio_free(bp->ops->bp_power); + } + + if(bp->ops->bp_en != BP_UNKNOW_DATA) + { + gpio_free(bp->ops->bp_en); + + } + + if(bp->ops->bp_reset != BP_UNKNOW_DATA) + { + gpio_free(bp->ops->bp_reset); + } + + if(bp->ops->ap_ready != BP_UNKNOW_DATA) + { + gpio_free(bp->ops->ap_ready); + + } + + if(bp->ops->bp_ready != BP_UNKNOW_DATA) + { + gpio_free(bp->ops->bp_ready); + + } + + if(bp->ops->ap_wakeup_bp != BP_UNKNOW_DATA) + { + gpio_free(bp->ops->ap_wakeup_bp); + + } + + if(bp->ops->bp_wakeup_ap != BP_UNKNOW_DATA) + { + gpio_free(bp->ops->bp_wakeup_ap); + + } + + if(bp->ops->bp_usb_en != BP_UNKNOW_DATA) + { + gpio_free(bp->ops->bp_usb_en); + + } + + if(bp->ops->bp_uart_en != BP_UNKNOW_DATA) + { + gpio_free(bp->ops->bp_uart_en); + + } + + if(bp->pdata->exit_platform_hw) + bp->pdata->exit_platform_hw(); + + kfree(bp); + +} + + +int bp_register_slave(struct bp_private_data *bp, + struct bp_platform_data *slave_pdata, + struct bp_operate *(*get_bp_ops)(void)) +{ + int result = 0; + struct bp_operate *ops = get_bp_ops(); + if((ops->bp_id >= BP_ID_NUM) || (ops->bp_id <= BP_ID_INVALID)) + { + printk("%s:%s id is error %d\n", __func__, ops->name, ops->bp_id); + return -1; + } + g_bp_ops[ops->bp_id] = ops; + printk("%s:%s,id=%d\n",__func__,g_bp_ops[ops->bp_id]->name, ops->bp_id); + return result; +} + + +int bp_unregister_slave(struct bp_private_data *bp, + struct bp_platform_data *slave_pdata, + struct bp_operate *(*get_bp_ops)(void)) +{ + int result = 0; + struct bp_operate *ops = get_bp_ops(); + if((ops->bp_id >= BP_ID_NUM) || (ops->bp_id <= BP_ID_INVALID)) + { + printk("%s:%s id is error %d\n", __func__, ops->name, ops->bp_id); + return -1; + } + printk("%s:%s,id=%d\n",__func__,g_bp_ops[ops->bp_id]->name, ops->bp_id); + g_bp_ops[ops->bp_id] = NULL; + return result; +} + + +static struct platform_driver bp_driver = { + .probe = bp_probe, + .shutdown = bp_shutdown, + .suspend = bp_suspend, + .resume = bp_resume, + .driver = { + .name = "bp-auto", + .owner = THIS_MODULE, + }, +}; + +static int __init bp_init(void) +{ + int ret ; + bp_class = class_create(THIS_MODULE, "bp-auto"); + ret = class_create_file(bp_class, &class_attr_bp_status); + if (ret) + { + printk("Fail to create class bp-auto\n"); + } + return platform_driver_register(&bp_driver); +} + +static void __exit bp_exit(void) +{ + platform_driver_unregister(&bp_driver); + class_remove_file(bp_class, &class_attr_bp_status); +} + +module_init(bp_init); +module_exit(bp_exit); + +MODULE_AUTHOR("ROCKCHIP Corporation:lw@rock-chips.com"); +MODULE_DESCRIPTION("device interface for auto modem driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/misc/bp/chips/Kconfig b/drivers/misc/bp/chips/Kconfig new file mode 100644 index 000000000000..70db74d5e27c --- /dev/null +++ b/drivers/misc/bp/chips/Kconfig @@ -0,0 +1,7 @@ +config BP_AUTO_MT6229 +bool "modem mt6229" + default n + +config BP_AUTO_MU509 +bool "modem mu509" + default n \ No newline at end of file diff --git a/drivers/misc/bp/chips/Makefile b/drivers/misc/bp/chips/Makefile new file mode 100644 index 000000000000..0f62417b82cf --- /dev/null +++ b/drivers/misc/bp/chips/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_BP_AUTO_MT6229) += mt6229.o +obj-$(CONFIG_BP_AUTO_MU509) += mu509.o \ No newline at end of file diff --git a/drivers/misc/bp/chips/mt6229.c b/drivers/misc/bp/chips/mt6229.c new file mode 100755 index 000000000000..4e9a9f1be65c --- /dev/null +++ b/drivers/misc/bp/chips/mt6229.c @@ -0,0 +1,282 @@ +/* drivers/misc/bp/chips/mt6229.c + * + * Copyright (C) 2012-2015 ROCKCHIP. + * Author: luowei + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#if 0 +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + + +/****************operate according to bp chip:start************/ +static int bp_active(struct bp_private_data *bp, int enable) +{ + int result = 0; + if(enable) + { + gpio_direction_output(bp->ops->bp_en, GPIO_LOW); + gpio_direction_output(bp->ops->bp_usb_en, GPIO_HIGH); + gpio_direction_output(bp->ops->bp_uart_en, GPIO_LOW); + + gpio_direction_output(bp->ops->ap_ready, GPIO_HIGH); + } + else + { + gpio_direction_output(bp->ops->bp_en, GPIO_HIGH); + gpio_direction_output(bp->ops->bp_usb_en, GPIO_LOW); + gpio_direction_output(bp->ops->bp_uart_en, GPIO_HIGH); + gpio_direction_output(bp->ops->ap_ready, GPIO_LOW); + } + + return result; +} + +static int ap_wake_bp(struct bp_private_data *bp, int wake) +{ + int result = 0; + if(wake) + { + gpio_direction_output(bp->ops->bp_uart_en, GPIO_LOW); + msleep(2000); + gpio_direction_output(bp->ops->ap_ready, GPIO_HIGH); + gpio_direction_output(bp->ops->bp_usb_en, GPIO_HIGH); + } + else + { + gpio_direction_output(bp->ops->bp_usb_en, GPIO_HIGH); + gpio_direction_output(bp->ops->bp_uart_en, GPIO_HIGH); + gpio_direction_output(bp->ops->ap_ready, GPIO_LOW); + } + + return result; + +} + +static void ap_wake_bp_work(struct work_struct *work) +{ + struct delayed_work *wakeup_work = container_of(work, struct delayed_work, work); + struct bp_private_data *bp = container_of(wakeup_work, struct bp_private_data, wakeup_work); + + if(bp->suspend_status) + { + if(bp->ops->ap_wake_bp) + bp->ops->ap_wake_bp(bp, 0); + } + else + { + if(bp->ops->ap_wake_bp) + bp->ops->ap_wake_bp(bp, 1); + } +} + + +static int bp_init(struct bp_private_data *bp) +{ + int result = 0; + gpio_direction_output(bp->ops->bp_power, GPIO_HIGH); + msleep(1000); + if(bp->ops->active) + bp->ops->active(bp, 1); + + INIT_DELAYED_WORK(&bp->wakeup_work, ap_wake_bp_work); + return result; +} + +static int bp_wake_ap(struct bp_private_data *bp) +{ + int result = 0; + + if(bp->suspend_status) + { + bp->suspend_status = 0; + wake_lock_timeout(&bp->bp_wakelock, 10 * HZ); + } + + return result; +} + + +static int bp_shutdown(struct bp_private_data *bp) +{ + int result = 0; + + if(bp->ops->active) + bp->ops->active(bp, 0); + + cancel_delayed_work_sync(&bp->wakeup_work); + + return result; +} + + +static int read_status(struct bp_private_data *bp) +{ + int result = 0; + + return result; +} + + +static int write_status(struct bp_private_data *bp) +{ + int result = 0; + + if (bp->status == BP_ON) + { + gpio_direction_output(bp->ops->bp_usb_en, GPIO_HIGH); + gpio_direction_output(bp->ops->bp_uart_en,GPIO_LOW); + } + else if(bp->status == BP_OFF) + { + gpio_direction_output(bp->ops->bp_usb_en, GPIO_LOW); + gpio_direction_output(bp->ops->bp_uart_en,GPIO_HIGH); + } + else + { + printk("%s, invalid parameter \n", __FUNCTION__); + } + + return result; +} + + +static int bp_suspend(struct bp_private_data *bp) +{ + int result = 0; + + if(!bp->suspend_status) + { + bp->suspend_status = 1; + if(bp->ops->ap_wake_bp) + bp->ops->ap_wake_bp(bp, 0); + } + + return result; +} + + + + +static int bp_resume(struct bp_private_data *bp) +{ + if(bp->suspend_status) + { + bp->suspend_status = 0; + PREPARE_DELAYED_WORK(&bp->wakeup_work, ap_wake_bp_work); + schedule_delayed_work(&bp->wakeup_work, 0); + } + + return 0; +} + + +struct bp_operate bp_mt6229_ops = { + .name = "mt6229", + .bp_id = BP_ID_MT6229, + .bp_bus = BP_BUS_TYPE_USB_UART, + .bp_pid = 0, + .bp_vid = 0, + .bp_power = RK30_PIN6_PB2, // 3g_power + .bp_en = RK30_PIN2_PB6, // 3g_en + .bp_reset = BP_UNKNOW_DATA, + .ap_ready = RK30_PIN2_PB7, // + .bp_ready = BP_UNKNOW_DATA, + .ap_wakeup_bp = BP_UNKNOW_DATA, + .bp_wakeup_ap = RK30_PIN6_PA1, // + .bp_uart_en = RK30_PIN2_PC1, //EINT9 + .bp_usb_en = RK30_PIN2_PC0, //W_disable + .trig = IRQF_TRIGGER_RISING, + + .active = bp_active, + .init = bp_init, + .ap_wake_bp = ap_wake_bp, + .bp_wake_ap = bp_wake_ap, + .shutdown = bp_shutdown, + .read_status = read_status, + .write_status = write_status, + .suspend = bp_suspend, + .resume = bp_resume, + .misc_name = "mt6229", + .private_miscdev = NULL, +}; + +/****************operate according to bp chip:end************/ + +//function name should not be changed +static struct bp_operate *bp_get_ops(void) +{ + return &bp_mt6229_ops; +} + +static int __init bp_mt6229_init(void) +{ + struct bp_operate *ops = bp_get_ops(); + int result = 0; + result = bp_register_slave(NULL, NULL, bp_get_ops); + if(result) + { + return result; + } + + if(ops->private_miscdev) + { + result = misc_register(ops->private_miscdev); + if (result < 0) { + printk("%s:misc_register err\n",__func__); + return result; + } + } + + DBG("%s\n",__func__); + return result; +} + +static void __exit bp_mt6229_exit(void) +{ + //struct bp_operate *ops = bp_get_ops(); + bp_unregister_slave(NULL, NULL, bp_get_ops); +} + + +subsys_initcall(bp_mt6229_init); +module_exit(bp_mt6229_exit); + diff --git a/drivers/misc/bp/chips/mu509.c b/drivers/misc/bp/chips/mu509.c new file mode 100755 index 000000000000..f4228e71a90d --- /dev/null +++ b/drivers/misc/bp/chips/mu509.c @@ -0,0 +1,281 @@ +/* drivers/misc/bp/chips/mu509.c + * + * Copyright (C) 2012-2015 ROCKCHIP. + * Author: luowei + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#if 0 +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + + +/****************operate according to bp chip:start************/ +static int bp_active(struct bp_private_data *bp, int enable) +{ + int result = 0; + if(enable) + { + gpio_direction_output(bp->ops->bp_reset, GPIO_HIGH); + msleep(100); + gpio_direction_output(bp->ops->bp_reset, GPIO_LOW); + gpio_direction_output(bp->ops->bp_en, GPIO_LOW); + msleep(1000); + gpio_direction_output(bp->ops->bp_en, GPIO_HIGH); + msleep(700); + gpio_direction_output(bp->ops->bp_en, GPIO_LOW); + gpio_direction_output(bp->ops->ap_wakeup_bp, GPIO_LOW); + } + else + { + gpio_set_value(bp->ops->bp_en, GPIO_LOW); + gpio_set_value(bp->ops->bp_en, GPIO_HIGH); + msleep(2500); + gpio_set_value(bp->ops->bp_en, GPIO_LOW); + } + + return result; +} + +static int ap_wake_bp(struct bp_private_data *bp, int wake) +{ + int result = 0; + + gpio_direction_output(bp->ops->ap_wakeup_bp, wake); + + return result; + +} + +static void ap_wake_bp_work(struct work_struct *work) +{ + struct delayed_work *wakeup_work = container_of(work, struct delayed_work, work); + struct bp_private_data *bp = container_of(wakeup_work, struct bp_private_data, wakeup_work); + + if(bp->suspend_status) + { + if(bp->ops->ap_wake_bp) + bp->ops->ap_wake_bp(bp, 0); + } + else + { + if(bp->ops->ap_wake_bp) + bp->ops->ap_wake_bp(bp, 1); + } +} + + +static int bp_init(struct bp_private_data *bp) +{ + int result = 0; + gpio_direction_output(bp->ops->bp_power, GPIO_HIGH); + msleep(1000); + if(bp->ops->active) + bp->ops->active(bp, 1); + + INIT_DELAYED_WORK(&bp->wakeup_work, ap_wake_bp_work); + return result; +} + +static int bp_reset(struct bp_private_data *bp) +{ + gpio_direction_output(bp->ops->bp_power, GPIO_HIGH); + msleep(100); + gpio_direction_output(bp->ops->bp_power, GPIO_LOW); + + return 0; +} + +static int bp_wake_ap(struct bp_private_data *bp) +{ + int result = 0; + + if(bp->suspend_status) + { + bp->suspend_status = 0; + wake_lock_timeout(&bp->bp_wakelock, 10 * HZ); + } + + return result; +} + + +static int bp_shutdown(struct bp_private_data *bp) +{ + int result = 0; + + if(bp->ops->active) + bp->ops->active(bp, 0); + + cancel_delayed_work_sync(&bp->wakeup_work); + + return result; +} + + +static int read_status(struct bp_private_data *bp) +{ + int result = 0; + + return result; +} + + +static int write_status(struct bp_private_data *bp) +{ + int result = 0; + + if (bp->status == BP_ON) + { + gpio_direction_output(bp->ops->bp_usb_en, GPIO_HIGH); + gpio_direction_output(bp->ops->bp_uart_en,GPIO_LOW); + } + else if(bp->status == BP_OFF) + { + gpio_direction_output(bp->ops->bp_usb_en, GPIO_LOW); + gpio_direction_output(bp->ops->bp_uart_en,GPIO_HIGH); + } + else + { + printk("%s, invalid parameter \n", __FUNCTION__); + } + + return result; +} + +static int bp_suspend(struct bp_private_data *bp) +{ + int result = 0; + + if(!bp->suspend_status) + { + bp->suspend_status = 1; + } + + return result; +} + + + + +static int bp_resume(struct bp_private_data *bp) +{ + if(bp->suspend_status) + { + bp->suspend_status = 0; + gpio_set_value(bp->ops->ap_wakeup_bp, GPIO_LOW); + } + + return 0; +} + + +struct bp_operate bp_mu509_ops = { + .name = "mu509", + .bp_id = BP_ID_MU509, + .bp_bus = BP_BUS_TYPE_USB_UART, + .bp_pid = 0, + .bp_vid = 0, + .bp_power = RK30_PIN6_PB2, // 3g_power + .bp_en = RK30_PIN2_PB6, // 3g_en + .bp_reset = RK30_PIN2_PC1, + .ap_ready = BP_UNKNOW_DATA, // + .bp_ready = BP_UNKNOW_DATA, + .ap_wakeup_bp = RK30_PIN2_PB7, + .bp_wakeup_ap = RK30_PIN6_PA1, // + .bp_uart_en = BP_UNKNOW_DATA, //EINT9 + .bp_usb_en = BP_UNKNOW_DATA, //W_disable + .trig = IRQF_TRIGGER_FALLING, + + .active = bp_active, + .init = bp_init, + .reset = bp_reset, + .ap_wake_bp = ap_wake_bp, + .bp_wake_ap = bp_wake_ap, + .shutdown = bp_shutdown, + .read_status = read_status, + .write_status = write_status, + .suspend = bp_suspend, + .resume = bp_resume, + .misc_name = "mu509", + .private_miscdev = NULL, +}; + +/****************operate according to bp chip:end************/ + +//function name should not be changed +static struct bp_operate *bp_get_ops(void) +{ + return &bp_mu509_ops; +} + +static int __init bp_mu509_init(void) +{ + struct bp_operate *ops = bp_get_ops(); + int result = 0; + result = bp_register_slave(NULL, NULL, bp_get_ops); + if(result) + { + return result; + } + + if(ops->private_miscdev) + { + result = misc_register(ops->private_miscdev); + if (result < 0) { + printk("%s:misc_register err\n",__func__); + return result; + } + } + + DBG("%s\n",__func__); + return result; +} + +static void __exit bp_mu509_exit(void) +{ + //struct bp_operate *ops = bp_get_ops(); + bp_unregister_slave(NULL, NULL, bp_get_ops); +} + + +subsys_initcall(bp_mu509_init); +module_exit(bp_mu509_exit); + diff --git a/include/linux/bp-auto.h b/include/linux/bp-auto.h new file mode 100644 index 000000000000..a2b0a898a808 --- /dev/null +++ b/include/linux/bp-auto.h @@ -0,0 +1,135 @@ +#ifndef _BP_AUTO_H +#define _BP_AUTO_H +#include +#include + +struct bp_private_data; + +#define BP_UNKNOW_DATA -1 +#define BP_OFF 0 +#define BP_ON 1 +#define BP_SUSPEND 2 +#define BP_WAKE 3 + +#define BP_IOCTL_BASE 'm' + +#define BP_IOCTL_RESET _IOW(BP_IOCTL_BASE, 0x00, int) +#define BP_IOCTL_POWON _IOW(BP_IOCTL_BASE, 0x01, int) +#define BP_IOCTL_POWOFF _IOW(BP_IOCTL_BASE, 0x02, int) +#define BP_IOCTL_WRITE_STATUS _IOW(BP_IOCTL_BASE, 0x03, int) +#define BP_IOCTL_GET_STATUS _IOR(BP_IOCTL_BASE, 0x04, int) +#define BP_IOCTL_SET_PVID _IOW(BP_IOCTL_BASE, 0x05, int) +#define BP_IOCTL_GET_BPID _IOR(BP_IOCTL_BASE, 0x06, int) + +enum bp_id{ + BP_ID_INVALID = 0, + + BP_ID_MT6229, + BP_ID_MU509, + BP_ID_MI700, + BP_ID_MW100, + BP_ID_TD8801, + + BP_ID_NUM, +}; + + + +enum bp_bus_type{ + BP_BUS_TYPE_INVALID = 0, + + BP_BUS_TYPE_UART, + BP_BUS_TYPE_SPI, + BP_BUS_TYPE_USB, + BP_BUS_TYPE_SDIO, + BP_BUS_TYPE_USB_UART, + BP_BUS_TYPE_SPI_UART, + BP_BUS_TYPE_SDIO_UART, + + BP_BUS_TYPE_NUM_ID, +}; + +struct bp_platform_data { + int board_id; + int bp_id; + int (*init_platform_hw)(void); + int (*exit_platform_hw)(void); + + int bp_power; + int bp_en; + int bp_reset; + int ap_ready; + int bp_ready; + int ap_wakeup_bp; + int bp_wakeup_ap; + int bp_usb_en; + int bp_uart_en; + + int gpio_valid; +}; + + +struct bp_operate { + char *name; + int bp_id; + int bp_bus; + + int bp_pid; + int bp_vid; + int bp_power; + int bp_en; + int bp_reset; + int ap_ready; + int bp_ready; + int ap_wakeup_bp; + int bp_wakeup_ap; + int bp_usb_en; + int bp_uart_en; + int trig; + + int (*active)(struct bp_private_data *bp, int enable); + int (*init)(struct bp_private_data *bp); + int (*reset)(struct bp_private_data *bp); + int (*ap_wake_bp)(struct bp_private_data *bp, int wake); + int (*bp_wake_ap)(struct bp_private_data *bp); + int (*shutdown)(struct bp_private_data *bp); + int (*read_status)(struct bp_private_data *bp); + int (*write_status)(struct bp_private_data *bp); + int (*suspend)(struct bp_private_data *bp); + int (*resume)(struct bp_private_data *bp); + + char *misc_name; + struct miscdevice *private_miscdev; + +}; + + +struct bp_private_data { + struct device *dev; + int status; + int suspend_status; + struct wake_lock bp_wakelock; + struct delayed_work wakeup_work; /*report second event*/ + struct bp_platform_data *pdata; + struct bp_operate *ops; + struct file_operations fops; + struct miscdevice miscdev; + struct file_operations id_fops; + struct miscdevice id_miscdev; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif +}; + +extern int bp_register_slave(struct bp_private_data *bp, + struct bp_platform_data *slave_pdata, + struct bp_operate *(*get_bp_ops)(void)); + + +extern int bp_unregister_slave(struct bp_private_data *bp, + struct bp_platform_data *slave_pdata, + struct bp_operate *(*get_bp_ops)(void)); + + +#endif +