msm: mdm: add support for multiple external modems
The mdm driver previously assumed that it would only need to support
one external modem at a time. This is no longer true. The changes now
support a arbitrary number of modems. Each modem must have its own
platform data.
Change-Id: I43918f92935b4b447a46d02721239fed0b5e877e
Signed-off-by: Ameya Thakur <ameyat@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index de6d9ca..3839014 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -86,6 +86,7 @@
#include "pm-boot.h"
#include "devices-msm8x60.h"
#include "smd_private.h"
+#include "sysmon.h"
#define MSM_PMEM_ADSP_SIZE 0x7800000
#define MSM_PMEM_AUDIO_SIZE 0x4CF000
@@ -1832,6 +1833,8 @@
.peripheral_platform_device = &apq8064_device_hsic_host,
.ramdump_timeout_ms = 120000,
.mdm2ap_status_gpio_run_cfg = &mdm2ap_status_gpio_run_cfg,
+ .sysmon_subsys_id_valid = 1,
+ .sysmon_subsys_id = SYSMON_SS_EXT_MODEM,
};
static struct tsens_platform_data apq_tsens_pdata = {
diff --git a/arch/arm/mach-msm/include/mach/mdm2.h b/arch/arm/mach-msm/include/mach/mdm2.h
index 6ec12c1..c6c03e4 100644
--- a/arch/arm/mach-msm/include/mach/mdm2.h
+++ b/arch/arm/mach-msm/include/mach/mdm2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -12,6 +12,7 @@
#ifndef _ARCH_ARM_MACH_MSM_MDM2_H
#define _ARCH_ARM_MACH_MSM_MDM2_H
+#include "sysmon.h"
struct mdm_vddmin_resource {
int rpm_id;
@@ -24,6 +25,7 @@
struct mdm_platform_data {
char *mdm_version;
int ramdump_delay_ms;
+ int ps_hold_delay_ms;
int soft_reset_inverted;
int early_power_on;
int sfr_query;
@@ -33,6 +35,10 @@
const unsigned int ramdump_timeout_ms;
int image_upgrade_supported;
struct gpiomux_setting *mdm2ap_status_gpio_run_cfg;
+ int send_shdn;
+ int cascading_ssr;
+ int sysmon_subsys_id_valid;
+ enum subsys_id sysmon_subsys_id;
};
#endif
diff --git a/arch/arm/mach-msm/mdm2.c b/arch/arm/mach-msm/mdm2.c
index 77eeb53..a9fad9d 100644
--- a/arch/arm/mach-msm/mdm2.c
+++ b/arch/arm/mach-msm/mdm2.c
@@ -28,6 +28,7 @@
#include <linux/debugfs.h>
#include <linux/completion.h>
#include <linux/workqueue.h>
+#include <linux/clk.h>
#include <linux/mfd/pmic8058.h>
#include <asm/mach-types.h>
#include <asm/uaccess.h>
@@ -38,26 +39,24 @@
#include <linux/msm_charm.h>
#include "msm_watchdog.h"
#include "devices.h"
+#include "clock.h"
#include "mdm_private.h"
#define MDM_PBLRDY_CNT 20
static int mdm_debug_mask;
-static int power_on_count;
-static int hsic_peripheral_status;
-static DEFINE_MUTEX(hsic_status_lock);
static void mdm_peripheral_connect(struct mdm_modem_drv *mdm_drv)
{
if (!mdm_drv->pdata->peripheral_platform_device)
return;
- mutex_lock(&hsic_status_lock);
- if (hsic_peripheral_status)
+ mutex_lock(&mdm_drv->peripheral_status_lock);
+ if (mdm_drv->peripheral_status)
goto out;
platform_device_add(mdm_drv->pdata->peripheral_platform_device);
- hsic_peripheral_status = 1;
+ mdm_drv->peripheral_status = 1;
out:
- mutex_unlock(&hsic_status_lock);
+ mutex_unlock(&mdm_drv->peripheral_status_lock);
}
static void mdm_peripheral_disconnect(struct mdm_modem_drv *mdm_drv)
@@ -65,13 +64,13 @@
if (!mdm_drv->pdata->peripheral_platform_device)
return;
- mutex_lock(&hsic_status_lock);
- if (!hsic_peripheral_status)
+ mutex_lock(&mdm_drv->peripheral_status_lock);
+ if (!mdm_drv->peripheral_status)
goto out;
platform_device_del(mdm_drv->pdata->peripheral_platform_device);
- hsic_peripheral_status = 0;
+ mdm_drv->peripheral_status = 0;
out:
- mutex_unlock(&hsic_status_lock);
+ mutex_unlock(&mdm_drv->peripheral_status_lock);
}
/* This function can be called from atomic context. */
@@ -112,15 +111,15 @@
for (i = 20; i > 0; i--) {
if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
if (mdm_debug_mask & MDM_DEBUG_MASK_SHDN_LOG)
- pr_info("%s: mdm2ap_status went low, i = %d\n",
- __func__, i);
+ pr_info("%s:id %d: mdm2ap_statuswent low, i=%d\n",
+ __func__, mdm_drv->device_id, i);
break;
}
msleep(100);
}
if (i == 0) {
- pr_err("%s: MDM2AP_STATUS never went low. Doing a hard reset\n",
- __func__);
+ pr_err("%s:id %d: MDM2AP_STATUS never went low. Doing a hard reset\n",
+ __func__, mdm_drv->device_id);
gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio,
soft_reset_direction);
/*
@@ -137,13 +136,14 @@
{
int i;
int pblrdy;
- if (power_on_count != 1) {
- pr_err("%s: Calling fn when power_on_count != 1\n",
- __func__);
+ if (mdm_drv->power_on_count != 1) {
+ pr_err("%s:id %d: Calling fn when power_on_count != 1\n",
+ __func__, mdm_drv->device_id);
return;
}
- pr_err("%s: Powering on modem for the first time\n", __func__);
+ pr_err("%s:id %d: Powering on modem for the first time\n",
+ __func__, mdm_drv->device_id);
mdm_peripheral_disconnect(mdm_drv);
/* If this is the first power-up after a panic, the modem may still
@@ -157,7 +157,8 @@
/* Pull AP2MDM_KPDPWR gpio high and wait for PS_HOLD to settle,
* then pull it back low.
*/
- pr_debug("%s: Pulling AP2MDM_KPDPWR gpio high\n", __func__);
+ pr_debug("%s:id %d: Pulling AP2MDM_KPDPWR gpio high\n",
+ __func__, mdm_drv->device_id);
gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 1);
msleep(1000);
gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 0);
@@ -172,7 +173,8 @@
break;
usleep_range(5000, 5000);
}
- pr_debug("%s: i:%d\n", __func__, i);
+ pr_debug("%s: id %d: pblrdy i:%d\n", __func__,
+ mdm_drv->device_id, i);
start_mdm_peripheral:
mdm_peripheral_connect(mdm_drv);
@@ -184,7 +186,8 @@
int i;
int pblrdy;
- pr_err("%s: soft resetting mdm modem\n", __func__);
+ pr_err("%s: id %d: soft resetting mdm modem\n",
+ __func__, mdm_drv->device_id);
mdm_peripheral_disconnect(mdm_drv);
mdm_toggle_soft_reset(mdm_drv);
@@ -198,7 +201,8 @@
usleep_range(5000, 5000);
}
- pr_debug("%s: i:%d\n", __func__, i);
+ pr_debug("%s: id %d: pblrdy i:%d\n", __func__,
+ mdm_drv->device_id, i);
start_mdm_peripheral:
mdm_peripheral_connect(mdm_drv);
@@ -207,7 +211,7 @@
static void mdm_power_on_common(struct mdm_modem_drv *mdm_drv)
{
- power_on_count++;
+ mdm_drv->power_on_count++;
/* this gpio will be used to indicate apq readiness,
* de-assert it now so that it can be asserted later.
@@ -222,10 +226,10 @@
* user space but we're already powered on. Ignore it.
*/
if (mdm_drv->pdata->early_power_on &&
- (power_on_count == 2))
+ (mdm_drv->power_on_count == 2))
return;
- if (power_on_count == 1)
+ if (mdm_drv->power_on_count == 1)
mdm_do_first_power_on(mdm_drv);
else
mdm_do_soft_power_on(mdm_drv);
@@ -238,7 +242,8 @@
static void mdm_status_changed(struct mdm_modem_drv *mdm_drv, int value)
{
- pr_debug("%s: value:%d\n", __func__, value);
+ pr_debug("%s: id %d: value:%d\n", __func__,
+ value, mdm_drv->device_id);
if (value) {
mdm_peripheral_disconnect(mdm_drv);
@@ -252,13 +257,15 @@
{
switch (type) {
case APQ_CONTROLLED_UPGRADE:
- pr_debug("%s APQ controlled modem image upgrade\n", __func__);
- mdm_drv->mdm_ready = 0;
+ pr_debug("%s: id %d: APQ controlled modem image upgrade\n",
+ __func__, mdm_drv->device_id);
+ atomic_set(&mdm_drv->mdm_ready, 0);
mdm_toggle_soft_reset(mdm_drv);
break;
case MDM_CONTROLLED_UPGRADE:
- pr_debug("%s MDM controlled modem image upgrade\n", __func__);
- mdm_drv->mdm_ready = 0;
+ pr_debug("%s: id %d: MDM controlled modem image upgrade\n",
+ __func__, mdm_drv->device_id);
+ atomic_set(&mdm_drv->mdm_ready, 0);
/*
* If we have no image currently present on the modem, then we
* would be in PBL, in which case the status gpio would not go
@@ -266,15 +273,19 @@
*/
mdm_drv->disable_status_check = 1;
if (GPIO_IS_VALID(mdm_drv->usb_switch_gpio)) {
- pr_info("%s Switching usb control to MDM\n", __func__);
+ pr_info("%s: id %d: Switching usb control to MDM\n",
+ __func__, mdm_drv->device_id);
gpio_direction_output(mdm_drv->usb_switch_gpio, 1);
} else
- pr_err("%s usb switch gpio unavailable\n", __func__);
+ pr_err("%s: id %d: usb switch gpio unavailable\n",
+ __func__, mdm_drv->device_id);
break;
default:
- pr_err("%s invalid upgrade type\n", __func__);
+ pr_err("%s: id %d: invalid upgrade type\n",
+ __func__, mdm_drv->device_id);
}
}
+
static struct mdm_ops mdm_cb = {
.power_on_mdm_cb = mdm_power_on_common,
.reset_mdm_cb = mdm_power_on_common,
@@ -285,44 +296,10 @@
.image_upgrade_cb = mdm_image_upgrade,
};
-static int __init mdm_modem_probe(struct platform_device *pdev)
+int mdm_get_ops(struct mdm_ops **mdm_ops)
{
- return mdm_common_create(pdev, &mdm_cb);
+ *mdm_ops = &mdm_cb;
+ return 0;
}
-static int __devexit mdm_modem_remove(struct platform_device *pdev)
-{
- return mdm_common_modem_remove(pdev);
-}
-static void mdm_modem_shutdown(struct platform_device *pdev)
-{
- mdm_common_modem_shutdown(pdev);
-}
-
-static struct platform_driver mdm_modem_driver = {
- .remove = mdm_modem_remove,
- .shutdown = mdm_modem_shutdown,
- .driver = {
- .name = "mdm2_modem",
- .owner = THIS_MODULE
- },
-};
-
-static int __init mdm_modem_init(void)
-{
- return platform_driver_probe(&mdm_modem_driver, mdm_modem_probe);
-}
-
-static void __exit mdm_modem_exit(void)
-{
- platform_driver_unregister(&mdm_modem_driver);
-}
-
-module_init(mdm_modem_init);
-module_exit(mdm_modem_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("mdm modem driver");
-MODULE_VERSION("2.0");
-MODULE_ALIAS("mdm_modem");
diff --git a/arch/arm/mach-msm/mdm_common.c b/arch/arm/mach-msm/mdm_common.c
index 0597c62..536e859 100644
--- a/arch/arm/mach-msm/mdm_common.c
+++ b/arch/arm/mach-msm/mdm_common.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -49,22 +49,15 @@
#define MDM_RDUMP_TIMEOUT 120000L
#define MDM2AP_STATUS_TIMEOUT_MS 60000L
-static unsigned int mdm_debug_mask;
-static struct workqueue_struct *mdm_queue;
-static struct workqueue_struct *mdm_sfr_queue;
-static unsigned int dump_timeout_ms;
-static int vddmin_gpios_sent;
-
+/* Allow a maximum device id of this many digits */
+#define MAX_DEVICE_DIGITS 10
#define EXTERNAL_MODEM "external_modem"
+#define SUBSYS_NAME_LENGTH \
+ (sizeof(EXTERNAL_MODEM) + MAX_DEVICE_DIGITS)
-static struct mdm_modem_drv *mdm_drv;
-static struct subsys_device *mdm_subsys_dev;
-
-DECLARE_COMPLETION(mdm_needs_reload);
-DECLARE_COMPLETION(mdm_boot);
-DECLARE_COMPLETION(mdm_ram_dumps);
-
-static int first_boot = 1;
+#define DEVICE_BASE_NAME "mdm"
+#define DEVICE_NAME_LENGTH \
+ (sizeof(DEVICE_BASE_NAME) + MAX_DEVICE_DIGITS)
#define RD_BUF_SIZE 100
#define SFR_MAX_RETRIES 10
@@ -74,58 +67,246 @@
GPIO_UPDATE_BOOTING_CONFIG = 1,
GPIO_UPDATE_RUNNING_CONFIG,
};
-static int mdm2ap_status_valid_old_config;
-static struct gpiomux_setting mdm2ap_status_old_config;
+
+struct mdm_device {
+ struct list_head link;
+ struct mdm_modem_drv mdm_data;
+
+ int mdm2ap_status_valid_old_config;
+ struct gpiomux_setting mdm2ap_status_old_config;
+ int first_boot;
+ struct workqueue_struct *mdm_queue;
+ struct workqueue_struct *mdm_sfr_queue;
+ unsigned int dump_timeout_ms;
+
+ char subsys_name[SUBSYS_NAME_LENGTH];
+ struct subsys_desc mdm_subsys;
+ struct subsys_device *mdm_subsys_dev;
+
+ char device_name[DEVICE_NAME_LENGTH];
+ struct miscdevice misc_device;
+
+ struct completion mdm_needs_reload;
+ struct completion mdm_boot;
+ struct completion mdm_ram_dumps;
+ int mdm_errfatal_irq;
+ int mdm_status_irq;
+ int mdm_pblrdy_irq;
+
+ struct delayed_work mdm2ap_status_check_work;
+ struct work_struct mdm_status_work;
+ struct work_struct sfr_reason_work;
+
+ struct notifier_block mdm_panic_blk;
+
+ int ssr_started_internally;
+};
+
+static struct list_head mdm_devices;
+static DEFINE_SPINLOCK(mdm_devices_lock);
+
+static int ssr_count;
+static DEFINE_SPINLOCK(ssr_lock);
+
+static unsigned int mdm_debug_mask;
+int vddmin_gpios_sent;
+static struct mdm_ops *mdm_ops;
+
+static void mdm_device_list_add(struct mdm_device *mdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdm_devices_lock, flags);
+ list_add_tail(&mdev->link, &mdm_devices);
+ spin_unlock_irqrestore(&mdm_devices_lock, flags);
+}
+
+static void mdm_device_list_remove(struct mdm_device *mdev)
+{
+ unsigned long flags;
+ struct mdm_device *lmdev, *tmp;
+
+ spin_lock_irqsave(&mdm_devices_lock, flags);
+ list_for_each_entry_safe(lmdev, tmp, &mdm_devices, link) {
+ if (mdev && mdev == lmdev) {
+ pr_debug("%s: removing device id %d\n",
+ __func__, mdev->mdm_data.device_id);
+ list_del(&mdev->link);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&mdm_devices_lock, flags);
+}
+
+struct mdm_device *mdm_get_device_by_device_id(int device_id)
+{
+ unsigned long flags;
+ struct mdm_device *mdev = NULL;
+
+ spin_lock_irqsave(&mdm_devices_lock, flags);
+ list_for_each_entry(mdev, &mdm_devices, link) {
+ if (mdev && mdev->mdm_data.device_id == device_id) {
+ spin_unlock_irqrestore(&mdm_devices_lock, flags);
+ return mdev;
+ }
+ }
+ spin_unlock_irqrestore(&mdm_devices_lock, flags);
+ return NULL;
+}
+
+struct mdm_device *mdm_get_device_by_name(const char *name)
+{
+ unsigned long flags;
+ struct mdm_device *mdev;
+
+ if (!name)
+ return NULL;
+ spin_lock_irqsave(&mdm_devices_lock, flags);
+ list_for_each_entry(mdev, &mdm_devices, link) {
+ if (mdev && !strncmp(mdev->device_name, name,
+ sizeof(mdev->device_name))) {
+ spin_unlock_irqrestore(&mdm_devices_lock, flags);
+ return mdev;
+ }
+ }
+ spin_unlock_irqrestore(&mdm_devices_lock, flags);
+ return NULL;
+}
+
+/* If the platform's cascading_ssr flag is set, the subsystem
+ * restart module will restart the other modems so stop
+ * monitoring them as well.
+ * This function can be called from interrupt context.
+ */
+static void mdm_start_ssr(struct mdm_device *mdev)
+{
+ unsigned long flags;
+ int start_ssr = 1;
+
+ spin_lock_irqsave(&ssr_lock, flags);
+ if (mdev->mdm_data.pdata->cascading_ssr &&
+ ssr_count > 0) {
+ start_ssr = 0;
+ } else {
+ ssr_count++;
+ mdev->ssr_started_internally = 1;
+ }
+ spin_unlock_irqrestore(&ssr_lock, flags);
+
+ if (start_ssr) {
+ atomic_set(&mdev->mdm_data.mdm_ready, 0);
+ pr_info("%s: Resetting mdm id %d due to mdm error\n",
+ __func__, mdev->mdm_data.device_id);
+ subsystem_restart_dev(mdev->mdm_subsys_dev);
+ } else {
+ pr_info("%s: Another modem is already in SSR\n",
+ __func__);
+ }
+}
+
+/* Increment the reference count to handle the case where
+ * subsystem restart is initiated by the SSR service.
+ */
+static void mdm_ssr_started(struct mdm_device *mdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ssr_lock, flags);
+ ssr_count++;
+ atomic_set(&mdev->mdm_data.mdm_ready, 0);
+ spin_unlock_irqrestore(&ssr_lock, flags);
+}
+
+/* mdm_ssr_completed assumes that mdm_ssr_started has previously
+ * been called.
+ */
+static void mdm_ssr_completed(struct mdm_device *mdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ssr_lock, flags);
+ ssr_count--;
+ if (mdev->ssr_started_internally) {
+ mdev->ssr_started_internally = 0;
+ ssr_count--;
+ }
+
+ if (ssr_count < 0) {
+ pr_err("%s: ssr_count = %d\n",
+ __func__, ssr_count);
+ panic("%s: ssr_count = %d < 0\n",
+ __func__, ssr_count);
+ }
+ spin_unlock_irqrestore(&ssr_lock, flags);
+}
static irqreturn_t mdm_vddmin_change(int irq, void *dev_id)
{
- int value = gpio_get_value(
- mdm_drv->pdata->vddmin_resource->mdm2ap_vddmin_gpio);
+ struct mdm_device *mdev = (struct mdm_device *)dev_id;
+ struct mdm_vddmin_resource *vddmin_res;
+ int value;
+ if (!mdev)
+ goto handled;
+
+ vddmin_res = mdev->mdm_data.pdata->vddmin_resource;
+ if (!vddmin_res)
+ goto handled;
+
+ value = gpio_get_value(
+ vddmin_res->mdm2ap_vddmin_gpio);
if (value == 0)
- pr_debug("External Modem entered Vddmin\n");
+ pr_info("External Modem id %d entered Vddmin\n",
+ mdev->mdm_data.device_id);
else
- pr_debug("External Modem exited Vddmin\n");
-
+ pr_info("External Modem id %d exited Vddmin\n",
+ mdev->mdm_data.device_id);
+handled:
return IRQ_HANDLED;
}
+/* The vddmin_res resource may not be supported by some platforms. */
static void mdm_setup_vddmin_gpios(void)
{
+ unsigned long flags;
struct msm_rpm_iv_pair req;
+ struct mdm_device *mdev;
struct mdm_vddmin_resource *vddmin_res;
int irq, ret;
- /* This resource may not be supported by some platforms. */
- vddmin_res = mdm_drv->pdata->vddmin_resource;
- if (!vddmin_res)
- return;
+ spin_lock_irqsave(&mdm_devices_lock, flags);
+ list_for_each_entry(mdev, &mdm_devices, link) {
+ vddmin_res = mdev->mdm_data.pdata->vddmin_resource;
+ if (!vddmin_res)
+ continue;
- pr_info("Enabling vddmin logging\n");
- req.id = vddmin_res->rpm_id;
- req.value = ((uint32_t)vddmin_res->ap2mdm_vddmin_gpio & 0x0000FFFF)
- << 16;
- req.value |= ((uint32_t)vddmin_res->modes & 0x000000FF) << 8;
- req.value |= (uint32_t)vddmin_res->drive_strength & 0x000000FF;
+ pr_info("Enabling vddmin logging on modem id %d\n",
+ mdev->mdm_data.device_id);
+ req.id = vddmin_res->rpm_id;
+ req.value =
+ ((uint32_t)vddmin_res->ap2mdm_vddmin_gpio & 0x0000FFFF)
+ << 16;
+ req.value |= ((uint32_t)vddmin_res->modes & 0x000000FF) << 8;
+ req.value |= (uint32_t)vddmin_res->drive_strength & 0x000000FF;
- msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
+ msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
- /* Start monitoring low power gpio from mdm */
- irq = MSM_GPIO_TO_INT(vddmin_res->mdm2ap_vddmin_gpio);
- if (irq < 0) {
- pr_err("%s: could not get LPM POWER IRQ resource.\n",
- __func__);
- goto error_end;
+ /* Start monitoring low power gpio from mdm */
+ irq = MSM_GPIO_TO_INT(vddmin_res->mdm2ap_vddmin_gpio);
+ if (irq < 0)
+ pr_err("%s: could not get LPM POWER IRQ resource mdm id %d.\n",
+ __func__, mdev->mdm_data.device_id);
+ else {
+ ret = request_threaded_irq(irq, NULL, mdm_vddmin_change,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "mdm lpm", mdev);
+
+ if (ret < 0)
+ pr_err("%s: MDM LPM IRQ#%d request failed with error=%d",
+ __func__, irq, ret);
+ }
}
-
- ret = request_threaded_irq(irq, NULL, mdm_vddmin_change,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "mdm lpm", NULL);
-
- if (ret < 0)
- pr_err("%s: MDM LPM IRQ#%d request failed with error=%d",
- __func__, irq, ret);
-error_end:
+ spin_unlock_irqrestore(&mdm_devices_lock, flags);
return;
}
@@ -133,48 +314,59 @@
{
int ret, ntries = 0;
char sfr_buf[RD_BUF_SIZE];
+ struct mdm_platform_data *pdata;
+ struct mdm_device *mdev = container_of(work,
+ struct mdm_device, sfr_reason_work);
+ pdata = mdev->mdm_data.pdata;
do {
- msleep(SFR_RETRY_INTERVAL);
- ret = sysmon_get_reason(SYSMON_SS_EXT_MODEM,
+ if (pdata->sysmon_subsys_id_valid)
+ {
+ msleep(SFR_RETRY_INTERVAL);
+ ret = sysmon_get_reason(pdata->sysmon_subsys_id,
sfr_buf, sizeof(sfr_buf));
- if (ret) {
- /*
- * The sysmon device may not have been probed as yet
- * after the restart.
- */
- pr_err("%s: Error retrieving mdm restart reason, ret = %d, "
- "%d/%d tries\n", __func__, ret,
- ntries + 1, SFR_MAX_RETRIES);
- } else {
- pr_err("mdm restart reason: %s\n", sfr_buf);
- break;
+ if (ret) {
+ /*
+ * The sysmon device may not have been probed as
+ * yet after the restart.
+ */
+ pr_err("%s: Error retrieving restart reason,"
+ "ret = %d %d/%d tries\n",
+ __func__, ret,
+ ntries + 1,
+ SFR_MAX_RETRIES);
+ } else {
+ pr_err("mdm restart reason: %s\n", sfr_buf);
+ break;
+ }
}
} while (++ntries < SFR_MAX_RETRIES);
}
-static DECLARE_WORK(sfr_reason_work, mdm_restart_reason_fn);
-
static void mdm2ap_status_check(struct work_struct *work)
{
+ struct mdm_device *mdev =
+ container_of(work, struct mdm_device,
+ mdm2ap_status_check_work.work);
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
/*
* If the mdm modem did not pull the MDM2AP_STATUS gpio
* high then call subsystem_restart.
*/
if (!mdm_drv->disable_status_check) {
if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
- pr_err("%s: MDM2AP_STATUS gpio did not go high\n",
- __func__);
- mdm_drv->mdm_ready = 0;
- subsystem_restart_dev(mdm_subsys_dev);
+ pr_err("%s: MDM2AP_STATUS did not go high on mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
+ mdm_start_ssr(mdev);
}
}
}
-static DECLARE_DELAYED_WORK(mdm2ap_status_check_work, mdm2ap_status_check);
-
-static void mdm_update_gpio_configs(enum gpio_update_config gpio_config)
+static void mdm_update_gpio_configs(struct mdm_device *mdev,
+ enum gpio_update_config gpio_config)
{
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
+
/* Some gpio configuration may need updating after modem bootup.*/
switch (gpio_config) {
case GPIO_UPDATE_RUNNING_CONFIG:
@@ -182,20 +374,20 @@
if (msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
GPIOMUX_ACTIVE,
mdm_drv->pdata->mdm2ap_status_gpio_run_cfg,
- &mdm2ap_status_old_config))
- pr_err("%s: failed updating running gpio config\n",
- __func__);
+ &mdev->mdm2ap_status_old_config))
+ pr_err("%s: failed updating running gpio config mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
else
- mdm2ap_status_valid_old_config = 1;
+ mdev->mdm2ap_status_valid_old_config = 1;
}
break;
case GPIO_UPDATE_BOOTING_CONFIG:
- if (mdm2ap_status_valid_old_config) {
+ if (mdev->mdm2ap_status_valid_old_config) {
msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
GPIOMUX_ACTIVE,
- &mdm2ap_status_old_config,
+ &mdev->mdm2ap_status_old_config,
NULL);
- mdm2ap_status_valid_old_config = 0;
+ mdev->mdm2ap_status_valid_old_config = 0;
}
break;
default:
@@ -208,17 +400,29 @@
unsigned long arg)
{
int status, ret = 0;
+ struct mdm_device *mdev;
+ struct mdm_modem_drv *mdm_drv;
+
+ mdev = mdm_get_device_by_name(filp->f_path.dentry->d_iname);
+ if (!mdev) {
+ pr_err("%s: mdm_device not found\n", __func__);
+ return -ENODEV;
+ }
if (_IOC_TYPE(cmd) != CHARM_CODE) {
- pr_err("%s: invalid ioctl code\n", __func__);
+ pr_err("%s: invalid ioctl code to mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
return -EINVAL;
}
- pr_debug("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
+ mdm_drv = &mdev->mdm_data;
+ pr_debug("%s: Entering ioctl cmd = %d, mdm id = %d\n",
+ __func__, _IOC_NR(cmd), mdev->mdm_data.device_id);
switch (cmd) {
case WAKE_CHARM:
- pr_info("%s: Powering on mdm\n", __func__);
- mdm_drv->ops->power_on_mdm_cb(mdm_drv);
+ pr_info("%s: Powering on mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
+ mdm_ops->power_on_mdm_cb(mdm_drv);
break;
case CHECK_FOR_BOOT:
if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
@@ -227,30 +431,33 @@
put_user(0, (unsigned long __user *) arg);
break;
case NORMAL_BOOT_DONE:
- pr_debug("%s: check if mdm is booted up\n", __func__);
+ pr_debug("%s: check if mdm id %d is booted up\n",
+ __func__, mdev->mdm_data.device_id);
get_user(status, (unsigned long __user *) arg);
if (status) {
- pr_debug("%s: normal boot failed\n", __func__);
+ pr_debug("%s: normal boot of mdm id %d failed\n",
+ __func__, mdev->mdm_data.device_id);
mdm_drv->mdm_boot_status = -EIO;
} else {
- pr_info("%s: normal boot done\n", __func__);
+ pr_info("%s: normal boot of mdm id %d done\n",
+ __func__, mdev->mdm_data.device_id);
mdm_drv->mdm_boot_status = 0;
}
- mdm_drv->mdm_ready = 1;
+ atomic_set(&mdm_drv->mdm_ready, 1);
- if (mdm_drv->ops->normal_boot_done_cb != NULL)
- mdm_drv->ops->normal_boot_done_cb(mdm_drv);
+ if (mdm_ops->normal_boot_done_cb != NULL)
+ mdm_ops->normal_boot_done_cb(mdm_drv);
- if (!first_boot)
- complete(&mdm_boot);
+ if (!mdev->first_boot)
+ complete(&mdev->mdm_boot);
else
- first_boot = 0;
+ mdev->first_boot = 0;
/* If successful, start a timer to check that the mdm2ap_status
* gpio goes high.
*/
if (!status && gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
- schedule_delayed_work(&mdm2ap_status_check_work,
+ schedule_delayed_work(&mdev->mdm2ap_status_check_work,
msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
break;
case RAM_DUMP_DONE:
@@ -262,21 +469,22 @@
pr_info("%s: ramdump collection completed\n", __func__);
mdm_drv->mdm_ram_dump_status = 0;
}
- complete(&mdm_ram_dumps);
+ complete(&mdev->mdm_ram_dumps);
break;
case WAIT_FOR_RESTART:
pr_debug("%s: wait for mdm to need images reloaded\n",
__func__);
- ret = wait_for_completion_interruptible(&mdm_needs_reload);
+ ret = wait_for_completion_interruptible(
+ &mdev->mdm_needs_reload);
if (!ret)
put_user(mdm_drv->boot_type,
(unsigned long __user *) arg);
- INIT_COMPLETION(mdm_needs_reload);
+ init_completion(&mdev->mdm_needs_reload);
break;
case GET_DLOAD_STATUS:
pr_debug("getting status of mdm2ap_errfatal_gpio\n");
if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
- !mdm_drv->mdm_ready)
+ !atomic_read(&mdm_drv->mdm_ready))
put_user(1, (unsigned long __user *) arg);
else
put_user(0, (unsigned long __user *) arg);
@@ -284,14 +492,16 @@
case IMAGE_UPGRADE:
pr_debug("%s Image upgrade ioctl recieved\n", __func__);
if (mdm_drv->pdata->image_upgrade_supported &&
- mdm_drv->ops->image_upgrade_cb) {
+ mdm_ops->image_upgrade_cb) {
get_user(status, (unsigned long __user *) arg);
- mdm_drv->ops->image_upgrade_cb(mdm_drv, status);
+ mdm_ops->image_upgrade_cb(mdm_drv, status);
} else
pr_debug("%s Image upgrade not supported\n", __func__);
break;
case SHUTDOWN_CHARM:
- mdm_drv->mdm_ready = 0;
+ if (!mdm_drv->pdata->send_shdn)
+ break;
+ atomic_set(&mdm_drv->mdm_ready, 0);
if (mdm_debug_mask & MDM_DEBUG_MASK_SHDN_LOG)
pr_info("Sending shutdown request to mdm\n");
ret = sysmon_send_shutdown(SYSMON_SS_EXT_MODEM);
@@ -304,68 +514,71 @@
ret = -EINVAL;
break;
}
-
return ret;
}
static void mdm_status_fn(struct work_struct *work)
{
+ struct mdm_device *mdev =
+ container_of(work, struct mdm_device, mdm_status_work);
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
pr_debug("%s: status:%d\n", __func__, value);
- if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
- mdm_drv->ops->status_cb(mdm_drv, value);
+ if (atomic_read(&mdm_drv->mdm_ready) && mdm_ops->status_cb)
+ mdm_ops->status_cb(mdm_drv, value);
/* Update gpio configuration to "running" config. */
- mdm_update_gpio_configs(GPIO_UPDATE_RUNNING_CONFIG);
+ mdm_update_gpio_configs(mdev, GPIO_UPDATE_RUNNING_CONFIG);
}
-static DECLARE_WORK(mdm_status_work, mdm_status_fn);
-
-static void mdm_disable_irqs(void)
+static void mdm_disable_irqs(struct mdm_device *mdev)
{
- disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
- disable_irq_nosync(mdm_drv->mdm_status_irq);
+ if (!mdev)
+ return;
+ disable_irq_nosync(mdev->mdm_errfatal_irq);
+ disable_irq_nosync(mdev->mdm_status_irq);
+ disable_irq_nosync(mdev->mdm_pblrdy_irq);
}
static irqreturn_t mdm_errfatal(int irq, void *dev_id)
{
- pr_debug("%s: mdm got errfatal interrupt\n", __func__);
- if (mdm_drv->mdm_ready &&
+ struct mdm_modem_drv *mdm_drv;
+ struct mdm_device *mdev = (struct mdm_device *)dev_id;
+ if (!mdev)
+ return IRQ_HANDLED;
+
+ pr_debug("%s: mdm id %d sent errfatal interrupt\n",
+ __func__, mdev->mdm_data.device_id);
+ mdm_drv = &mdev->mdm_data;
+ if (atomic_read(&mdm_drv->mdm_ready) &&
(gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
- pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
- mdm_drv->mdm_ready = 0;
- subsystem_restart_dev(mdm_subsys_dev);
+ pr_info("%s: Received err fatal from mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
+ mdm_start_ssr(mdev);
}
return IRQ_HANDLED;
}
+/* set the mdm_device as the file's private data */
static int mdm_modem_open(struct inode *inode, struct file *file)
{
return 0;
}
-static const struct file_operations mdm_modem_fops = {
- .owner = THIS_MODULE,
- .open = mdm_modem_open,
- .unlocked_ioctl = mdm_modem_ioctl,
-};
-
-
-static struct miscdevice mdm_modem_misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "mdm",
- .fops = &mdm_modem_fops
-};
-
static int mdm_panic_prep(struct notifier_block *this,
unsigned long event, void *ptr)
{
int i;
+ struct mdm_modem_drv *mdm_drv;
+ struct mdm_device *mdev =
+ container_of(this, struct mdm_device, mdm_panic_blk);
+
+ mdm_drv = &mdev->mdm_data;
pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
__func__);
- mdm_disable_irqs();
+ mdm_disable_irqs(mdev);
gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
@@ -377,49 +590,67 @@
if (i <= 0) {
pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
/* Reset the modem so that it will go into download mode. */
- if (mdm_drv && mdm_drv->ops->atomic_reset_mdm_cb)
- mdm_drv->ops->atomic_reset_mdm_cb(mdm_drv);
+ if (mdm_drv && mdm_ops->atomic_reset_mdm_cb)
+ mdm_ops->atomic_reset_mdm_cb(mdm_drv);
}
return NOTIFY_DONE;
}
-static struct notifier_block mdm_panic_blk = {
- .notifier_call = mdm_panic_prep,
-};
-
static irqreturn_t mdm_status_change(int irq, void *dev_id)
{
- int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
+ struct mdm_modem_drv *mdm_drv;
+ struct mdm_device *mdev = (struct mdm_device *)dev_id;
+ int value;
+ if (!mdev)
+ return IRQ_HANDLED;
+
+ mdm_drv = &mdev->mdm_data;
+ value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
if ((mdm_debug_mask & MDM_DEBUG_MASK_SHDN_LOG) && (value == 0))
pr_info("%s: mdm2ap_status went low\n", __func__);
- pr_debug("%s: mdm sent status change interrupt\n", __func__);
- if (value == 0 && mdm_drv->mdm_ready == 1) {
- pr_info("%s: unexpected reset external modem\n", __func__);
+ pr_debug("%s: mdm id %d sent status change interrupt\n",
+ __func__, mdev->mdm_data.device_id);
+ if (value == 0 && atomic_read(&mdm_drv->mdm_ready)) {
+ pr_info("%s: unexpected reset external modem id %d\n",
+ __func__, mdev->mdm_data.device_id);
mdm_drv->mdm_unexpected_reset_occurred = 1;
- mdm_drv->mdm_ready = 0;
- subsystem_restart_dev(mdm_subsys_dev);
+ mdm_start_ssr(mdev);
} else if (value == 1) {
- cancel_delayed_work(&mdm2ap_status_check_work);
- pr_info("%s: status = 1: mdm is now ready\n", __func__);
- queue_work(mdm_queue, &mdm_status_work);
+ cancel_delayed_work(&mdev->mdm2ap_status_check_work);
+ pr_info("%s: status = 1: mdm id %d is now ready\n",
+ __func__, mdev->mdm_data.device_id);
+ queue_work(mdev->mdm_queue, &mdev->mdm_status_work);
}
return IRQ_HANDLED;
}
static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
{
- pr_info("%s: pbl ready:%d\n", __func__,
- gpio_get_value(mdm_drv->mdm2ap_pblrdy));
+ struct mdm_modem_drv *mdm_drv;
+ struct mdm_device *mdev = (struct mdm_device *)dev_id;
+ if (!mdev)
+ return IRQ_HANDLED;
+ mdm_drv = &mdev->mdm_data;
+ pr_info("%s: mdm id %d: pbl ready:%d\n",
+ __func__, mdev->mdm_data.device_id,
+ gpio_get_value(mdm_drv->mdm2ap_pblrdy));
return IRQ_HANDLED;
}
static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys)
{
- mdm_drv->mdm_ready = 0;
- cancel_delayed_work(&mdm2ap_status_check_work);
+ struct mdm_device *mdev =
+ container_of(crashed_subsys, struct mdm_device, mdm_subsys);
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
+
+ pr_debug("%s: ssr on modem id %d\n", __func__,
+ mdev->mdm_data.device_id);
+
+ mdm_ssr_started(mdev);
+ cancel_delayed_work(&mdev->mdm2ap_status_check_work);
gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
if (mdm_drv->pdata->ramdump_delay_ms > 0) {
/* Wait for the external modem to complete
@@ -428,9 +659,9 @@
msleep(mdm_drv->pdata->ramdump_delay_ms);
}
if (!mdm_drv->mdm_unexpected_reset_occurred) {
- mdm_drv->ops->reset_mdm_cb(mdm_drv);
+ mdm_ops->reset_mdm_cb(mdm_drv);
/* Update gpio configuration to "booting" config. */
- mdm_update_gpio_configs(GPIO_UPDATE_BOOTING_CONFIG);
+ mdm_update_gpio_configs(mdev, GPIO_UPDATE_BOOTING_CONFIG);
} else {
mdm_drv->mdm_unexpected_reset_occurred = 0;
}
@@ -439,59 +670,76 @@
static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
{
+ struct mdm_device *mdev =
+ container_of(crashed_subsys, struct mdm_device,
+ mdm_subsys);
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
+
+ pr_debug("%s: ssr on modem id %d\n",
+ __func__, mdev->mdm_data.device_id);
+
gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
- mdm_drv->ops->power_on_mdm_cb(mdm_drv);
+
+ if (mdm_drv->pdata->ps_hold_delay_ms > 0)
+ msleep(mdm_drv->pdata->ps_hold_delay_ms);
+
+ mdm_ops->power_on_mdm_cb(mdm_drv);
mdm_drv->boot_type = CHARM_NORMAL_BOOT;
- complete(&mdm_needs_reload);
- if (!wait_for_completion_timeout(&mdm_boot,
+ mdm_ssr_completed(mdev);
+ complete(&mdev->mdm_needs_reload);
+ if (!wait_for_completion_timeout(&mdev->mdm_boot,
msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
mdm_drv->mdm_boot_status = -ETIMEDOUT;
pr_info("%s: mdm modem restart timed out.\n", __func__);
} else {
- pr_info("%s: mdm modem has been restarted\n", __func__);
+ pr_info("%s: id %d: mdm modem has been restarted\n",
+ __func__, mdm_drv->device_id);
/* Log the reason for the restart */
if (mdm_drv->pdata->sfr_query)
- queue_work(mdm_sfr_queue, &sfr_reason_work);
+ queue_work(mdev->mdm_sfr_queue, &mdev->sfr_reason_work);
}
- INIT_COMPLETION(mdm_boot);
+ init_completion(&mdev->mdm_boot);
return mdm_drv->mdm_boot_status;
}
static int mdm_subsys_ramdumps(int want_dumps,
const struct subsys_desc *crashed_subsys)
{
+ struct mdm_device *mdev =
+ container_of(crashed_subsys, struct mdm_device,
+ mdm_subsys);
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
+
+ pr_debug("%s: ssr on modem id %d\n", __func__,
+ mdev->mdm_data.device_id);
+
mdm_drv->mdm_ram_dump_status = 0;
- cancel_delayed_work(&mdm2ap_status_check_work);
+ cancel_delayed_work(&mdev->mdm2ap_status_check_work);
if (want_dumps) {
mdm_drv->boot_type = CHARM_RAM_DUMPS;
- complete(&mdm_needs_reload);
- if (!wait_for_completion_timeout(&mdm_ram_dumps,
- msecs_to_jiffies(dump_timeout_ms))) {
+ complete(&mdev->mdm_needs_reload);
+ if (!wait_for_completion_timeout(&mdev->mdm_ram_dumps,
+ msecs_to_jiffies(mdev->dump_timeout_ms))) {
mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
- pr_info("%s: mdm modem ramdumps timed out.\n",
+ mdm_ssr_completed(mdev);
+ pr_err("%s: mdm modem ramdumps timed out.\n",
__func__);
} else
pr_info("%s: mdm modem ramdumps completed.\n",
__func__);
- INIT_COMPLETION(mdm_ram_dumps);
+ init_completion(&mdev->mdm_ram_dumps);
if (!mdm_drv->pdata->no_powerdown_after_ramdumps) {
- mdm_drv->ops->power_down_mdm_cb(mdm_drv);
+ mdm_ops->power_down_mdm_cb(mdm_drv);
/* Update gpio configuration to "booting" config. */
- mdm_update_gpio_configs(GPIO_UPDATE_BOOTING_CONFIG);
+ mdm_update_gpio_configs(mdev,
+ GPIO_UPDATE_BOOTING_CONFIG);
}
}
return mdm_drv->mdm_ram_dump_status;
}
-static struct subsys_desc mdm_subsystem = {
- .shutdown = mdm_subsys_shutdown,
- .ramdump = mdm_subsys_ramdumps,
- .powerup = mdm_subsys_powerup,
- .name = EXTERNAL_MODEM,
-};
-
/* Once the gpios are sent to RPM and debugging
* starts, there is no way to stop it without
* rebooting the device.
@@ -505,8 +753,8 @@
}
mdm_debug_mask = val;
- if (mdm_drv->ops->debug_state_changed_cb)
- mdm_drv->ops->debug_state_changed_cb(mdm_debug_mask);
+ if (mdm_ops->debug_state_changed_cb)
+ mdm_ops->debug_state_changed_cb(mdm_debug_mask);
return 0;
}
@@ -533,11 +781,55 @@
return 0;
}
-static void mdm_modem_initialize_data(struct platform_device *pdev,
- struct mdm_ops *mdm_ops)
+static const struct file_operations mdm_modem_fops = {
+ .owner = THIS_MODULE,
+ .open = mdm_modem_open,
+ .unlocked_ioctl = mdm_modem_ioctl,
+};
+
+static void mdm_modem_initialize_data(struct platform_device *pdev,
+ struct mdm_device *mdev)
{
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
struct resource *pres;
+ mdm_drv->pdata = pdev->dev.platform_data;
+ if (pdev->id < 0)
+ mdm_drv->device_id = 0;
+ else
+ mdm_drv->device_id = pdev->id;
+
+ memset((void *)&mdev->mdm_subsys, 0,
+ sizeof(struct subsys_desc));
+ if (mdev->mdm_data.device_id <= 0)
+ snprintf(mdev->subsys_name, sizeof(mdev->subsys_name),
+ "%s", EXTERNAL_MODEM);
+ else
+ snprintf(mdev->subsys_name, sizeof(mdev->subsys_name),
+ "%s.%d", EXTERNAL_MODEM, mdev->mdm_data.device_id);
+ mdev->mdm_subsys.shutdown = mdm_subsys_shutdown;
+ mdev->mdm_subsys.ramdump = mdm_subsys_ramdumps;
+ mdev->mdm_subsys.powerup = mdm_subsys_powerup;
+ mdev->mdm_subsys.name = mdev->subsys_name;
+
+ memset((void *)&mdev->misc_device, 0,
+ sizeof(struct miscdevice));
+ if (mdev->mdm_data.device_id <= 0)
+ snprintf(mdev->device_name, sizeof(mdev->device_name),
+ "%s", DEVICE_BASE_NAME);
+ else
+ snprintf(mdev->device_name, sizeof(mdev->device_name),
+ "%s%d", DEVICE_BASE_NAME, mdev->mdm_data.device_id);
+ mdev->misc_device.minor = MISC_DYNAMIC_MINOR;
+ mdev->misc_device.name = mdev->device_name;
+ mdev->misc_device.fops = &mdm_modem_fops;
+
+ memset((void *)&mdev->mdm_panic_blk, 0,
+ sizeof(struct notifier_block));
+ mdev->mdm_panic_blk.notifier_call = mdm_panic_prep;
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &mdev->mdm_panic_blk);
+
/* MDM2AP_ERRFATAL */
pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
"MDM2AP_ERRFATAL");
@@ -595,26 +887,49 @@
mdm_drv->boot_type = CHARM_NORMAL_BOOT;
- mdm_drv->ops = mdm_ops;
- mdm_drv->pdata = pdev->dev.platform_data;
- dump_timeout_ms = mdm_drv->pdata->ramdump_timeout_ms > 0 ?
+ mdm_drv->dump_timeout_ms = mdm_drv->pdata->ramdump_timeout_ms > 0 ?
mdm_drv->pdata->ramdump_timeout_ms : MDM_RDUMP_TIMEOUT;
+
+ init_completion(&mdev->mdm_needs_reload);
+ init_completion(&mdev->mdm_boot);
+ init_completion(&mdev->mdm_ram_dumps);
+
+ mdev->first_boot = 1;
+ mutex_init(&mdm_drv->peripheral_status_lock);
}
-int mdm_common_create(struct platform_device *pdev,
- struct mdm_ops *p_mdm_cb)
+static void mdm_deconfigure_ipc(struct mdm_device *mdev)
{
- int ret = -1, irq;
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
- mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
- if (mdm_drv == NULL) {
- pr_err("%s: kzalloc fail.\n", __func__);
- goto alloc_err;
+ gpio_free(mdm_drv->ap2mdm_status_gpio);
+ gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
+ if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
+ gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
+ if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
+ gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
+ gpio_free(mdm_drv->mdm2ap_status_gpio);
+ gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
+ if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
+ gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
+
+ if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
+ gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
+
+ if (mdev->mdm_queue) {
+ destroy_workqueue(mdev->mdm_queue);
+ mdev->mdm_queue = NULL;
}
+ if (mdev->mdm_sfr_queue) {
+ destroy_workqueue(mdev->mdm_sfr_queue);
+ mdev->mdm_sfr_queue = NULL;
+ }
+}
- mdm_modem_initialize_data(pdev, p_mdm_cb);
- if (mdm_drv->ops->debug_state_changed_cb)
- mdm_drv->ops->debug_state_changed_cb(mdm_debug_mask);
+static int mdm_configure_ipc(struct mdm_device *mdev)
+{
+ struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
+ int ret = -1, irq;
gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
@@ -641,7 +956,6 @@
mdm_drv->usb_switch_gpio = -1;
}
}
-
gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
@@ -651,96 +965,87 @@
gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
- mdm_queue = create_singlethread_workqueue("mdm_queue");
- if (!mdm_queue) {
- pr_err("%s: could not create workqueue. All mdm "
- "functionality will be disabled\n",
- __func__);
+ mdev->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
+ if (!mdev->mdm_queue) {
+ pr_err("%s: could not create mdm_queue for mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
ret = -ENOMEM;
goto fatal_err;
}
- mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
- if (!mdm_sfr_queue) {
- pr_err("%s: could not create workqueue mdm_sfr_queue."
- " All mdm functionality will be disabled\n",
- __func__);
+ mdev->mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
+ if (!mdev->mdm_sfr_queue) {
+ pr_err("%s: could not create mdm_sfr_queue for mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
ret = -ENOMEM;
- destroy_workqueue(mdm_queue);
goto fatal_err;
}
- atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
- mdm_debugfs_init();
-
/* Register subsystem handlers */
- mdm_subsys_dev = subsys_register(&mdm_subsystem);
- if (IS_ERR(mdm_subsys_dev)) {
- ret = PTR_ERR(mdm_subsys_dev);
+ mdev->mdm_subsys_dev = subsys_register(&mdev->mdm_subsys);
+ if (IS_ERR(mdev->mdm_subsys_dev)) {
+ ret = PTR_ERR(mdev->mdm_subsys_dev);
goto fatal_err;
}
/* ERR_FATAL irq. */
irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
if (irq < 0) {
- pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
- "error=%d No IRQ will be generated on errfatal.",
- __func__, irq);
+ pr_err("%s: bad MDM2AP_ERRFATAL IRQ resource, err = %d\n",
+ __func__, irq);
goto errfatal_err;
}
ret = request_irq(irq, mdm_errfatal,
- IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
+ IRQF_TRIGGER_RISING , "mdm errfatal", mdev);
if (ret < 0) {
- pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
- ". No IRQ will be generated on errfatal.",
- __func__, irq, ret);
+ pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed, err=%d\n",
+ __func__, irq, ret);
goto errfatal_err;
}
- mdm_drv->mdm_errfatal_irq = irq;
+ mdev->mdm_errfatal_irq = irq;
errfatal_err:
- /* status irq */
+ /* status irq */
irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
if (irq < 0) {
- pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
- "error=%d No IRQ will be generated on status change.",
- __func__, irq);
+ pr_err("%s: bad MDM2AP_STATUS IRQ resource, err = %d\n",
+ __func__, irq);
goto status_err;
}
ret = request_threaded_irq(irq, NULL, mdm_status_change,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
- "mdm status", mdm_drv);
+ "mdm status", mdev);
if (ret < 0) {
- pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
- ". No IRQ will be generated on status change.",
- __func__, irq, ret);
+ pr_err("%s: MDM2AP_STATUS IRQ#%d request failed, err=%d",
+ __func__, irq, ret);
goto status_err;
}
- mdm_drv->mdm_status_irq = irq;
+ mdev->mdm_status_irq = irq;
status_err:
if (GPIO_IS_VALID(mdm_drv->mdm2ap_pblrdy)) {
irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_pblrdy);
if (irq < 0) {
- pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource",
- __func__);
+ pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource\n",
+ __func__);
goto pblrdy_err;
}
ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
- IRQF_SHARED,
- "mdm pbl ready", mdm_drv);
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+ IRQF_SHARED,
+ "mdm pbl ready", mdev);
if (ret < 0) {
- pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d",
+ pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d\n",
__func__, irq, ret);
goto pblrdy_err;
}
+ mdev->mdm_pblrdy_irq = irq;
}
pblrdy_err:
@@ -751,67 +1056,140 @@
if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
- /* Perform early powerup of the external modem in order to
- * allow tabla devices to be found.
- */
- if (mdm_drv->pdata->early_power_on)
- mdm_drv->ops->power_on_mdm_cb(mdm_drv);
-
- pr_info("%s: Registering mdm modem\n", __func__);
- return misc_register(&mdm_modem_misc);
+ return 0;
fatal_err:
- gpio_free(mdm_drv->ap2mdm_status_gpio);
- gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
- gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
- gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
- gpio_free(mdm_drv->mdm2ap_status_gpio);
- gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
- gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
-
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
- gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
-
- kfree(mdm_drv);
- ret = -ENODEV;
-
-alloc_err:
+ mdm_deconfigure_ipc(mdev);
return ret;
}
-int mdm_common_modem_remove(struct platform_device *pdev)
+static int __devinit mdm_modem_probe(struct platform_device *pdev)
+{
+ struct mdm_device *mdev = NULL;
+ int ret = -1;
+
+ mdev = kzalloc(sizeof(struct mdm_device), GFP_KERNEL);
+ if (!mdev) {
+ pr_err("%s: kzalloc fail.\n", __func__);
+ ret = -ENOMEM;
+ goto init_err;
+ }
+
+ mdm_modem_initialize_data(pdev, mdev);
+
+ if (mdm_ops->debug_state_changed_cb)
+ mdm_ops->debug_state_changed_cb(mdm_debug_mask);
+
+ if (mdm_configure_ipc(mdev)) {
+ pr_err("%s: mdm_configure_ipc failed, id = %d\n",
+ __func__, mdev->mdm_data.device_id);
+ goto init_err;
+ }
+
+ pr_debug("%s: Registering mdm id %d\n", __func__,
+ mdev->mdm_data.device_id);
+ ret = misc_register(&mdev->misc_device);
+ if (ret) {
+ pr_err("%s: failed registering mdm id %d, ret = %d\n",
+ __func__, mdev->mdm_data.device_id, ret);
+ mdm_deconfigure_ipc(mdev);
+ goto init_err;
+ } else {
+ pr_err("%s: registered mdm id %d\n",
+ __func__, mdev->mdm_data.device_id);
+
+ mdm_device_list_add(mdev);
+ INIT_DELAYED_WORK(&mdev->mdm2ap_status_check_work,
+ mdm2ap_status_check);
+ INIT_WORK(&mdev->mdm_status_work, mdm_status_fn);
+ INIT_WORK(&mdev->sfr_reason_work, mdm_restart_reason_fn);
+
+ /* Perform early powerup of the external modem in order to
+ * allow tabla devices to be found.
+ */
+ if (mdev->mdm_data.pdata->early_power_on)
+ mdm_ops->power_on_mdm_cb(&mdev->mdm_data);
+ }
+
+ return ret;
+
+init_err:
+ kfree(mdev);
+ return ret;
+}
+
+static int __devexit mdm_modem_remove(struct platform_device *pdev)
{
int ret;
+ struct mdm_device *mdev = mdm_get_device_by_device_id(pdev->id);
- gpio_free(mdm_drv->ap2mdm_status_gpio);
- gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
- gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
- gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
- gpio_free(mdm_drv->mdm2ap_status_gpio);
- gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
- gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
+ if (!mdev)
+ return -ENODEV;
- if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
- gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
-
- kfree(mdm_drv);
-
- ret = misc_deregister(&mdm_modem_misc);
+ pr_debug("%s: removing device id %d\n",
+ __func__, mdev->mdm_data.device_id);
+ mdm_deconfigure_ipc(mdev);
+ ret = misc_deregister(&mdev->misc_device);
+ mdm_device_list_remove(mdev);
+ kfree(mdev);
return ret;
}
-void mdm_common_modem_shutdown(struct platform_device *pdev)
+static void mdm_modem_shutdown(struct platform_device *pdev)
{
- mdm_disable_irqs();
+ struct mdm_modem_drv *mdm_drv;
+ struct mdm_device *mdev = mdm_get_device_by_device_id(pdev->id);
+ if (!mdev)
+ return;
- mdm_drv->ops->power_down_mdm_cb(mdm_drv);
+ pr_debug("%s: shutting down device id %d\n",
+ __func__, mdev->mdm_data.device_id);
+
+ mdm_disable_irqs(mdev);
+ mdm_drv = &mdev->mdm_data;
+ mdm_ops->power_down_mdm_cb(mdm_drv);
if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
}
+static struct of_device_id mdm_match_table[] = {
+ {.compatible = "qcom,mdm2_modem,mdm2_modem.1"},
+ {},
+};
+
+static struct platform_driver mdm_modem_driver = {
+ .probe = mdm_modem_probe,
+ .remove = __devexit_p(mdm_modem_remove),
+ .shutdown = mdm_modem_shutdown,
+ .driver = {
+ .name = "mdm2_modem",
+ .owner = THIS_MODULE,
+ .of_match_table = mdm_match_table,
+ },
+};
+
+static int __init mdm_modem_init(void)
+{
+ int ret;
+
+ ret = mdm_get_ops(&mdm_ops);
+ if (ret)
+ return ret;
+
+ INIT_LIST_HEAD(&mdm_devices);
+ mdm_debugfs_init();
+ return platform_driver_register(&mdm_modem_driver);
+}
+
+static void __exit mdm_modem_exit(void)
+{
+ platform_driver_unregister(&mdm_modem_driver);
+}
+
+module_init(mdm_modem_init);
+module_exit(mdm_modem_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("mdm modem driver");
+MODULE_VERSION("2.0");
+MODULE_ALIAS("mdm_modem");
diff --git a/arch/arm/mach-msm/mdm_private.h b/arch/arm/mach-msm/mdm_private.h
index 9e865c5..147094c 100644
--- a/arch/arm/mach-msm/mdm_private.h
+++ b/arch/arm/mach-msm/mdm_private.h
@@ -44,25 +44,22 @@
unsigned mdm2ap_pblrdy;
unsigned usb_switch_gpio;
- int mdm_errfatal_irq;
- int mdm_status_irq;
- int mdm_ready;
+ atomic_t mdm_ready;
int mdm_boot_status;
int mdm_ram_dump_status;
enum charm_boot_type boot_type;
int mdm_debug_on;
int mdm_unexpected_reset_occurred;
int disable_status_check;
+ unsigned int dump_timeout_ms;
+ int power_on_count;
+ int peripheral_status;
+ struct mutex peripheral_status_lock;
+ int device_id;
- struct mdm_ops *ops;
struct mdm_platform_data *pdata;
};
-
-int mdm_common_create(struct platform_device *pdev,
- struct mdm_ops *mdm_cb);
-int mdm_common_modem_remove(struct platform_device *pdev);
-void mdm_common_modem_shutdown(struct platform_device *pdev);
-void mdm_common_set_debug_state(int value);
+int mdm_get_ops(struct mdm_ops **mdm_ops);
#endif