0001
0002
0003
0004
0005
0006
0007 #include <linux/delay.h>
0008 #include <linux/err.h>
0009 #include <linux/io.h>
0010 #include <linux/interrupt.h>
0011 #include <linux/module.h>
0012 #include <linux/of.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/slab.h>
0015
0016 #include <linux/gpio/consumer.h>
0017 #include <linux/of_platform.h>
0018 #include <linux/phy/phy.h>
0019 #include <linux/pinctrl/consumer.h>
0020
0021 #define PHY_MDM6600_PHY_DELAY_MS 4000
0022 #define PHY_MDM6600_ENABLED_DELAY_MS 8000
0023 #define PHY_MDM6600_WAKE_KICK_MS 600
0024 #define MDM6600_MODEM_IDLE_DELAY_MS 1000
0025 #define MDM6600_MODEM_WAKE_DELAY_MS 200
0026
0027 enum phy_mdm6600_ctrl_lines {
0028 PHY_MDM6600_ENABLE,
0029 PHY_MDM6600_POWER,
0030 PHY_MDM6600_RESET,
0031 PHY_MDM6600_NR_CTRL_LINES,
0032 };
0033
0034 enum phy_mdm6600_bootmode_lines {
0035 PHY_MDM6600_MODE0,
0036 PHY_MDM6600_MODE1,
0037 PHY_MDM6600_NR_MODE_LINES,
0038 };
0039
0040 enum phy_mdm6600_cmd_lines {
0041 PHY_MDM6600_CMD0,
0042 PHY_MDM6600_CMD1,
0043 PHY_MDM6600_CMD2,
0044 PHY_MDM6600_NR_CMD_LINES,
0045 };
0046
0047 enum phy_mdm6600_status_lines {
0048 PHY_MDM6600_STATUS0,
0049 PHY_MDM6600_STATUS1,
0050 PHY_MDM6600_STATUS2,
0051 PHY_MDM6600_NR_STATUS_LINES,
0052 };
0053
0054
0055
0056
0057
0058 enum phy_mdm6600_cmd {
0059 PHY_MDM6600_CMD_BP_PANIC_ACK,
0060 PHY_MDM6600_CMD_DATA_ONLY_BYPASS,
0061 PHY_MDM6600_CMD_FULL_BYPASS,
0062 PHY_MDM6600_CMD_NO_BYPASS,
0063 PHY_MDM6600_CMD_BP_SHUTDOWN_REQ,
0064 PHY_MDM6600_CMD_BP_UNKNOWN_5,
0065 PHY_MDM6600_CMD_BP_UNKNOWN_6,
0066 PHY_MDM6600_CMD_UNDEFINED,
0067 };
0068
0069
0070
0071
0072
0073 enum phy_mdm6600_status {
0074 PHY_MDM6600_STATUS_PANIC,
0075 PHY_MDM6600_STATUS_PANIC_BUSY_WAIT,
0076 PHY_MDM6600_STATUS_QC_DLOAD,
0077 PHY_MDM6600_STATUS_RAM_DOWNLOADER,
0078 PHY_MDM6600_STATUS_PHONE_CODE_AWAKE,
0079 PHY_MDM6600_STATUS_PHONE_CODE_ASLEEP,
0080 PHY_MDM6600_STATUS_SHUTDOWN_ACK,
0081 PHY_MDM6600_STATUS_UNDEFINED,
0082 };
0083
0084 static const char * const
0085 phy_mdm6600_status_name[] = {
0086 "off", "busy", "qc_dl", "ram_dl", "awake",
0087 "asleep", "shutdown", "undefined",
0088 };
0089
0090 struct phy_mdm6600 {
0091 struct device *dev;
0092 struct phy *generic_phy;
0093 struct phy_provider *phy_provider;
0094 struct gpio_desc *ctrl_gpios[PHY_MDM6600_NR_CTRL_LINES];
0095 struct gpio_descs *mode_gpios;
0096 struct gpio_descs *status_gpios;
0097 struct gpio_descs *cmd_gpios;
0098 struct delayed_work bootup_work;
0099 struct delayed_work status_work;
0100 struct delayed_work modem_wake_work;
0101 struct completion ack;
0102 bool enabled;
0103 bool running;
0104 bool awake;
0105 int status;
0106 };
0107
0108 static int phy_mdm6600_init(struct phy *x)
0109 {
0110 struct phy_mdm6600 *ddata = phy_get_drvdata(x);
0111 struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
0112
0113 if (!ddata->enabled)
0114 return -EPROBE_DEFER;
0115
0116 gpiod_set_value_cansleep(enable_gpio, 0);
0117
0118 return 0;
0119 }
0120
0121 static int phy_mdm6600_power_on(struct phy *x)
0122 {
0123 struct phy_mdm6600 *ddata = phy_get_drvdata(x);
0124 struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
0125 int error;
0126
0127 if (!ddata->enabled)
0128 return -ENODEV;
0129
0130 error = pinctrl_pm_select_default_state(ddata->dev);
0131 if (error)
0132 dev_warn(ddata->dev, "%s: error with default_state: %i\n",
0133 __func__, error);
0134
0135 gpiod_set_value_cansleep(enable_gpio, 1);
0136
0137
0138 if (pm_runtime_enabled(&x->dev))
0139 phy_pm_runtime_put(x);
0140
0141 return 0;
0142 }
0143
0144 static int phy_mdm6600_power_off(struct phy *x)
0145 {
0146 struct phy_mdm6600 *ddata = phy_get_drvdata(x);
0147 struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
0148 int error;
0149
0150 if (!ddata->enabled)
0151 return -ENODEV;
0152
0153
0154 if (pm_runtime_enabled(&x->dev)) {
0155 error = phy_pm_runtime_get(x);
0156 if (error < 0 && error != -EINPROGRESS)
0157 dev_warn(ddata->dev, "%s: phy_pm_runtime_get: %i\n",
0158 __func__, error);
0159 }
0160
0161 gpiod_set_value_cansleep(enable_gpio, 0);
0162
0163 error = pinctrl_pm_select_sleep_state(ddata->dev);
0164 if (error)
0165 dev_warn(ddata->dev, "%s: error with sleep_state: %i\n",
0166 __func__, error);
0167
0168 return 0;
0169 }
0170
0171 static const struct phy_ops gpio_usb_ops = {
0172 .init = phy_mdm6600_init,
0173 .power_on = phy_mdm6600_power_on,
0174 .power_off = phy_mdm6600_power_off,
0175 .owner = THIS_MODULE,
0176 };
0177
0178
0179
0180
0181
0182
0183
0184
0185 static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
0186 {
0187 DECLARE_BITMAP(values, PHY_MDM6600_NR_CMD_LINES);
0188
0189 values[0] = val;
0190
0191 gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
0192 ddata->cmd_gpios->desc,
0193 ddata->cmd_gpios->info, values);
0194 }
0195
0196
0197
0198
0199
0200 static void phy_mdm6600_status(struct work_struct *work)
0201 {
0202 struct phy_mdm6600 *ddata;
0203 struct device *dev;
0204 DECLARE_BITMAP(values, PHY_MDM6600_NR_STATUS_LINES);
0205 int error;
0206
0207 ddata = container_of(work, struct phy_mdm6600, status_work.work);
0208 dev = ddata->dev;
0209
0210 error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES,
0211 ddata->status_gpios->desc,
0212 ddata->status_gpios->info,
0213 values);
0214 if (error)
0215 return;
0216
0217 ddata->status = values[0] & ((1 << PHY_MDM6600_NR_STATUS_LINES) - 1);
0218
0219 dev_info(dev, "modem status: %i %s\n",
0220 ddata->status,
0221 phy_mdm6600_status_name[ddata->status]);
0222 complete(&ddata->ack);
0223 }
0224
0225 static irqreturn_t phy_mdm6600_irq_thread(int irq, void *data)
0226 {
0227 struct phy_mdm6600 *ddata = data;
0228
0229 schedule_delayed_work(&ddata->status_work, msecs_to_jiffies(10));
0230
0231 return IRQ_HANDLED;
0232 }
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244 static irqreturn_t phy_mdm6600_wakeirq_thread(int irq, void *data)
0245 {
0246 struct phy_mdm6600 *ddata = data;
0247 struct gpio_desc *mode_gpio1;
0248 int error, wakeup;
0249
0250 mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1];
0251 wakeup = gpiod_get_value(mode_gpio1);
0252 if (!wakeup)
0253 return IRQ_NONE;
0254
0255 dev_dbg(ddata->dev, "OOB wake on mode_gpio1: %i\n", wakeup);
0256 error = pm_runtime_get_sync(ddata->dev);
0257 if (error < 0) {
0258 pm_runtime_put_noidle(ddata->dev);
0259
0260 return IRQ_NONE;
0261 }
0262
0263
0264 pm_runtime_mark_last_busy(ddata->dev);
0265 pm_runtime_put_autosuspend(ddata->dev);
0266
0267 return IRQ_HANDLED;
0268 }
0269
0270
0271
0272
0273
0274 static void phy_mdm6600_init_irq(struct phy_mdm6600 *ddata)
0275 {
0276 struct device *dev = ddata->dev;
0277 int i, error, irq;
0278
0279 for (i = PHY_MDM6600_STATUS0;
0280 i <= PHY_MDM6600_STATUS2; i++) {
0281 struct gpio_desc *gpio = ddata->status_gpios->desc[i];
0282
0283 irq = gpiod_to_irq(gpio);
0284 if (irq <= 0)
0285 continue;
0286
0287 error = devm_request_threaded_irq(dev, irq, NULL,
0288 phy_mdm6600_irq_thread,
0289 IRQF_TRIGGER_RISING |
0290 IRQF_TRIGGER_FALLING |
0291 IRQF_ONESHOT,
0292 "mdm6600",
0293 ddata);
0294 if (error)
0295 dev_warn(dev, "no modem status irq%i: %i\n",
0296 irq, error);
0297 }
0298 }
0299
0300 struct phy_mdm6600_map {
0301 const char *name;
0302 int direction;
0303 };
0304
0305 static const struct phy_mdm6600_map
0306 phy_mdm6600_ctrl_gpio_map[PHY_MDM6600_NR_CTRL_LINES] = {
0307 { "enable", GPIOD_OUT_LOW, },
0308 { "power", GPIOD_OUT_LOW, },
0309 { "reset", GPIOD_OUT_HIGH, },
0310 };
0311
0312
0313
0314
0315
0316 static int phy_mdm6600_init_lines(struct phy_mdm6600 *ddata)
0317 {
0318 struct device *dev = ddata->dev;
0319 int i;
0320
0321
0322 for (i = 0; i < ARRAY_SIZE(phy_mdm6600_ctrl_gpio_map); i++) {
0323 const struct phy_mdm6600_map *map =
0324 &phy_mdm6600_ctrl_gpio_map[i];
0325 struct gpio_desc **gpio = &ddata->ctrl_gpios[i];
0326
0327 *gpio = devm_gpiod_get(dev, map->name, map->direction);
0328 if (IS_ERR(*gpio)) {
0329 dev_info(dev, "gpio %s error %li\n",
0330 map->name, PTR_ERR(*gpio));
0331 return PTR_ERR(*gpio);
0332 }
0333 }
0334
0335
0336 ddata->mode_gpios = devm_gpiod_get_array(dev, "motorola,mode",
0337 GPIOD_OUT_LOW);
0338 if (IS_ERR(ddata->mode_gpios))
0339 return PTR_ERR(ddata->mode_gpios);
0340
0341 if (ddata->mode_gpios->ndescs != PHY_MDM6600_NR_MODE_LINES)
0342 return -EINVAL;
0343
0344
0345 ddata->status_gpios = devm_gpiod_get_array(dev, "motorola,status",
0346 GPIOD_IN);
0347 if (IS_ERR(ddata->status_gpios))
0348 return PTR_ERR(ddata->status_gpios);
0349
0350 if (ddata->status_gpios->ndescs != PHY_MDM6600_NR_STATUS_LINES)
0351 return -EINVAL;
0352
0353
0354 ddata->cmd_gpios = devm_gpiod_get_array(dev, "motorola,cmd",
0355 GPIOD_OUT_LOW);
0356 if (IS_ERR(ddata->cmd_gpios))
0357 return PTR_ERR(ddata->cmd_gpios);
0358
0359 if (ddata->cmd_gpios->ndescs != PHY_MDM6600_NR_CMD_LINES)
0360 return -EINVAL;
0361
0362 return 0;
0363 }
0364
0365
0366
0367
0368
0369
0370
0371
0372
0373
0374
0375 static int phy_mdm6600_device_power_on(struct phy_mdm6600 *ddata)
0376 {
0377 struct gpio_desc *mode_gpio0, *mode_gpio1, *reset_gpio, *power_gpio;
0378 int error = 0, wakeirq;
0379
0380 mode_gpio0 = ddata->mode_gpios->desc[PHY_MDM6600_MODE0];
0381 mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1];
0382 reset_gpio = ddata->ctrl_gpios[PHY_MDM6600_RESET];
0383 power_gpio = ddata->ctrl_gpios[PHY_MDM6600_POWER];
0384
0385
0386
0387
0388
0389
0390
0391 gpiod_set_value_cansleep(mode_gpio0, 0);
0392 gpiod_set_value_cansleep(mode_gpio1, 0);
0393
0394
0395 phy_mdm6600_cmd(ddata, PHY_MDM6600_CMD_NO_BYPASS);
0396
0397
0398 gpiod_set_value_cansleep(reset_gpio, 0);
0399 msleep(100);
0400
0401
0402 gpiod_set_value_cansleep(power_gpio, 1);
0403 msleep(100);
0404 gpiod_set_value_cansleep(power_gpio, 0);
0405
0406
0407
0408
0409
0410
0411
0412 msleep(PHY_MDM6600_PHY_DELAY_MS);
0413 ddata->enabled = true;
0414
0415
0416 dev_info(ddata->dev, "Waiting for power up request to complete..\n");
0417 if (wait_for_completion_timeout(&ddata->ack,
0418 msecs_to_jiffies(PHY_MDM6600_ENABLED_DELAY_MS))) {
0419 if (ddata->status > PHY_MDM6600_STATUS_PANIC &&
0420 ddata->status < PHY_MDM6600_STATUS_SHUTDOWN_ACK)
0421 dev_info(ddata->dev, "Powered up OK\n");
0422 } else {
0423 ddata->enabled = false;
0424 error = -ETIMEDOUT;
0425 dev_err(ddata->dev, "Timed out powering up\n");
0426 }
0427
0428
0429 gpiod_direction_input(mode_gpio1);
0430
0431 wakeirq = gpiod_to_irq(mode_gpio1);
0432 if (wakeirq <= 0)
0433 return wakeirq;
0434
0435 error = devm_request_threaded_irq(ddata->dev, wakeirq, NULL,
0436 phy_mdm6600_wakeirq_thread,
0437 IRQF_TRIGGER_RISING |
0438 IRQF_TRIGGER_FALLING |
0439 IRQF_ONESHOT,
0440 "mdm6600-wake",
0441 ddata);
0442 if (error)
0443 dev_warn(ddata->dev, "no modem wakeirq irq%i: %i\n",
0444 wakeirq, error);
0445
0446 ddata->running = true;
0447
0448 return error;
0449 }
0450
0451
0452
0453
0454
0455 static void phy_mdm6600_device_power_off(struct phy_mdm6600 *ddata)
0456 {
0457 struct gpio_desc *reset_gpio =
0458 ddata->ctrl_gpios[PHY_MDM6600_RESET];
0459
0460 ddata->enabled = false;
0461 phy_mdm6600_cmd(ddata, PHY_MDM6600_CMD_BP_SHUTDOWN_REQ);
0462 msleep(100);
0463
0464 gpiod_set_value_cansleep(reset_gpio, 1);
0465
0466 dev_info(ddata->dev, "Waiting for power down request to complete.. ");
0467 if (wait_for_completion_timeout(&ddata->ack,
0468 msecs_to_jiffies(5000))) {
0469 if (ddata->status == PHY_MDM6600_STATUS_PANIC)
0470 dev_info(ddata->dev, "Powered down OK\n");
0471 } else {
0472 dev_err(ddata->dev, "Timed out powering down\n");
0473 }
0474 }
0475
0476 static void phy_mdm6600_deferred_power_on(struct work_struct *work)
0477 {
0478 struct phy_mdm6600 *ddata;
0479 int error;
0480
0481 ddata = container_of(work, struct phy_mdm6600, bootup_work.work);
0482
0483 error = phy_mdm6600_device_power_on(ddata);
0484 if (error)
0485 dev_err(ddata->dev, "Device not functional\n");
0486 }
0487
0488
0489
0490
0491
0492
0493
0494
0495 static void phy_mdm6600_wake_modem(struct phy_mdm6600 *ddata)
0496 {
0497 struct gpio_desc *mode_gpio0;
0498
0499 mode_gpio0 = ddata->mode_gpios->desc[PHY_MDM6600_MODE0];
0500 gpiod_set_value_cansleep(mode_gpio0, 1);
0501 usleep_range(5, 15);
0502 gpiod_set_value_cansleep(mode_gpio0, 0);
0503 if (ddata->awake)
0504 usleep_range(5, 15);
0505 else
0506 msleep(MDM6600_MODEM_WAKE_DELAY_MS);
0507 }
0508
0509 static void phy_mdm6600_modem_wake(struct work_struct *work)
0510 {
0511 struct phy_mdm6600 *ddata;
0512
0513 ddata = container_of(work, struct phy_mdm6600, modem_wake_work.work);
0514 phy_mdm6600_wake_modem(ddata);
0515
0516
0517
0518
0519
0520
0521 schedule_delayed_work(&ddata->modem_wake_work,
0522 msecs_to_jiffies(PHY_MDM6600_WAKE_KICK_MS));
0523 }
0524
0525 static int __maybe_unused phy_mdm6600_runtime_suspend(struct device *dev)
0526 {
0527 struct phy_mdm6600 *ddata = dev_get_drvdata(dev);
0528
0529 cancel_delayed_work_sync(&ddata->modem_wake_work);
0530 ddata->awake = false;
0531
0532 return 0;
0533 }
0534
0535 static int __maybe_unused phy_mdm6600_runtime_resume(struct device *dev)
0536 {
0537 struct phy_mdm6600 *ddata = dev_get_drvdata(dev);
0538
0539 phy_mdm6600_modem_wake(&ddata->modem_wake_work.work);
0540 ddata->awake = true;
0541
0542 return 0;
0543 }
0544
0545 static const struct dev_pm_ops phy_mdm6600_pm_ops = {
0546 SET_RUNTIME_PM_OPS(phy_mdm6600_runtime_suspend,
0547 phy_mdm6600_runtime_resume, NULL)
0548 };
0549
0550 static const struct of_device_id phy_mdm6600_id_table[] = {
0551 { .compatible = "motorola,mapphone-mdm6600", },
0552 {},
0553 };
0554 MODULE_DEVICE_TABLE(of, phy_mdm6600_id_table);
0555
0556 static int phy_mdm6600_probe(struct platform_device *pdev)
0557 {
0558 struct phy_mdm6600 *ddata;
0559 int error;
0560
0561 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
0562 if (!ddata)
0563 return -ENOMEM;
0564
0565 INIT_DELAYED_WORK(&ddata->bootup_work,
0566 phy_mdm6600_deferred_power_on);
0567 INIT_DELAYED_WORK(&ddata->status_work, phy_mdm6600_status);
0568 INIT_DELAYED_WORK(&ddata->modem_wake_work, phy_mdm6600_modem_wake);
0569 init_completion(&ddata->ack);
0570
0571 ddata->dev = &pdev->dev;
0572 platform_set_drvdata(pdev, ddata);
0573
0574
0575 error = pinctrl_pm_select_sleep_state(ddata->dev);
0576 if (error)
0577 dev_warn(ddata->dev, "%s: error with sleep_state: %i\n",
0578 __func__, error);
0579
0580 error = phy_mdm6600_init_lines(ddata);
0581 if (error)
0582 return error;
0583
0584 phy_mdm6600_init_irq(ddata);
0585 schedule_delayed_work(&ddata->bootup_work, 0);
0586
0587
0588
0589
0590
0591
0592 msleep(PHY_MDM6600_PHY_DELAY_MS + 500);
0593
0594
0595
0596
0597
0598
0599
0600 pm_runtime_use_autosuspend(ddata->dev);
0601 pm_runtime_set_autosuspend_delay(ddata->dev,
0602 MDM6600_MODEM_IDLE_DELAY_MS);
0603 pm_runtime_enable(ddata->dev);
0604 error = pm_runtime_get_sync(ddata->dev);
0605 if (error < 0) {
0606 dev_warn(ddata->dev, "failed to wake modem: %i\n", error);
0607 pm_runtime_put_noidle(ddata->dev);
0608 goto cleanup;
0609 }
0610
0611 ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops);
0612 if (IS_ERR(ddata->generic_phy)) {
0613 error = PTR_ERR(ddata->generic_phy);
0614 goto idle;
0615 }
0616
0617 phy_set_drvdata(ddata->generic_phy, ddata);
0618
0619 ddata->phy_provider =
0620 devm_of_phy_provider_register(ddata->dev,
0621 of_phy_simple_xlate);
0622 if (IS_ERR(ddata->phy_provider))
0623 error = PTR_ERR(ddata->phy_provider);
0624
0625 idle:
0626 pm_runtime_mark_last_busy(ddata->dev);
0627 pm_runtime_put_autosuspend(ddata->dev);
0628
0629 cleanup:
0630 if (error < 0)
0631 phy_mdm6600_device_power_off(ddata);
0632 pm_runtime_disable(ddata->dev);
0633 pm_runtime_dont_use_autosuspend(ddata->dev);
0634 return error;
0635 }
0636
0637 static int phy_mdm6600_remove(struct platform_device *pdev)
0638 {
0639 struct phy_mdm6600 *ddata = platform_get_drvdata(pdev);
0640 struct gpio_desc *reset_gpio = ddata->ctrl_gpios[PHY_MDM6600_RESET];
0641
0642 pm_runtime_dont_use_autosuspend(ddata->dev);
0643 pm_runtime_put_sync(ddata->dev);
0644 pm_runtime_disable(ddata->dev);
0645
0646 if (!ddata->running)
0647 wait_for_completion_timeout(&ddata->ack,
0648 msecs_to_jiffies(PHY_MDM6600_ENABLED_DELAY_MS));
0649
0650 gpiod_set_value_cansleep(reset_gpio, 1);
0651 phy_mdm6600_device_power_off(ddata);
0652
0653 cancel_delayed_work_sync(&ddata->modem_wake_work);
0654 cancel_delayed_work_sync(&ddata->bootup_work);
0655 cancel_delayed_work_sync(&ddata->status_work);
0656
0657 return 0;
0658 }
0659
0660 static struct platform_driver phy_mdm6600_driver = {
0661 .probe = phy_mdm6600_probe,
0662 .remove = phy_mdm6600_remove,
0663 .driver = {
0664 .name = "phy-mapphone-mdm6600",
0665 .pm = &phy_mdm6600_pm_ops,
0666 .of_match_table = of_match_ptr(phy_mdm6600_id_table),
0667 },
0668 };
0669
0670 module_platform_driver(phy_mdm6600_driver);
0671
0672 MODULE_ALIAS("platform:gpio_usb");
0673 MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
0674 MODULE_DESCRIPTION("mdm6600 gpio usb phy driver");
0675 MODULE_LICENSE("GPL v2");