| Joel King | e9cd527 | 2012-01-28 12:48:59 -0800 | [diff] [blame] | 1 | /* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. | 
| Joel King | b6f0f61 | 2011-11-01 16:59:14 -0700 | [diff] [blame] | 2 | * | 
|  | 3 | * This program is free software; you can redistribute it and/or modify | 
|  | 4 | * it under the terms of the GNU General Public License version 2 and | 
|  | 5 | * only version 2 as published by the Free Software Foundation. | 
|  | 6 | * | 
|  | 7 | * This program is distributed in the hope that it will be useful, | 
|  | 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 10 | * GNU General Public License for more details. | 
|  | 11 | * | 
|  | 12 | */ | 
|  | 13 |  | 
|  | 14 | #include <linux/module.h> | 
|  | 15 | #include <linux/platform_device.h> | 
|  | 16 | #include <linux/err.h> | 
|  | 17 | #include <linux/slab.h> | 
|  | 18 | #include <linux/io.h> | 
|  | 19 | #include <linux/mutex.h> | 
|  | 20 | #include <linux/miscdevice.h> | 
|  | 21 | #include <linux/fs.h> | 
|  | 22 | #include <linux/gpio.h> | 
|  | 23 | #include <linux/kernel.h> | 
|  | 24 | #include <linux/irq.h> | 
|  | 25 | #include <linux/ioctl.h> | 
|  | 26 | #include <linux/delay.h> | 
|  | 27 | #include <linux/reboot.h> | 
|  | 28 | #include <linux/debugfs.h> | 
|  | 29 | #include <linux/completion.h> | 
|  | 30 | #include <linux/workqueue.h> | 
|  | 31 | #include <linux/clk.h> | 
|  | 32 | #include <linux/mfd/pmic8058.h> | 
|  | 33 | #include <asm/mach-types.h> | 
|  | 34 | #include <asm/uaccess.h> | 
|  | 35 | #include <mach/mdm2.h> | 
|  | 36 | #include <mach/restart.h> | 
|  | 37 | #include <mach/subsystem_notif.h> | 
|  | 38 | #include <mach/subsystem_restart.h> | 
|  | 39 | #include <linux/msm_charm.h> | 
|  | 40 | #include "msm_watchdog.h" | 
|  | 41 | #include "devices.h" | 
|  | 42 | #include "clock.h" | 
|  | 43 | #include "mdm_private.h" | 
| Vamsi Krishna | c6dcd5e | 2012-05-09 15:38:01 -0700 | [diff] [blame] | 44 | #define MDM_PBLRDY_CNT		20 | 
| Joel King | b6f0f61 | 2011-11-01 16:59:14 -0700 | [diff] [blame] | 45 |  | 
| Joel King | 96c96dc | 2012-07-30 09:06:15 -0700 | [diff] [blame] | 46 | static int mdm_debug_mask; | 
| Joel King | 3da02a9 | 2012-03-20 10:55:52 -0700 | [diff] [blame] | 47 | static int power_on_count; | 
| Hemant Kumar | f1ca919 | 2012-02-07 18:59:33 -0800 | [diff] [blame] | 48 | static int hsic_peripheral_status; | 
| Joel King | d8052c0 | 2012-02-03 12:33:31 -0800 | [diff] [blame] | 49 | static DEFINE_MUTEX(hsic_status_lock); | 
|  | 50 |  | 
|  | 51 | static void mdm_peripheral_connect(struct mdm_modem_drv *mdm_drv) | 
|  | 52 | { | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 53 | if (!mdm_drv->pdata->peripheral_platform_device) | 
|  | 54 | return; | 
|  | 55 |  | 
| Joel King | d8052c0 | 2012-02-03 12:33:31 -0800 | [diff] [blame] | 56 | mutex_lock(&hsic_status_lock); | 
|  | 57 | if (hsic_peripheral_status) | 
|  | 58 | goto out; | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 59 | platform_device_add(mdm_drv->pdata->peripheral_platform_device); | 
| Joel King | d8052c0 | 2012-02-03 12:33:31 -0800 | [diff] [blame] | 60 | hsic_peripheral_status = 1; | 
|  | 61 | out: | 
|  | 62 | mutex_unlock(&hsic_status_lock); | 
|  | 63 | } | 
|  | 64 |  | 
|  | 65 | static void mdm_peripheral_disconnect(struct mdm_modem_drv *mdm_drv) | 
|  | 66 | { | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 67 | if (!mdm_drv->pdata->peripheral_platform_device) | 
|  | 68 | return; | 
|  | 69 |  | 
| Joel King | d8052c0 | 2012-02-03 12:33:31 -0800 | [diff] [blame] | 70 | mutex_lock(&hsic_status_lock); | 
|  | 71 | if (!hsic_peripheral_status) | 
|  | 72 | goto out; | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 73 | platform_device_del(mdm_drv->pdata->peripheral_platform_device); | 
| Joel King | d8052c0 | 2012-02-03 12:33:31 -0800 | [diff] [blame] | 74 | hsic_peripheral_status = 0; | 
|  | 75 | out: | 
|  | 76 | mutex_unlock(&hsic_status_lock); | 
|  | 77 | } | 
| Joel King | b6f0f61 | 2011-11-01 16:59:14 -0700 | [diff] [blame] | 78 |  | 
| Joel King | a7a7b9a | 2012-06-28 13:35:28 -0700 | [diff] [blame] | 79 | /* This function can be called from atomic context. */ | 
| Joel King | 733377c | 2012-06-20 13:07:38 -0700 | [diff] [blame] | 80 | static void mdm_toggle_soft_reset(struct mdm_modem_drv *mdm_drv) | 
|  | 81 | { | 
|  | 82 | int soft_reset_direction_assert = 0, | 
|  | 83 | soft_reset_direction_de_assert = 1; | 
|  | 84 |  | 
|  | 85 | if (mdm_drv->pdata->soft_reset_inverted) { | 
|  | 86 | soft_reset_direction_assert = 1; | 
|  | 87 | soft_reset_direction_de_assert = 0; | 
|  | 88 | } | 
|  | 89 | gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio, | 
|  | 90 | soft_reset_direction_assert); | 
| Joel King | a7a7b9a | 2012-06-28 13:35:28 -0700 | [diff] [blame] | 91 | /* Use mdelay because this function can be called from atomic | 
|  | 92 | * context. | 
|  | 93 | */ | 
|  | 94 | mdelay(10); | 
| Joel King | 733377c | 2012-06-20 13:07:38 -0700 | [diff] [blame] | 95 | gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio, | 
|  | 96 | soft_reset_direction_de_assert); | 
|  | 97 | } | 
|  | 98 |  | 
| Joel King | a7a7b9a | 2012-06-28 13:35:28 -0700 | [diff] [blame] | 99 | /* This function can be called from atomic context. */ | 
|  | 100 | static void mdm_atomic_soft_reset(struct mdm_modem_drv *mdm_drv) | 
|  | 101 | { | 
|  | 102 | mdm_toggle_soft_reset(mdm_drv); | 
|  | 103 | } | 
|  | 104 |  | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 105 | static void mdm_power_down_common(struct mdm_modem_drv *mdm_drv) | 
|  | 106 | { | 
| Joel King | 733377c | 2012-06-20 13:07:38 -0700 | [diff] [blame] | 107 | int i; | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 108 | int soft_reset_direction = | 
|  | 109 | mdm_drv->pdata->soft_reset_inverted ? 1 : 0; | 
|  | 110 |  | 
| Hemant Kumar | 069f643 | 2012-08-22 18:30:19 -0700 | [diff] [blame] | 111 | mdm_peripheral_disconnect(mdm_drv); | 
|  | 112 |  | 
| Joel King | 733377c | 2012-06-20 13:07:38 -0700 | [diff] [blame] | 113 | /* Wait for the modem to complete its power down actions. */ | 
|  | 114 | for (i = 20; i > 0; i--) { | 
|  | 115 | if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) | 
|  | 116 | break; | 
|  | 117 | msleep(100); | 
|  | 118 | } | 
|  | 119 | if (i == 0) { | 
|  | 120 | pr_err("%s: MDM2AP_STATUS never went low. Doing a hard reset\n", | 
|  | 121 | __func__); | 
|  | 122 | gpio_direction_output(mdm_drv->ap2mdm_soft_reset_gpio, | 
|  | 123 | soft_reset_direction); | 
|  | 124 | /* | 
|  | 125 | * Currently, there is a debounce timer on the charm PMIC. It is | 
|  | 126 | * necessary to hold the PMIC RESET low for ~3.5 seconds | 
|  | 127 | * for the reset to fully take place. Sleep here to ensure the | 
|  | 128 | * reset has occured before the function exits. | 
|  | 129 | */ | 
|  | 130 | msleep(4000); | 
|  | 131 | } | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 132 | } | 
|  | 133 |  | 
|  | 134 | static void mdm_do_first_power_on(struct mdm_modem_drv *mdm_drv) | 
|  | 135 | { | 
| Vamsi Krishna | c6dcd5e | 2012-05-09 15:38:01 -0700 | [diff] [blame] | 136 | int i; | 
|  | 137 | int pblrdy; | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 138 | if (power_on_count != 1) { | 
|  | 139 | pr_err("%s: Calling fn when power_on_count != 1\n", | 
|  | 140 | __func__); | 
|  | 141 | return; | 
|  | 142 | } | 
|  | 143 |  | 
|  | 144 | pr_err("%s: Powering on modem for the first time\n", __func__); | 
|  | 145 | mdm_peripheral_disconnect(mdm_drv); | 
|  | 146 |  | 
| Joel King | 733377c | 2012-06-20 13:07:38 -0700 | [diff] [blame] | 147 | /* If this is the first power-up after a panic, the modem may still | 
|  | 148 | * be in a power-on state, in which case we need to toggle the gpio | 
|  | 149 | * instead of just de-asserting it. No harm done if the modem was | 
|  | 150 | * powered down. | 
|  | 151 | */ | 
|  | 152 | mdm_toggle_soft_reset(mdm_drv); | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 153 | /* If the device has a kpd pwr gpio then toggle it. */ | 
| Ameya Thakur | e155ece | 2012-07-09 12:08:37 -0700 | [diff] [blame] | 154 | if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio)) { | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 155 | /* Pull AP2MDM_KPDPWR gpio high and wait for PS_HOLD to settle, | 
|  | 156 | * then	pull it back low. | 
|  | 157 | */ | 
|  | 158 | pr_debug("%s: Pulling AP2MDM_KPDPWR gpio high\n", __func__); | 
|  | 159 | gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 1); | 
|  | 160 | msleep(1000); | 
|  | 161 | gpio_direction_output(mdm_drv->ap2mdm_kpdpwr_n_gpio, 0); | 
|  | 162 | } | 
|  | 163 |  | 
| Ameya Thakur | e155ece | 2012-07-09 12:08:37 -0700 | [diff] [blame] | 164 | if (!GPIO_IS_VALID(mdm_drv->mdm2ap_pblrdy)) | 
| Vamsi Krishna | c6dcd5e | 2012-05-09 15:38:01 -0700 | [diff] [blame] | 165 | goto start_mdm_peripheral; | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 166 |  | 
| Vamsi Krishna | c6dcd5e | 2012-05-09 15:38:01 -0700 | [diff] [blame] | 167 | for (i = 0; i  < MDM_PBLRDY_CNT; i++) { | 
|  | 168 | pblrdy = gpio_get_value(mdm_drv->mdm2ap_pblrdy); | 
|  | 169 | if (pblrdy) | 
|  | 170 | break; | 
|  | 171 | usleep_range(5000, 5000); | 
|  | 172 | } | 
| Vamsi Krishna | c6dcd5e | 2012-05-09 15:38:01 -0700 | [diff] [blame] | 173 | pr_debug("%s: i:%d\n", __func__, i); | 
|  | 174 |  | 
|  | 175 | start_mdm_peripheral: | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 176 | mdm_peripheral_connect(mdm_drv); | 
|  | 177 | msleep(200); | 
|  | 178 | } | 
|  | 179 |  | 
|  | 180 | static void mdm_do_soft_power_on(struct mdm_modem_drv *mdm_drv) | 
|  | 181 | { | 
| Vamsi Krishna | c6dcd5e | 2012-05-09 15:38:01 -0700 | [diff] [blame] | 182 | int i; | 
|  | 183 | int pblrdy; | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 184 |  | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 185 | pr_err("%s: soft resetting mdm modem\n", __func__); | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 186 | mdm_peripheral_disconnect(mdm_drv); | 
| Joel King | 733377c | 2012-06-20 13:07:38 -0700 | [diff] [blame] | 187 | mdm_toggle_soft_reset(mdm_drv); | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 188 |  | 
| Ameya Thakur | e155ece | 2012-07-09 12:08:37 -0700 | [diff] [blame] | 189 | if (!GPIO_IS_VALID(mdm_drv->mdm2ap_pblrdy)) | 
| Vamsi Krishna | c6dcd5e | 2012-05-09 15:38:01 -0700 | [diff] [blame] | 190 | goto start_mdm_peripheral; | 
|  | 191 |  | 
|  | 192 | for (i = 0; i  < MDM_PBLRDY_CNT; i++) { | 
|  | 193 | pblrdy = gpio_get_value(mdm_drv->mdm2ap_pblrdy); | 
|  | 194 | if (pblrdy) | 
|  | 195 | break; | 
|  | 196 | usleep_range(5000, 5000); | 
|  | 197 | } | 
|  | 198 |  | 
|  | 199 | pr_debug("%s: i:%d\n", __func__, i); | 
|  | 200 |  | 
|  | 201 | start_mdm_peripheral: | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 202 | mdm_peripheral_connect(mdm_drv); | 
|  | 203 | msleep(200); | 
|  | 204 | } | 
|  | 205 |  | 
|  | 206 | static void mdm_power_on_common(struct mdm_modem_drv *mdm_drv) | 
| Joel King | b6f0f61 | 2011-11-01 16:59:14 -0700 | [diff] [blame] | 207 | { | 
| Joel King | 3da02a9 | 2012-03-20 10:55:52 -0700 | [diff] [blame] | 208 | power_on_count++; | 
|  | 209 |  | 
| Vamsi Krishna | 9e307cd | 2012-04-11 13:15:36 -0700 | [diff] [blame] | 210 | /* this gpio will be used to indicate apq readiness, | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 211 | * de-assert it now so that it can be asserted later. | 
|  | 212 | * May not be used. | 
| Vamsi Krishna | 9e307cd | 2012-04-11 13:15:36 -0700 | [diff] [blame] | 213 | */ | 
| Ameya Thakur | e155ece | 2012-07-09 12:08:37 -0700 | [diff] [blame] | 214 | if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio)) | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 215 | gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0); | 
| Vamsi Krishna | 9e307cd | 2012-04-11 13:15:36 -0700 | [diff] [blame] | 216 |  | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 217 | /* | 
|  | 218 | * If we did an "early power on" then ignore the very next | 
|  | 219 | * power-on request because it would the be first request from | 
|  | 220 | * user space but we're already powered on. Ignore it. | 
| Joel King | 3da02a9 | 2012-03-20 10:55:52 -0700 | [diff] [blame] | 221 | */ | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 222 | if (mdm_drv->pdata->early_power_on && | 
|  | 223 | (power_on_count == 2)) | 
| Joel King | 3da02a9 | 2012-03-20 10:55:52 -0700 | [diff] [blame] | 224 | return; | 
|  | 225 |  | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 226 | if (power_on_count == 1) | 
|  | 227 | mdm_do_first_power_on(mdm_drv); | 
|  | 228 | else | 
|  | 229 | mdm_do_soft_power_on(mdm_drv); | 
| Joel King | b6f0f61 | 2011-11-01 16:59:14 -0700 | [diff] [blame] | 230 | } | 
|  | 231 |  | 
| Joel King | b6f0f61 | 2011-11-01 16:59:14 -0700 | [diff] [blame] | 232 | static void debug_state_changed(int value) | 
|  | 233 | { | 
| Joel King | 96c96dc | 2012-07-30 09:06:15 -0700 | [diff] [blame] | 234 | mdm_debug_mask = value; | 
| Joel King | b6f0f61 | 2011-11-01 16:59:14 -0700 | [diff] [blame] | 235 | } | 
|  | 236 |  | 
| Joel King | d8052c0 | 2012-02-03 12:33:31 -0800 | [diff] [blame] | 237 | static void mdm_status_changed(struct mdm_modem_drv *mdm_drv, int value) | 
| Vamsi Krishna | 3392563 | 2011-12-13 15:43:09 -0800 | [diff] [blame] | 238 | { | 
| Joel King | 2a42f50 | 2012-02-03 11:36:25 -0800 | [diff] [blame] | 239 | pr_debug("%s: value:%d\n", __func__, value); | 
| Vamsi Krishna | 3392563 | 2011-12-13 15:43:09 -0800 | [diff] [blame] | 240 |  | 
|  | 241 | if (value) { | 
| Joel King | d8052c0 | 2012-02-03 12:33:31 -0800 | [diff] [blame] | 242 | mdm_peripheral_disconnect(mdm_drv); | 
|  | 243 | mdm_peripheral_connect(mdm_drv); | 
| Ameya Thakur | e155ece | 2012-07-09 12:08:37 -0700 | [diff] [blame] | 244 | if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio)) | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 245 | gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 1); | 
| Vamsi Krishna | 3392563 | 2011-12-13 15:43:09 -0800 | [diff] [blame] | 246 | } | 
|  | 247 | } | 
|  | 248 |  | 
| Ameya Thakur | 43248fd | 2012-07-10 18:50:52 -0700 | [diff] [blame] | 249 | static void mdm_image_upgrade(struct mdm_modem_drv *mdm_drv, int type) | 
|  | 250 | { | 
|  | 251 | switch (type) { | 
|  | 252 | case APQ_CONTROLLED_UPGRADE: | 
|  | 253 | pr_debug("%s APQ controlled modem image upgrade\n", __func__); | 
|  | 254 | mdm_drv->mdm_ready = 0; | 
|  | 255 | mdm_toggle_soft_reset(mdm_drv); | 
|  | 256 | break; | 
|  | 257 | case MDM_CONTROLLED_UPGRADE: | 
|  | 258 | pr_debug("%s MDM controlled modem image upgrade\n", __func__); | 
|  | 259 | mdm_drv->mdm_ready = 0; | 
|  | 260 | /* | 
|  | 261 | * If we have no image currently present on the modem, then we | 
|  | 262 | * would be in PBL, in which case the status gpio would not go | 
|  | 263 | * high. | 
|  | 264 | */ | 
|  | 265 | mdm_drv->disable_status_check = 1; | 
| Ameya Thakur | e155ece | 2012-07-09 12:08:37 -0700 | [diff] [blame] | 266 | if (GPIO_IS_VALID(mdm_drv->usb_switch_gpio)) { | 
| Ameya Thakur | 43248fd | 2012-07-10 18:50:52 -0700 | [diff] [blame] | 267 | pr_info("%s Switching usb control to MDM\n", __func__); | 
|  | 268 | gpio_direction_output(mdm_drv->usb_switch_gpio, 1); | 
|  | 269 | } else | 
|  | 270 | pr_err("%s usb switch gpio unavailable\n", __func__); | 
|  | 271 | break; | 
|  | 272 | default: | 
|  | 273 | pr_err("%s invalid upgrade type\n", __func__); | 
|  | 274 | } | 
|  | 275 | } | 
| Joel King | e9cd527 | 2012-01-28 12:48:59 -0800 | [diff] [blame] | 276 | static struct mdm_ops mdm_cb = { | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 277 | .power_on_mdm_cb = mdm_power_on_common, | 
|  | 278 | .reset_mdm_cb = mdm_power_on_common, | 
| Joel King | a7a7b9a | 2012-06-28 13:35:28 -0700 | [diff] [blame] | 279 | .atomic_reset_mdm_cb = mdm_atomic_soft_reset, | 
| Joel King | e92eb87 | 2012-05-06 09:30:24 -0700 | [diff] [blame] | 280 | .power_down_mdm_cb = mdm_power_down_common, | 
| Joel King | e9cd527 | 2012-01-28 12:48:59 -0800 | [diff] [blame] | 281 | .debug_state_changed_cb = debug_state_changed, | 
|  | 282 | .status_cb = mdm_status_changed, | 
| Ameya Thakur | 43248fd | 2012-07-10 18:50:52 -0700 | [diff] [blame] | 283 | .image_upgrade_cb = mdm_image_upgrade, | 
| Joel King | e9cd527 | 2012-01-28 12:48:59 -0800 | [diff] [blame] | 284 | }; | 
|  | 285 |  | 
| Joel King | b6f0f61 | 2011-11-01 16:59:14 -0700 | [diff] [blame] | 286 | static int __init mdm_modem_probe(struct platform_device *pdev) | 
|  | 287 | { | 
| Joel King | b6f0f61 | 2011-11-01 16:59:14 -0700 | [diff] [blame] | 288 | return mdm_common_create(pdev, &mdm_cb); | 
|  | 289 | } | 
|  | 290 |  | 
|  | 291 | static int __devexit mdm_modem_remove(struct platform_device *pdev) | 
|  | 292 | { | 
|  | 293 | return mdm_common_modem_remove(pdev); | 
|  | 294 | } | 
|  | 295 |  | 
|  | 296 | static void mdm_modem_shutdown(struct platform_device *pdev) | 
|  | 297 | { | 
|  | 298 | mdm_common_modem_shutdown(pdev); | 
|  | 299 | } | 
|  | 300 |  | 
|  | 301 | static struct platform_driver mdm_modem_driver = { | 
|  | 302 | .remove         = mdm_modem_remove, | 
|  | 303 | .shutdown	= mdm_modem_shutdown, | 
|  | 304 | .driver         = { | 
|  | 305 | .name = "mdm2_modem", | 
|  | 306 | .owner = THIS_MODULE | 
|  | 307 | }, | 
|  | 308 | }; | 
|  | 309 |  | 
|  | 310 | static int __init mdm_modem_init(void) | 
|  | 311 | { | 
|  | 312 | return platform_driver_probe(&mdm_modem_driver, mdm_modem_probe); | 
|  | 313 | } | 
|  | 314 |  | 
|  | 315 | static void __exit mdm_modem_exit(void) | 
|  | 316 | { | 
|  | 317 | platform_driver_unregister(&mdm_modem_driver); | 
|  | 318 | } | 
|  | 319 |  | 
|  | 320 | module_init(mdm_modem_init); | 
|  | 321 | module_exit(mdm_modem_exit); | 
|  | 322 |  | 
|  | 323 | MODULE_LICENSE("GPL v2"); | 
|  | 324 | MODULE_DESCRIPTION("mdm modem driver"); | 
|  | 325 | MODULE_VERSION("2.0"); | 
|  | 326 | MODULE_ALIAS("mdm_modem"); |