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
source "drivers/misc/cb710/Kconfig"
source "drivers/misc/ts27010mux/Kconfig"
source "drivers/misc/iwmc3200top/Kconfig"
+source "drivers/misc/radio_ctrl/Kconfig"
endif # MISC_DEVICES
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/
+++ /dev/null
-/*
- 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 <linux/delay.h>
-#include <linux/device.h>
-#include <linux/gpio.h>
-#include <linux/mdm6600_ctrl.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/cdev.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/kobject.h>
-
-#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");
--- /dev/null
+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.
--- /dev/null
+#
+# Makefile for modem (radio) control drivers
+#
+
+obj-$(CONFIG_RADIO_CTRL_CLASS) += radio_class.o
+obj-$(CONFIG_MDM6600_CTRL) += mdm6600_ctrl.o
--- /dev/null
+/*
+ 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 <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kobject.h>
+#include <linux/radio_ctrl/mdm6600_ctrl.h>
+#include <linux/radio_ctrl/radio_class.h>
+
+#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");
--- /dev/null
+/*
+ * 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 <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/radio_ctrl/radio_class.h>
+
+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/<dev>/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/<dev>/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/<dev>/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 <james.wylder@motorola.com>");
+MODULE_DESCRIPTION("Radio Control Class");
+MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- 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__ */
--- /dev/null
+/*
+ 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__ */
--- /dev/null
+/*
+ * 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__ */