From: James Wylder Date: Thu, 10 Feb 2011 17:03:22 +0000 (-0600) Subject: misc: radio_ctrl: Create radio control interface X-Git-Tag: firefly_0821_release~9834^2~106 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=5c9ec517d075e052bc99eacc3d14382e70ddb184;p=firefly-linux-kernel-4.4.55.git misc: radio_ctrl: Create radio control interface Migrate the mdm6x00 sysfs interface to its own driver so that additional drivers can be added. Change-Id: Ia647f298adaba75ac296b2b33e632ba55e4dd55a Signed-off-by: James Wylder --- diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index d9b6f3b5f9bf..9c4ef5015d6e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -210,17 +210,6 @@ config KERNEL_DEBUGGER_CORE Generic kernel debugging command processor used by low level (interrupt context) platform-specific debuggers. -config MDM6600_CTRL - bool "Motorola Modem Controller" - default n - ---help--- - Enables the device driver to control and interface with - the modem co-processor. This module is needed to monitor - modem panics, interact with the modem during factory resets, - and allow modem power up/down support. - - If unsure, say N. - config SGI_XP tristate "Support communication between SGI SSIs" depends on NET @@ -512,5 +501,6 @@ source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" source "drivers/misc/ts27010mux/Kconfig" source "drivers/misc/iwmc3200top/Kconfig" +source "drivers/misc/radio_ctrl/Kconfig" endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 94e552057368..c7c058e406cf 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -47,7 +47,7 @@ obj-$(CONFIG_SENSORS_MAX9635) += max9635.o obj-$(CONFIG_SENSORS_NCT1008) += nct1008.o obj-$(CONFIG_SENSORS_L3G4200D) += l3g4200d.o obj-$(CONFIG_GPS_GPIO_BRCM4750) += gps-gpio-brcm4750.o -obj-$(CONFIG_MDM6600_CTRL) += mdm6600_ctrl.o +obj-y += radio_ctrl/ obj-$(CONFIG_SENSORS_MOTO_BMP085) += moto_bmp085.o obj-$(CONFIG_TEGRA_CRYPTO_DEV) += tegra-cryptodev.o obj-$(CONFIG_TS27010MUX) += ts27010mux/ diff --git a/drivers/misc/mdm6600_ctrl.c b/drivers/misc/mdm6600_ctrl.c deleted file mode 100644 index 7eb2a688a463..000000000000 --- a/drivers/misc/mdm6600_ctrl.c +++ /dev/null @@ -1,656 +0,0 @@ -/* - Copyright (C) 2010 Motorola, Inc. - - 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. - - 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 - -#define AP_STATUS_BP_PANIC_ACK 0x00 -#define AP_STATUS_DATA_ONLY_BYPASS 0x01 -#define AP_STATUS_FULL_BYPASS 0x02 -#define AP_STATUS_NO_BYPASS 0x03 -#define AP_STATUS_BP_SHUTDOWN_REQ 0x04 -#define AP_STATUS_UNDEFINED 0x07 - -#define BP_STATUS_PANIC 0x00 -#define BP_STATUS_PANIC_BUSY_WAIT 0x01 -#define BP_STATUS_QC_DLOAD 0x02 -#define BP_STATUS_RAM_DOWNLOADER 0x03 -#define BP_STATUS_PHONE_CODE_AWAKE 0x04 -#define BP_STATUS_PHONE_CODE_ASLEEP 0x05 -#define BP_STATUS_SHUTDOWN_ACK 0x06 -#define BP_STATUS_UNDEFINED 0x07 - - -#define LOOP_DELAY_TIME_MS 500 - -static const char *mdmctrl = "mdm6600_ctrl"; - -static const char *bp_status[8] = { - [BP_STATUS_PANIC] = "panic", - [BP_STATUS_PANIC_BUSY_WAIT] = "panic busy wait", - [BP_STATUS_QC_DLOAD] = "qc dload", - [BP_STATUS_RAM_DOWNLOADER] = "ram downloader", - [BP_STATUS_PHONE_CODE_AWAKE] = "awake", - [BP_STATUS_PHONE_CODE_ASLEEP] = "asleep", - [BP_STATUS_SHUTDOWN_ACK] = "shutdown ack", - [BP_STATUS_UNDEFINED] = "undefined", -}; - -static const char *bp_power_state[2] = { - "off", - "on", -}; - -#define BP_STATUS_MAX_LENGTH 32 -#define BP_COMMAND_MAX_LENGTH 32 - -/* structure to keep track of gpio, irq, and irq enabled info */ -struct gpio_info { - int irq; - struct work_struct work; -}; - -struct mdm_ctrl_info { - struct mdm_ctrl_platform_data *pdata; - struct gpio_info gpios[MDM_CTRL_NUM_GPIOS]; -}; - -static struct mdm_ctrl_info mdm_ctrl; - -static DEFINE_MUTEX(mdm_ctrl_info_lock); - -struct workqueue_struct *working_queue = NULL; - -static dev_t dev_number; -struct class *radio_cls = NULL; -struct device *mdm_dev = NULL; - -static unsigned int bp_status_idx = BP_STATUS_UNDEFINED; -static unsigned int bp_power_idx = 0; - -static void __devexit mdm_ctrl_shutdown(struct platform_device *pdev); -static void mdm_ctrl_powerup(void); -static void mdm_ctrl_set_bootmode(int mode); - -static const char *bp_status_string(unsigned int stat) -{ - if (stat < ARRAY_SIZE(bp_status)) - return bp_status[stat]; - else - return "status out of range"; -} - -static const char *bp_power_state_string(unsigned int stat) -{ - if (stat < ARRAY_SIZE(bp_power_state)) - return bp_power_state[stat]; - else - return "status out of range"; -} - -static ssize_t mdm_status_show(struct device *dev, - struct device_attribute *attr, char *buff) -{ - ssize_t status = 0; - status = snprintf(buff, BP_STATUS_MAX_LENGTH, "%s\n", - bp_status_string(bp_status_idx)); - - return status; -} - -static ssize_t mdm_power_show(struct device *dev, - struct device_attribute *attr, char *buff) -{ - ssize_t status = 0; - status = snprintf(buff, BP_STATUS_MAX_LENGTH, "%s\n", - bp_power_state_string(bp_power_idx)); - - return status; -} - -static ssize_t mdm_user_command(struct device *dev, - struct device_attribute *attr, const char *buff, - size_t size) -{ - char tmp[BP_COMMAND_MAX_LENGTH]; - char *post_strip = NULL; - - if (size > BP_COMMAND_MAX_LENGTH - 1) { - return size; - } - - /* strip whitespaces if any */ - memcpy(tmp, buff, size); - tmp[size] = '\0'; - post_strip = strim(tmp); - - pr_info("%s: user command = %s\n", mdmctrl, post_strip); - - if (strcmp(post_strip,"shutdown") == 0) { - mdm_ctrl_shutdown(NULL); - } else if (strcmp(post_strip,"powerup") == 0) { - mdm_ctrl_powerup(); - } else if (strcmp(post_strip,"bootmode_normal") == 0) { - mdm_ctrl_set_bootmode(0); - } else if (strcmp(post_strip,"bootmode_flash") == 0) { - mdm_ctrl_set_bootmode(1); - } - - return size; -} - -static DEVICE_ATTR(status, 0444, mdm_status_show, NULL); -static DEVICE_ATTR(power_status, 0444, mdm_power_show, NULL); -static DEVICE_ATTR(command, 0200, NULL, mdm_user_command); - -static unsigned int mdm_gpio_get_value(struct mdm_ctrl_gpio gpio) -{ - return gpio_get_value(gpio.number); -} - -static void mdm_gpio_set_value(struct mdm_ctrl_gpio gpio, - unsigned int value) -{ - gpio_set_value(gpio.number, value); -} - -static void mdm_gpio_free(struct mdm_ctrl_gpio *gpio) -{ - if (gpio->allocated) - gpio_free(gpio->number); - gpio->allocated = 0; -} - -static int mdm_gpio_setup(struct mdm_ctrl_gpio *gpio) -{ - if (gpio_request(gpio->number, gpio->name)) { - printk(KERN_ERR "failed to aquire gpio %s", gpio->name); - return -1; - } - gpio->allocated = 1; - gpio_export(gpio->number, false); - if (gpio->direction == MDM_GPIO_DIRECTION_IN) - gpio_direction_input(gpio->number); - else if (gpio->direction == MDM_GPIO_DIRECTION_OUT) - gpio_direction_output(gpio->number, gpio->default_value); - return 0; -} - -static unsigned int get_bp_status(void) -{ - unsigned int status = BP_STATUS_UNDEFINED; - unsigned int bp_status[3] = {0}; - - mutex_lock(&mdm_ctrl_info_lock); - if (mdm_ctrl.pdata) { - bp_status[0] = mdm_gpio_get_value( - mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_BP_STATUS_0]); - bp_status[1] = mdm_gpio_get_value( - mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_BP_STATUS_1]); - bp_status[2] = mdm_gpio_get_value( - mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_BP_STATUS_2]); - } - mutex_unlock(&mdm_ctrl_info_lock); - - status = ((bp_status[2] & 0x1) << 2) | - ((bp_status[1] & 0x1) << 1) | - (bp_status[0] & 0x1); - - return status; -} - -static unsigned int get_bp_power_status(void) -{ - unsigned int status = 0; - - mutex_lock(&mdm_ctrl_info_lock); - if (mdm_ctrl.pdata) { - status = mdm_gpio_get_value( - mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_BP_RESOUT]); - } - - mutex_unlock(&mdm_ctrl_info_lock); - - return status & 0x1; -} - -static unsigned int get_ap_status(void) -{ - unsigned int status = AP_STATUS_UNDEFINED; - unsigned int ap_status[3] = {0}; - - mutex_lock(&mdm_ctrl_info_lock); - if (mdm_ctrl.pdata) { - ap_status[0] = mdm_gpio_get_value( - mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_AP_STATUS_0]); - ap_status[1] = mdm_gpio_get_value( - mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_AP_STATUS_1]); - ap_status[2] = mdm_gpio_get_value( - mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_AP_STATUS_2]); - } - mutex_unlock(&mdm_ctrl_info_lock); - - status = ((ap_status[2] & 0x1) << 2) | - ((ap_status[1] & 0x1) << 1) | - (ap_status[0] & 0x1); - - return status; -} - -static void set_ap_status(unsigned int status) -{ - mutex_lock(&mdm_ctrl_info_lock); - if (mdm_ctrl.pdata) { - mdm_gpio_set_value( - mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_AP_STATUS_0], - (status & 0x1)); - mdm_gpio_set_value( - mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_AP_STATUS_1], - (status >> 1) & 0x1); - mdm_gpio_set_value( - mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_AP_STATUS_2], - (status >> 2) & 0x1); - } - mutex_unlock(&mdm_ctrl_info_lock); -} - -static void set_bp_pwron(int on) -{ - mutex_lock(&mdm_ctrl_info_lock); - if ((mdm_ctrl.pdata) && ((on == 1) || (on == 0))) { - mdm_gpio_set_value( - mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_BP_PWRON], - on); - } - mutex_unlock(&mdm_ctrl_info_lock); -} - -static void set_bp_resin(int on) -{ - mutex_lock(&mdm_ctrl_info_lock); - if ((mdm_ctrl.pdata) && ((on == 1) || (on == 0))) { - mdm_gpio_set_value( - mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_BP_RESIN], - on); - } - mutex_unlock(&mdm_ctrl_info_lock); -} - -static void update_bp_status(void) { - - static int bp_status_prev_idx = BP_STATUS_UNDEFINED; - - bp_status_prev_idx = bp_status_idx; - bp_status_idx = get_bp_status(); - bp_power_idx = get_bp_power_status(); - - pr_info("%s: modem status: %s -> %s [power %s]", mdmctrl, - bp_status_string(bp_status_prev_idx), - bp_status_string(bp_status_idx), - bp_power_state_string(bp_power_idx)); - - kobject_uevent(&mdm_dev->kobj, KOBJ_CHANGE); -} - -static void mdm_ctrl_powerup(void) -{ - unsigned int bp_status; - - pr_info("%s: Starting up modem.", mdmctrl); - - bp_status = get_bp_status(); - pr_info("%s: Initial Modem status %s [0x%x]", - mdmctrl, bp_status_string(bp_status), bp_status); - - set_ap_status(AP_STATUS_NO_BYPASS); - pr_info("%s: ap_status set to %d", mdmctrl, get_ap_status()); - msleep(100); - set_bp_resin(0); - msleep(100); - /* Toggle the power, delaying to allow modem to respond */ - set_bp_pwron(1); - msleep(100); - set_bp_pwron(0); - - /* now let user handles bp status change through uevent */ -} - -static void mdm_ctrl_set_bootmode(int mode) -{ - unsigned int bp_status; - - mutex_lock(&mdm_ctrl_info_lock); - if (mdm_ctrl.pdata && ((mode == 0) || (mode == 1))) { - gpio_request(mdm_ctrl.pdata->cmd_gpios.cmd1, - "BP Command 1"); - gpio_direction_output(mdm_ctrl.pdata->cmd_gpios.cmd1, - mode); - gpio_request(mdm_ctrl.pdata->cmd_gpios.cmd2, - "BP Command 2"); - gpio_direction_output(mdm_ctrl.pdata->cmd_gpios.cmd2, - mode); - - } - mutex_unlock(&mdm_ctrl_info_lock); -} - -static void irq_worker(struct work_struct *work) -{ - struct gpio_info *gpio = container_of(work, struct gpio_info, work); - update_bp_status(); - enable_irq(gpio->irq); -} - -static irqreturn_t irq_handler(int irq, void *data) -{ - struct gpio_info *gpio = (struct gpio_info *) data; - - disable_irq_nosync(irq); - queue_work(working_queue, &gpio->work); - - return IRQ_HANDLED; -} - -static int mdm_gpio_setup_internal(struct mdm_ctrl_platform_data *pdata) -{ - int i; - int rv = 0; - struct gpio_info *gpio_data = NULL; - - mutex_lock(&mdm_ctrl_info_lock); - memset(&mdm_ctrl, 0, sizeof (mdm_ctrl)); - - mdm_ctrl.pdata = pdata; - - for (i = 0; i < MDM_CTRL_NUM_GPIOS; i++) { - gpio_data = &mdm_ctrl.gpios[i]; - if (pdata->gpios[i].direction == MDM_GPIO_DIRECTION_IN) { - INIT_WORK(&gpio_data->work, irq_worker); - gpio_data->irq = gpio_to_irq(pdata->gpios[i].number); - rv = request_irq(gpio_data->irq, irq_handler, - IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING, - pdata->gpios[i].name, gpio_data); - if (rv < 0) { - pr_err("%s: Cannot request IRQ (%d) from kernel!", - mdmctrl, gpio_data->irq); - } else { - enable_irq_wake(gpio_data->irq); - } - } - } - - mutex_unlock(&mdm_ctrl_info_lock); - return rv; -} - -static void mdm_gpio_cleanup_internal(void) -{ - int i; - struct gpio_info *gpio_data = NULL; - - mutex_lock(&mdm_ctrl_info_lock); - - for (i = 0; i < MDM_CTRL_NUM_GPIOS; i++) { - gpio_data = &mdm_ctrl.gpios[i]; - - if (gpio_data->irq) { - disable_irq_wake(gpio_data->irq); - free_irq(gpio_data->irq, gpio_data); - } - } - memset(&mdm_ctrl, 0, sizeof (mdm_ctrl)); - mutex_unlock(&mdm_ctrl_info_lock); -} - -static int __devinit mdm_ctrl_probe(struct platform_device *pdev) -{ - int i; - struct mdm_ctrl_platform_data *pdata = pdev->dev.platform_data; - - dev_info(&pdev->dev, "mdm_ctrl_probe"); - - if (alloc_chrdev_region(&dev_number, 0, 1, "mdm_ctrl") < 0) { - dev_err(&pdev->dev, "Can't register new device."); - return -1; - } - - /* /sys/class/radio */ - radio_cls = class_create(THIS_MODULE, "radio"); - if (IS_ERR(radio_cls)) { - dev_err(&pdev->dev, "Failed to create radio class."); - goto err_cls; - } - - /* /sys/class/radio/mdm6600 */ - mdm_dev = device_create(radio_cls, NULL, dev_number, NULL, "mdm6600"); - if (IS_ERR(mdm_dev)) { - dev_err(&pdev->dev, "Failed to create mdm_dev."); - goto err_mdm; - } - - /* /sys/class/radio/mdm6600/status */ - if (device_create_file(mdm_dev, &dev_attr_status) > 0) { - dev_err(&pdev->dev, "Failed to create status sysfile."); - goto err_status; - } - - /* /sys/class/radio/mdm6600/power_status */ - if (device_create_file(mdm_dev, &dev_attr_power_status) > 0) { - dev_err(&pdev->dev, "Failed to create power sysfile ."); - goto err_power; - } - - /* /sys/class/radio/mdm6600/command */ - if (device_create_file(mdm_dev, &dev_attr_command) > 0) { - dev_err(&pdev->dev, "Failed to create command sysfile."); - goto err_command; - } - - for (i = 0; i < MDM_CTRL_NUM_GPIOS; i++) { - if (mdm_gpio_setup(&pdata->gpios[i])) { - dev_err(&pdev->dev, "failed to aquire gpio %d\n", - pdata->gpios[i].number); - goto probe_cleanup; - } - } - - working_queue = create_singlethread_workqueue("mdm_ctrl_wq"); - if (!working_queue) { - dev_err(&pdev->dev, "Cannot create work queue."); - goto probe_err; - } - - if (mdm_gpio_setup_internal(pdata) < 0) { - dev_err(&pdev->dev, "Failed to setup bp status irq"); - goto err_setup; - } - - update_bp_status(); - - return 0; - -err_setup: - mdm_gpio_cleanup_internal(); - -probe_err: - destroy_workqueue(working_queue); - -probe_cleanup: - for (i = 0; i < MDM_CTRL_NUM_GPIOS; i++) - mdm_gpio_free(&pdata->gpios[i]); - -err_command: - device_remove_file(mdm_dev, &dev_attr_command); - -err_power: - device_remove_file(mdm_dev, &dev_attr_power_status); - -err_status: - device_remove_file(mdm_dev, &dev_attr_status); - -err_mdm: - if (!IS_ERR_OR_NULL(mdm_dev)) { - device_destroy(radio_cls, dev_number); - mdm_dev = NULL; - } - -err_cls: - if (!IS_ERR_OR_NULL(radio_cls)) { - class_destroy(radio_cls); - radio_cls = NULL; - } - - return -1; -} - -static int __devexit mdm_ctrl_remove(struct platform_device *pdev) -{ - int i; - struct mdm_ctrl_platform_data *pdata = pdev->dev.platform_data; - - dev_info(&pdev->dev, "cleanup\n"); - - mdm_gpio_cleanup_internal(); - - if (working_queue) - destroy_workqueue(working_queue); - - for (i = 0; i < MDM_CTRL_NUM_GPIOS; i++) - mdm_gpio_free(&pdata->gpios[i]); - - device_remove_file(mdm_dev, &dev_attr_command); - device_remove_file(mdm_dev, &dev_attr_power_status); - device_remove_file(mdm_dev, &dev_attr_status); - - if (!IS_ERR_OR_NULL(mdm_dev)) { - device_destroy(radio_cls, dev_number); - mdm_dev = NULL; - } - - if (!IS_ERR_OR_NULL(radio_cls)) { - class_destroy(radio_cls); - radio_cls = NULL; - } - - return 0; -} - -static unsigned int __devexit bp_shutdown_wait(unsigned int delay_sec) -{ - unsigned int i, loop_count; - unsigned int bp_status; - unsigned int gpio_value; - unsigned int pd_failure = 1; - - loop_count = (delay_sec * 1000) / LOOP_DELAY_TIME_MS; - - for (i = 0; i < loop_count; i++) { - msleep(LOOP_DELAY_TIME_MS); - bp_status = get_bp_status(); - if (bp_status == BP_STATUS_SHUTDOWN_ACK) { - pr_info("%s: Modem powered off (with ack).", mdmctrl); - pd_failure = 0; - break; - } - - gpio_value = get_bp_power_status(); - - if (gpio_value == 0) { - pr_info("%s: Modem powered off.", mdmctrl); - pd_failure = 0; - break; - } - } - return pd_failure; -} - -static void __devexit mdm_ctrl_shutdown(struct platform_device *pdev) -{ - unsigned int pd_failure; - unsigned int bp_status; - - pr_info("%s: Shutting down modem.", mdmctrl); - - bp_status = get_bp_status(); - pr_info("%s: Initial Modem status %s [0x%x]", - mdmctrl, bp_status_string(bp_status), bp_status); - - set_ap_status(AP_STATUS_BP_SHUTDOWN_REQ); - - /* Allow modem to process status */ - msleep(100); - pr_info("%s: ap_status set to %d", mdmctrl, get_ap_status()); - - /* Assert PWRON to tell modem to shutdown and leave pin asserted */ - /* until acknowledged or wait times out */ - set_bp_pwron(1); - msleep(100); - - /* This should be enough to power down the modem */ - /* if this doesn't work, reset the modem and try */ - /* one more time, ultimately the modem will be */ - /* hard powered off */ - pd_failure = bp_shutdown_wait(5); - set_bp_pwron(0); - if (pd_failure) { - pr_info("%s: Resetting unresponsive modem.", mdmctrl); - set_bp_resin(1); - pd_failure = bp_shutdown_wait(5); - } - - if (pd_failure) - pr_err("%s: Modem failed to power down.", mdmctrl); -} - -static struct platform_driver mdm6x00_ctrl_driver = { - .probe = mdm_ctrl_probe, - .remove = __devexit_p(mdm_ctrl_remove), - .shutdown = __devexit_p(mdm_ctrl_shutdown), - .driver = { - .name = MDM_CTRL_MODULE_NAME, - .owner = THIS_MODULE, - }, -}; - -static int __init mdm6600_ctrl_init(void) -{ - printk(KERN_DEBUG "mdm6600_ctrl_init\n"); - return platform_driver_register(&mdm6x00_ctrl_driver); -} - -static void __exit mdm6600_ctrl_exit(void) -{ - printk(KERN_DEBUG "mdm6600_ctrl_exit\n"); - platform_driver_unregister(&mdm6x00_ctrl_driver); -} - -module_init(mdm6600_ctrl_init); -module_exit(mdm6600_ctrl_exit); - -MODULE_AUTHOR("Motorola"); -MODULE_DESCRIPTION("Modem Control Driver"); -MODULE_VERSION("1.1.3"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/radio_ctrl/Kconfig b/drivers/misc/radio_ctrl/Kconfig new file mode 100644 index 000000000000..4a24f50d0864 --- /dev/null +++ b/drivers/misc/radio_ctrl/Kconfig @@ -0,0 +1,15 @@ +config RADIO_CTRL_CLASS + bool "Radio Modem Controller Class" + default n + +config MDM6600_CTRL + bool "Motorola MDM6X00 Modem Controller" + default n + select RADIO_CTRL_CLASS + ---help--- + Enables the device driver to control and interface with + the modem co-processor. This module is needed to monitor + modem panics, interact with the modem during factory resets, + and allow modem power up/down support. + + If unsure, say N. diff --git a/drivers/misc/radio_ctrl/Makefile b/drivers/misc/radio_ctrl/Makefile new file mode 100644 index 000000000000..68936c356a87 --- /dev/null +++ b/drivers/misc/radio_ctrl/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for modem (radio) control drivers +# + +obj-$(CONFIG_RADIO_CTRL_CLASS) += radio_class.o +obj-$(CONFIG_MDM6600_CTRL) += mdm6600_ctrl.o diff --git a/drivers/misc/radio_ctrl/mdm6600_ctrl.c b/drivers/misc/radio_ctrl/mdm6600_ctrl.c new file mode 100644 index 000000000000..a6fa4fb902f8 --- /dev/null +++ b/drivers/misc/radio_ctrl/mdm6600_ctrl.c @@ -0,0 +1,581 @@ +/* + Copyright (C) 2010 Motorola, Inc. + + 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. + + 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 + +#define AP_STATUS_BP_PANIC_ACK 0x00 +#define AP_STATUS_DATA_ONLY_BYPASS 0x01 +#define AP_STATUS_FULL_BYPASS 0x02 +#define AP_STATUS_NO_BYPASS 0x03 +#define AP_STATUS_BP_SHUTDOWN_REQ 0x04 +#define AP_STATUS_UNDEFINED 0x07 + +#define BP_STATUS_PANIC 0x00 +#define BP_STATUS_PANIC_BUSY_WAIT 0x01 +#define BP_STATUS_QC_DLOAD 0x02 +#define BP_STATUS_RAM_DOWNLOADER 0x03 +#define BP_STATUS_PHONE_CODE_AWAKE 0x04 +#define BP_STATUS_PHONE_CODE_ASLEEP 0x05 +#define BP_STATUS_SHUTDOWN_ACK 0x06 +#define BP_STATUS_UNDEFINED 0x07 + + +#define LOOP_DELAY_TIME_MS 500 + +static const char *mdmctrl = "mdm6600_ctrl"; + +static const char *bp_status[8] = { + [BP_STATUS_PANIC] = "panic", + [BP_STATUS_PANIC_BUSY_WAIT] = "panic busy wait", + [BP_STATUS_QC_DLOAD] = "qc dload", + [BP_STATUS_RAM_DOWNLOADER] = "ram downloader", + [BP_STATUS_PHONE_CODE_AWAKE] = "awake", + [BP_STATUS_PHONE_CODE_ASLEEP] = "asleep", + [BP_STATUS_SHUTDOWN_ACK] = "shutdown ack", + [BP_STATUS_UNDEFINED] = "undefined", +}; + +static const char *bp_power_state[2] = { + "off", + "on", +}; + +#define BP_STATUS_MAX_LENGTH 32 +#define BP_COMMAND_MAX_LENGTH 32 + +/* structure to keep track of gpio, irq, and irq enabled info */ +struct gpio_info { + int irq; + struct work_struct work; +}; + +struct mdm_ctrl_info { + struct mdm_ctrl_platform_data *pdata; + struct gpio_info gpios[MDM_CTRL_NUM_GPIOS]; +}; + +static struct mdm_ctrl_info mdm_ctrl; + +static DEFINE_MUTEX(mdm_ctrl_info_lock); + +struct workqueue_struct *working_queue = NULL; + +struct class *radio_cls = NULL; +static struct radio_dev radio_cdev; + +static unsigned int bp_status_idx = BP_STATUS_UNDEFINED; +static unsigned int bp_power_idx = 0; + +static void __devexit mdm_ctrl_shutdown(struct platform_device *pdev); +static void mdm_ctrl_powerup(void); +static void mdm_ctrl_set_bootmode(int mode); + +static const char *bp_status_string(unsigned int stat) +{ + if (stat < ARRAY_SIZE(bp_status)) + return bp_status[stat]; + else + return "status out of range"; +} + +static const char *bp_power_state_string(unsigned int stat) +{ + if (stat < ARRAY_SIZE(bp_power_state)) + return bp_power_state[stat]; + else + return "status out of range"; +} + +static ssize_t mdm_status_show(struct radio_dev *dev, char *buff) +{ + ssize_t status = 0; + status = snprintf(buff, BP_STATUS_MAX_LENGTH, "%s\n", + bp_status_string(bp_status_idx)); + + return status; +} + +static ssize_t mdm_power_show(struct radio_dev *rdev, char *buff) +{ + ssize_t status = 0; + status = snprintf(buff, BP_STATUS_MAX_LENGTH, "%s\n", + bp_power_state_string(bp_power_idx)); + + return status; +} + +static ssize_t mdm_user_command(struct radio_dev *rdev, char *post_strip) +{ + + pr_info("%s: user command = %s\n", mdmctrl, post_strip); + + if (strcmp(post_strip,"shutdown") == 0) { + mdm_ctrl_shutdown(NULL); + } else if (strcmp(post_strip,"powerup") == 0) { + mdm_ctrl_powerup(); + } else if (strcmp(post_strip,"bootmode_normal") == 0) { + mdm_ctrl_set_bootmode(0); + } else if (strcmp(post_strip,"bootmode_flash") == 0) { + mdm_ctrl_set_bootmode(1); + } else { + return -EINVAL; + } + + return 0; +} + +static unsigned int mdm_gpio_get_value(struct mdm_ctrl_gpio gpio) +{ + return gpio_get_value(gpio.number); +} + +static void mdm_gpio_set_value(struct mdm_ctrl_gpio gpio, + unsigned int value) +{ + gpio_set_value(gpio.number, value); +} + +static void mdm_gpio_free(struct mdm_ctrl_gpio *gpio) +{ + if (gpio->allocated) + gpio_free(gpio->number); + gpio->allocated = 0; +} + +static int mdm_gpio_setup(struct mdm_ctrl_gpio *gpio) +{ + if (gpio_request(gpio->number, gpio->name)) { + printk(KERN_ERR "failed to aquire gpio %s", gpio->name); + return -1; + } + gpio->allocated = 1; + gpio_export(gpio->number, false); + if (gpio->direction == MDM_GPIO_DIRECTION_IN) + gpio_direction_input(gpio->number); + else if (gpio->direction == MDM_GPIO_DIRECTION_OUT) + gpio_direction_output(gpio->number, gpio->default_value); + return 0; +} + +static unsigned int get_bp_status(void) +{ + unsigned int status = BP_STATUS_UNDEFINED; + unsigned int bp_status[3] = {0}; + + mutex_lock(&mdm_ctrl_info_lock); + if (mdm_ctrl.pdata) { + bp_status[0] = mdm_gpio_get_value( + mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_BP_STATUS_0]); + bp_status[1] = mdm_gpio_get_value( + mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_BP_STATUS_1]); + bp_status[2] = mdm_gpio_get_value( + mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_BP_STATUS_2]); + } + mutex_unlock(&mdm_ctrl_info_lock); + + status = ((bp_status[2] & 0x1) << 2) | + ((bp_status[1] & 0x1) << 1) | + (bp_status[0] & 0x1); + + return status; +} + +static unsigned int get_bp_power_status(void) +{ + unsigned int status = 0; + + mutex_lock(&mdm_ctrl_info_lock); + if (mdm_ctrl.pdata) { + status = mdm_gpio_get_value( + mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_BP_RESOUT]); + } + + mutex_unlock(&mdm_ctrl_info_lock); + + return status & 0x1; +} + +static unsigned int get_ap_status(void) +{ + unsigned int status = AP_STATUS_UNDEFINED; + unsigned int ap_status[3] = {0}; + + mutex_lock(&mdm_ctrl_info_lock); + if (mdm_ctrl.pdata) { + ap_status[0] = mdm_gpio_get_value( + mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_AP_STATUS_0]); + ap_status[1] = mdm_gpio_get_value( + mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_AP_STATUS_1]); + ap_status[2] = mdm_gpio_get_value( + mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_AP_STATUS_2]); + } + mutex_unlock(&mdm_ctrl_info_lock); + + status = ((ap_status[2] & 0x1) << 2) | + ((ap_status[1] & 0x1) << 1) | + (ap_status[0] & 0x1); + + return status; +} + +static void set_ap_status(unsigned int status) +{ + mutex_lock(&mdm_ctrl_info_lock); + if (mdm_ctrl.pdata) { + mdm_gpio_set_value( + mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_AP_STATUS_0], + (status & 0x1)); + mdm_gpio_set_value( + mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_AP_STATUS_1], + (status >> 1) & 0x1); + mdm_gpio_set_value( + mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_AP_STATUS_2], + (status >> 2) & 0x1); + } + mutex_unlock(&mdm_ctrl_info_lock); +} + +static void set_bp_pwron(int on) +{ + mutex_lock(&mdm_ctrl_info_lock); + if ((mdm_ctrl.pdata) && ((on == 1) || (on == 0))) { + mdm_gpio_set_value( + mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_BP_PWRON], + on); + } + mutex_unlock(&mdm_ctrl_info_lock); +} + +static void set_bp_resin(int on) +{ + mutex_lock(&mdm_ctrl_info_lock); + if ((mdm_ctrl.pdata) && ((on == 1) || (on == 0))) { + mdm_gpio_set_value( + mdm_ctrl.pdata->gpios[MDM_CTRL_GPIO_BP_RESIN], + on); + } + mutex_unlock(&mdm_ctrl_info_lock); +} + +static void update_bp_status(void) { + + static int bp_status_prev_idx = BP_STATUS_UNDEFINED; + + bp_status_prev_idx = bp_status_idx; + bp_status_idx = get_bp_status(); + bp_power_idx = get_bp_power_status(); + + pr_info("%s: modem status: %s -> %s [power %s]", mdmctrl, + bp_status_string(bp_status_prev_idx), + bp_status_string(bp_status_idx), + bp_power_state_string(bp_power_idx)); + + kobject_uevent(&radio_cdev.dev->kobj, KOBJ_CHANGE); +} + +static void mdm_ctrl_powerup(void) +{ + unsigned int bp_status; + + pr_info("%s: Starting up modem.", mdmctrl); + + bp_status = get_bp_status(); + pr_info("%s: Initial Modem status %s [0x%x]", + mdmctrl, bp_status_string(bp_status), bp_status); + + set_ap_status(AP_STATUS_NO_BYPASS); + pr_info("%s: ap_status set to %d", mdmctrl, get_ap_status()); + msleep(100); + set_bp_resin(0); + msleep(100); + /* Toggle the power, delaying to allow modem to respond */ + set_bp_pwron(1); + msleep(100); + set_bp_pwron(0); + + /* now let user handles bp status change through uevent */ +} + +static void mdm_ctrl_set_bootmode(int mode) +{ + mutex_lock(&mdm_ctrl_info_lock); + if (mdm_ctrl.pdata && ((mode == 0) || (mode == 1))) { + gpio_request(mdm_ctrl.pdata->cmd_gpios.cmd1, + "BP Command 1"); + gpio_direction_output(mdm_ctrl.pdata->cmd_gpios.cmd1, + mode); + gpio_request(mdm_ctrl.pdata->cmd_gpios.cmd2, + "BP Command 2"); + gpio_direction_output(mdm_ctrl.pdata->cmd_gpios.cmd2, + mode); + + } + mutex_unlock(&mdm_ctrl_info_lock); +} + +static void irq_worker(struct work_struct *work) +{ + struct gpio_info *gpio = container_of(work, struct gpio_info, work); + update_bp_status(); + enable_irq(gpio->irq); +} + +static irqreturn_t irq_handler(int irq, void *data) +{ + struct gpio_info *gpio = (struct gpio_info *) data; + + disable_irq_nosync(irq); + queue_work(working_queue, &gpio->work); + + return IRQ_HANDLED; +} + +static int mdm_gpio_setup_internal(struct mdm_ctrl_platform_data *pdata) +{ + int i; + int rv = 0; + struct gpio_info *gpio_data = NULL; + + mutex_lock(&mdm_ctrl_info_lock); + memset(&mdm_ctrl, 0, sizeof (mdm_ctrl)); + + mdm_ctrl.pdata = pdata; + + for (i = 0; i < MDM_CTRL_NUM_GPIOS; i++) { + gpio_data = &mdm_ctrl.gpios[i]; + if (pdata->gpios[i].direction == MDM_GPIO_DIRECTION_IN) { + INIT_WORK(&gpio_data->work, irq_worker); + gpio_data->irq = gpio_to_irq(pdata->gpios[i].number); + rv = request_irq(gpio_data->irq, irq_handler, + IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING, + pdata->gpios[i].name, gpio_data); + if (rv < 0) { + pr_err("%s: Cannot request IRQ (%d) from kernel!", + mdmctrl, gpio_data->irq); + } else { + enable_irq_wake(gpio_data->irq); + } + } + } + + mutex_unlock(&mdm_ctrl_info_lock); + return rv; +} + +static void mdm_gpio_cleanup_internal(void) +{ + int i; + struct gpio_info *gpio_data = NULL; + + mutex_lock(&mdm_ctrl_info_lock); + + for (i = 0; i < MDM_CTRL_NUM_GPIOS; i++) { + gpio_data = &mdm_ctrl.gpios[i]; + + if (gpio_data->irq) { + disable_irq_wake(gpio_data->irq); + free_irq(gpio_data->irq, gpio_data); + } + } + memset(&mdm_ctrl, 0, sizeof (mdm_ctrl)); + mutex_unlock(&mdm_ctrl_info_lock); +} + +static struct radio_dev radio_cdev = { + .name = "mdm6600", + .power_status = mdm_power_show, + .status = mdm_status_show, + .command = mdm_user_command, +}; + +static int __devinit mdm_ctrl_probe(struct platform_device *pdev) +{ + int i; + struct mdm_ctrl_platform_data *pdata = pdev->dev.platform_data; + + dev_info(&pdev->dev, "mdm_ctrl_probe"); + + pr_debug("%s: radio_cdev = %p\n", __func__, &radio_cdev); + + for (i = 0; i < MDM_CTRL_NUM_GPIOS; i++) { + if (mdm_gpio_setup(&pdata->gpios[i])) { + dev_err(&pdev->dev, "failed to aquire gpio %d\n", + pdata->gpios[i].number); + goto probe_cleanup; + } + } + + working_queue = create_singlethread_workqueue("mdm_ctrl_wq"); + if (!working_queue) { + dev_err(&pdev->dev, "Cannot create work queue."); + goto probe_err; + } + + if (mdm_gpio_setup_internal(pdata) < 0) { + dev_err(&pdev->dev, "Failed to setup bp status irq"); + goto err_setup; + } + + if (radio_dev_register(&radio_cdev)) { + pr_err("%s: failed to register mdm_ctr device\n", __func__); + goto err_setup; + } + + update_bp_status(); + + return 0; + +err_setup: + mdm_gpio_cleanup_internal(); + +probe_err: + destroy_workqueue(working_queue); + +probe_cleanup: + for (i = 0; i < MDM_CTRL_NUM_GPIOS; i++) + mdm_gpio_free(&pdata->gpios[i]); + + return -1; +} + +static int __devexit mdm_ctrl_remove(struct platform_device *pdev) +{ + int i; + struct mdm_ctrl_platform_data *pdata = pdev->dev.platform_data; + + dev_info(&pdev->dev, "cleanup\n"); + + radio_dev_unregister(&radio_cdev); + + mdm_gpio_cleanup_internal(); + + if (working_queue) + destroy_workqueue(working_queue); + + for (i = 0; i < MDM_CTRL_NUM_GPIOS; i++) + mdm_gpio_free(&pdata->gpios[i]); + + return 0; +} + +static unsigned int __devexit bp_shutdown_wait(unsigned int delay_sec) +{ + unsigned int i, loop_count; + unsigned int bp_status; + unsigned int gpio_value; + unsigned int pd_failure = 1; + + loop_count = (delay_sec * 1000) / LOOP_DELAY_TIME_MS; + + for (i = 0; i < loop_count; i++) { + msleep(LOOP_DELAY_TIME_MS); + bp_status = get_bp_status(); + if (bp_status == BP_STATUS_SHUTDOWN_ACK) { + pr_info("%s: Modem powered off (with ack).", mdmctrl); + pd_failure = 0; + break; + } + + gpio_value = get_bp_power_status(); + + if (gpio_value == 0) { + pr_info("%s: Modem powered off.", mdmctrl); + pd_failure = 0; + break; + } + } + return pd_failure; +} + +static void __devexit mdm_ctrl_shutdown(struct platform_device *pdev) +{ + unsigned int pd_failure; + unsigned int bp_status; + + pr_info("%s: Shutting down modem.", mdmctrl); + + bp_status = get_bp_status(); + pr_info("%s: Initial Modem status %s [0x%x]", + mdmctrl, bp_status_string(bp_status), bp_status); + + set_ap_status(AP_STATUS_BP_SHUTDOWN_REQ); + + /* Allow modem to process status */ + msleep(100); + pr_info("%s: ap_status set to %d", mdmctrl, get_ap_status()); + + /* Assert PWRON to tell modem to shutdown and leave pin asserted */ + /* until acknowledged or wait times out */ + set_bp_pwron(1); + msleep(100); + + /* This should be enough to power down the modem */ + /* if this doesn't work, reset the modem and try */ + /* one more time, ultimately the modem will be */ + /* hard powered off */ + pd_failure = bp_shutdown_wait(5); + set_bp_pwron(0); + if (pd_failure) { + pr_info("%s: Resetting unresponsive modem.", mdmctrl); + set_bp_resin(1); + pd_failure = bp_shutdown_wait(5); + } + + if (pd_failure) + pr_err("%s: Modem failed to power down.", mdmctrl); +} + +static struct platform_driver mdm6x00_ctrl_driver = { + .probe = mdm_ctrl_probe, + .remove = __devexit_p(mdm_ctrl_remove), + .shutdown = __devexit_p(mdm_ctrl_shutdown), + .driver = { + .name = MDM_CTRL_MODULE_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init mdm6600_ctrl_init(void) +{ + printk(KERN_DEBUG "mdm6600_ctrl_init\n"); + return platform_driver_register(&mdm6x00_ctrl_driver); +} + +static void __exit mdm6600_ctrl_exit(void) +{ + printk(KERN_DEBUG "mdm6600_ctrl_exit\n"); + platform_driver_unregister(&mdm6x00_ctrl_driver); +} + +module_init(mdm6600_ctrl_init); +module_exit(mdm6600_ctrl_exit); + +MODULE_AUTHOR("Motorola"); +MODULE_DESCRIPTION("MDM6X00 Control Driver"); +MODULE_VERSION("1.1.4"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/radio_ctrl/radio_class.c b/drivers/misc/radio_ctrl/radio_class.c new file mode 100644 index 000000000000..33a9adac8236 --- /dev/null +++ b/drivers/misc/radio_ctrl/radio_class.c @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2011 Motorola, Inc. + * + * 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. + * + * 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 + +static struct class *radio_cls; + +/* show the device status */ +static ssize_t radio_status_show(struct device *dev, + struct device_attribute *attr, char *buff) +{ + struct radio_dev *rdev = dev_get_drvdata(dev); + + if (!rdev || !rdev->status) { + pr_err("%s: supporting function not found\n", __func__); + return -ENODEV; + } + + return rdev->status(rdev, buff); +} + +/* show the device power state */ +static ssize_t radio_power_show(struct device *dev, + struct device_attribute *attr, char *buff) +{ + struct radio_dev *rdev = dev_get_drvdata(dev); + + if (!rdev || !rdev->power_status) { + pr_err("%s: supporting function not found\n", __func__); + return -ENODEV; + } + return rdev->power_status(rdev, buff); +} + +/* control the radio device */ +static ssize_t radio_command(struct device *dev, + struct device_attribute *attr, const char *buff, size_t size) +{ + struct radio_dev *rdev = dev_get_drvdata(dev); + char tmp[RADIO_COMMAND_MAX_LENGTH]; + char *post_strip = NULL; + ssize_t ret; + + if (!rdev || !rdev->command) { + pr_err("%s: supporting function not found\n", __func__); + return -ENODEV; + } + pr_debug("%s: rdev.name = %s\n", __func__, rdev->name); + + if (size > RADIO_COMMAND_MAX_LENGTH - 1) { + pr_err("%s: command too long\n", __func__); + return -EINVAL; + } + + /* strip whitespaces if any */ + memcpy(tmp, buff, size); + tmp[size] = '\0'; + post_strip = strim(tmp); + + pr_debug("%s: command = %s size = %d\n", __func__, post_strip, size); + + ret = rdev->command(rdev, post_strip); + return ret >= 0 ? size : ret; +} + +static DEVICE_ATTR(status, S_IRUGO, radio_status_show, NULL); +static DEVICE_ATTR(power_status, S_IRUGO, radio_power_show, NULL); +static DEVICE_ATTR(command, S_IWUSR, NULL, radio_command); + +int radio_dev_register(struct radio_dev *rdev) +{ + int err = -1; + + rdev->dev = device_create(radio_cls, NULL, 0, rdev, + "%s", rdev->name); + if (IS_ERR(rdev->dev)) + return PTR_ERR(rdev->dev); + + pr_info("%s: register %s\n", __func__, rdev->name); + pr_debug("%s: dev = %p\n", __func__, rdev->dev); + + /* /sys/class/radio//status */ + if (rdev->status) { + err = device_create_file(rdev->dev, &dev_attr_status); + if (err < 0) { + pr_err("%s: status file create failed for %s\n", + __func__, rdev->name); + goto err_status; + } + } + + /* /sys/class/radio//power_status */ + if (rdev->power_status) { + err = device_create_file(rdev->dev, &dev_attr_power_status); + if (err < 0) { + pr_err("%s: power_status file create failed for %s\n", + __func__, rdev->name); + goto err_pwr_status; + } + } + + /* /sys/class/radio//command */ + if (rdev->command) { + err = device_create_file(rdev->dev, &dev_attr_command); + if (err < 0) { + pr_err("%s: command file create failed for %s\n", + __func__, rdev->name); + goto err_command; + } + } + + return 0; + +err_command: + device_remove_file(rdev->dev, &dev_attr_power_status); +err_pwr_status: + device_remove_file(rdev->dev, &dev_attr_status); +err_status: + put_device(rdev->dev); + device_unregister(rdev->dev); + dev_set_drvdata(rdev->dev, NULL); + + return err; +} +EXPORT_SYMBOL(radio_dev_register); + +void radio_dev_unregister(struct radio_dev *rdev) +{ + if (rdev) { + if (rdev->command) + device_remove_file(rdev->dev, &dev_attr_command); + if (rdev->power_status) + device_remove_file(rdev->dev, &dev_attr_power_status); + if (rdev->status) + device_remove_file(rdev->dev, &dev_attr_status); + + put_device(rdev->dev); + device_unregister(rdev->dev); + dev_set_drvdata(rdev->dev, NULL); + } +} +EXPORT_SYMBOL(radio_dev_unregister); + +static int __init radio_class_init(void) +{ + pr_info("%s: initialized radio_class\n", __func__); + + /* /sys/class/radio */ + radio_cls = class_create(THIS_MODULE, "radio"); + if (IS_ERR(radio_cls)) { + pr_err("%s: failed to initialize radio class\n", __func__); + return PTR_ERR(radio_cls); + } + + return 0; +} + +static void __exit radio_class_exit(void) +{ + pr_info("%s: destroy radio_class\n", __func__); + class_destroy(radio_cls); +} + +module_init(radio_class_init); +module_exit(radio_class_exit); + +MODULE_AUTHOR("Jim Wylder "); +MODULE_DESCRIPTION("Radio Control Class"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mdm6600_ctrl.h b/include/linux/mdm6600_ctrl.h deleted file mode 100644 index 5fe4a10fe6c2..000000000000 --- a/include/linux/mdm6600_ctrl.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright (C) 2010 Motorola, Inc. - - 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. - - 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 -*/ -#ifndef __LINUX_MDM_CTRL_H__ -#define __LINUX_MDM_CTRL_H__ - -#define MDM_CTRL_MODULE_NAME "mdm6600_ctrl" -#define MAX_GPIO_NAME 20 - -enum { - MDM_CTRL_GPIO_AP_STATUS_0, - MDM_CTRL_GPIO_AP_STATUS_1, - MDM_CTRL_GPIO_AP_STATUS_2, - MDM_CTRL_GPIO_BP_STATUS_0, - MDM_CTRL_GPIO_BP_STATUS_1, - MDM_CTRL_GPIO_BP_STATUS_2, - MDM_CTRL_GPIO_BP_RESOUT, - MDM_CTRL_GPIO_BP_RESIN, - MDM_CTRL_GPIO_BP_PWRON, - - MDM_CTRL_NUM_GPIOS, -}; - -enum { - MDM_GPIO_DIRECTION_IN, - MDM_GPIO_DIRECTION_OUT, -}; - -struct mdm_ctrl_gpio { - unsigned int number; - unsigned int direction; - unsigned int default_value; - unsigned int allocated; - char *name; -}; - -struct mdm_command_gpios { - unsigned int cmd1; - unsigned int cmd2; -}; - -struct mdm_ctrl_platform_data { - struct mdm_ctrl_gpio gpios[MDM_CTRL_NUM_GPIOS]; - struct mdm_command_gpios cmd_gpios; -}; -#endif /* __LINUX_MDM_CTRL_H__ */ diff --git a/include/linux/radio_ctrl/mdm6600_ctrl.h b/include/linux/radio_ctrl/mdm6600_ctrl.h new file mode 100644 index 000000000000..5fe4a10fe6c2 --- /dev/null +++ b/include/linux/radio_ctrl/mdm6600_ctrl.h @@ -0,0 +1,60 @@ +/* + Copyright (C) 2010 Motorola, Inc. + + 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. + + 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 +*/ +#ifndef __LINUX_MDM_CTRL_H__ +#define __LINUX_MDM_CTRL_H__ + +#define MDM_CTRL_MODULE_NAME "mdm6600_ctrl" +#define MAX_GPIO_NAME 20 + +enum { + MDM_CTRL_GPIO_AP_STATUS_0, + MDM_CTRL_GPIO_AP_STATUS_1, + MDM_CTRL_GPIO_AP_STATUS_2, + MDM_CTRL_GPIO_BP_STATUS_0, + MDM_CTRL_GPIO_BP_STATUS_1, + MDM_CTRL_GPIO_BP_STATUS_2, + MDM_CTRL_GPIO_BP_RESOUT, + MDM_CTRL_GPIO_BP_RESIN, + MDM_CTRL_GPIO_BP_PWRON, + + MDM_CTRL_NUM_GPIOS, +}; + +enum { + MDM_GPIO_DIRECTION_IN, + MDM_GPIO_DIRECTION_OUT, +}; + +struct mdm_ctrl_gpio { + unsigned int number; + unsigned int direction; + unsigned int default_value; + unsigned int allocated; + char *name; +}; + +struct mdm_command_gpios { + unsigned int cmd1; + unsigned int cmd2; +}; + +struct mdm_ctrl_platform_data { + struct mdm_ctrl_gpio gpios[MDM_CTRL_NUM_GPIOS]; + struct mdm_command_gpios cmd_gpios; +}; +#endif /* __LINUX_MDM_CTRL_H__ */ diff --git a/include/linux/radio_ctrl/radio_class.h b/include/linux/radio_ctrl/radio_class.h new file mode 100644 index 000000000000..d09b2dcb56c7 --- /dev/null +++ b/include/linux/radio_ctrl/radio_class.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2011 Motorola, Inc. + * + * 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. + * + * 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 + */ +#ifndef __RADIO_CLASS_H__ +#define __RADIO_CLASS_H__ + +struct radio_dev { + const char *name; + struct device *dev; + + ssize_t (*power_status)(struct radio_dev *rdev, char *buf); + ssize_t (*status)(struct radio_dev *rdev, char *buf); + ssize_t (*command)(struct radio_dev *rdev, char *buf); +}; + +extern void radio_dev_unregister(struct radio_dev *radio_cdev); +extern int radio_dev_register(struct radio_dev *radio_cdev); + +#define RADIO_STATUS_MAX_LENGTH 32 +#define RADIO_COMMAND_MAX_LENGTH 32 +#define RADIO_BOOTMODE_NORMAL 0 +#define RADIO_BOOTMODE_FLASH 1 +#endif /* __RADIO_CLASS_H__ */