linux/drivers/phy/motorola/phy-mapphone-mdm6600.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Motorola Mapphone MDM6600 modem GPIO controlled USB PHY driver
   4 * Copyright (C) 2018 Tony Lindgren <tony@atomide.com>
   5 */
   6
   7#include <linux/delay.h>
   8#include <linux/err.h>
   9#include <linux/io.h>
  10#include <linux/interrupt.h>
  11#include <linux/module.h>
  12#include <linux/of.h>
  13#include <linux/platform_device.h>
  14#include <linux/slab.h>
  15
  16#include <linux/gpio/consumer.h>
  17#include <linux/of_platform.h>
  18#include <linux/phy/phy.h>
  19#include <linux/pinctrl/consumer.h>
  20
  21#define PHY_MDM6600_PHY_DELAY_MS        4000    /* PHY enable 2.2s to 3.5s */
  22#define PHY_MDM6600_ENABLED_DELAY_MS    8000    /* 8s more total for MDM6600 */
  23#define MDM6600_MODEM_IDLE_DELAY_MS     1000    /* modem after USB suspend */
  24#define MDM6600_MODEM_WAKE_DELAY_MS     200     /* modem response after idle */
  25
  26enum phy_mdm6600_ctrl_lines {
  27        PHY_MDM6600_ENABLE,                     /* USB PHY enable */
  28        PHY_MDM6600_POWER,                      /* Device power */
  29        PHY_MDM6600_RESET,                      /* Device reset */
  30        PHY_MDM6600_NR_CTRL_LINES,
  31};
  32
  33enum phy_mdm6600_bootmode_lines {
  34        PHY_MDM6600_MODE0,                      /* out USB mode0 and OOB wake */
  35        PHY_MDM6600_MODE1,                      /* out USB mode1, in OOB wake */
  36        PHY_MDM6600_NR_MODE_LINES,
  37};
  38
  39enum phy_mdm6600_cmd_lines {
  40        PHY_MDM6600_CMD0,
  41        PHY_MDM6600_CMD1,
  42        PHY_MDM6600_CMD2,
  43        PHY_MDM6600_NR_CMD_LINES,
  44};
  45
  46enum phy_mdm6600_status_lines {
  47        PHY_MDM6600_STATUS0,
  48        PHY_MDM6600_STATUS1,
  49        PHY_MDM6600_STATUS2,
  50        PHY_MDM6600_NR_STATUS_LINES,
  51};
  52
  53/*
  54 * MDM6600 command codes. These are based on Motorola Mapphone Linux
  55 * kernel tree.
  56 */
  57enum phy_mdm6600_cmd {
  58        PHY_MDM6600_CMD_BP_PANIC_ACK,
  59        PHY_MDM6600_CMD_DATA_ONLY_BYPASS,       /* Reroute USB to CPCAP PHY */
  60        PHY_MDM6600_CMD_FULL_BYPASS,            /* Reroute USB to CPCAP PHY */
  61        PHY_MDM6600_CMD_NO_BYPASS,              /* Request normal USB mode */
  62        PHY_MDM6600_CMD_BP_SHUTDOWN_REQ,        /* Request device power off */
  63        PHY_MDM6600_CMD_BP_UNKNOWN_5,
  64        PHY_MDM6600_CMD_BP_UNKNOWN_6,
  65        PHY_MDM6600_CMD_UNDEFINED,
  66};
  67
  68/*
  69 * MDM6600 status codes. These are based on Motorola Mapphone Linux
  70 * kernel tree.
  71 */
  72enum phy_mdm6600_status {
  73        PHY_MDM6600_STATUS_PANIC,               /* Seems to be really off */
  74        PHY_MDM6600_STATUS_PANIC_BUSY_WAIT,
  75        PHY_MDM6600_STATUS_QC_DLOAD,
  76        PHY_MDM6600_STATUS_RAM_DOWNLOADER,      /* MDM6600 USB flashing mode */
  77        PHY_MDM6600_STATUS_PHONE_CODE_AWAKE,    /* MDM6600 normal USB mode */
  78        PHY_MDM6600_STATUS_PHONE_CODE_ASLEEP,
  79        PHY_MDM6600_STATUS_SHUTDOWN_ACK,
  80        PHY_MDM6600_STATUS_UNDEFINED,
  81};
  82
  83static const char * const
  84phy_mdm6600_status_name[] = {
  85        "off", "busy", "qc_dl", "ram_dl", "awake",
  86        "asleep", "shutdown", "undefined",
  87};
  88
  89struct phy_mdm6600 {
  90        struct device *dev;
  91        struct phy *generic_phy;
  92        struct phy_provider *phy_provider;
  93        struct gpio_desc *ctrl_gpios[PHY_MDM6600_NR_CTRL_LINES];
  94        struct gpio_descs *mode_gpios;
  95        struct gpio_descs *status_gpios;
  96        struct gpio_descs *cmd_gpios;
  97        struct delayed_work bootup_work;
  98        struct delayed_work status_work;
  99        struct delayed_work modem_wake_work;
 100        struct completion ack;
 101        bool enabled;                           /* mdm6600 phy enabled */
 102        bool running;                           /* mdm6600 boot done */
 103        bool awake;                             /* mdm6600 respnds on n_gsm */
 104        int status;
 105};
 106
 107static int phy_mdm6600_init(struct phy *x)
 108{
 109        struct phy_mdm6600 *ddata = phy_get_drvdata(x);
 110        struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
 111
 112        if (!ddata->enabled)
 113                return -EPROBE_DEFER;
 114
 115        gpiod_set_value_cansleep(enable_gpio, 0);
 116
 117        return 0;
 118}
 119
 120static int phy_mdm6600_power_on(struct phy *x)
 121{
 122        struct phy_mdm6600 *ddata = phy_get_drvdata(x);
 123        struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
 124        int error;
 125
 126        if (!ddata->enabled)
 127                return -ENODEV;
 128
 129        error = pinctrl_pm_select_default_state(ddata->dev);
 130        if (error)
 131                dev_warn(ddata->dev, "%s: error with default_state: %i\n",
 132                         __func__, error);
 133
 134        gpiod_set_value_cansleep(enable_gpio, 1);
 135
 136        /* Allow aggressive PM for USB, it's only needed for n_gsm port */
 137        if (pm_runtime_enabled(&x->dev))
 138                phy_pm_runtime_put(x);
 139
 140        return 0;
 141}
 142
 143static int phy_mdm6600_power_off(struct phy *x)
 144{
 145        struct phy_mdm6600 *ddata = phy_get_drvdata(x);
 146        struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
 147        int error;
 148
 149        if (!ddata->enabled)
 150                return -ENODEV;
 151
 152        /* Paired with phy_pm_runtime_put() in phy_mdm6600_power_on() */
 153        if (pm_runtime_enabled(&x->dev)) {
 154                error = phy_pm_runtime_get(x);
 155                if (error < 0 && error != -EINPROGRESS)
 156                        dev_warn(ddata->dev, "%s: phy_pm_runtime_get: %i\n",
 157                                 __func__, error);
 158        }
 159
 160        gpiod_set_value_cansleep(enable_gpio, 0);
 161
 162        error = pinctrl_pm_select_sleep_state(ddata->dev);
 163        if (error)
 164                dev_warn(ddata->dev, "%s: error with sleep_state: %i\n",
 165                         __func__, error);
 166
 167        return 0;
 168}
 169
 170static const struct phy_ops gpio_usb_ops = {
 171        .init = phy_mdm6600_init,
 172        .power_on = phy_mdm6600_power_on,
 173        .power_off = phy_mdm6600_power_off,
 174        .owner = THIS_MODULE,
 175};
 176
 177/**
 178 * phy_mdm6600_cmd() - send a command request to mdm6600
 179 * @ddata: device driver data
 180 *
 181 * Configures the three command request GPIOs to the specified value.
 182 */
 183static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
 184{
 185        DECLARE_BITMAP(values, PHY_MDM6600_NR_CMD_LINES);
 186
 187        values[0] = val;
 188
 189        gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
 190                                       ddata->cmd_gpios->desc,
 191                                       ddata->cmd_gpios->info, values);
 192}
 193
 194/**
 195 * phy_mdm6600_status() - read mdm6600 status lines
 196 * @ddata: device driver data
 197 */
 198static void phy_mdm6600_status(struct work_struct *work)
 199{
 200        struct phy_mdm6600 *ddata;
 201        struct device *dev;
 202        DECLARE_BITMAP(values, PHY_MDM6600_NR_STATUS_LINES);
 203        int error, i, val = 0;
 204
 205        ddata = container_of(work, struct phy_mdm6600, status_work.work);
 206        dev = ddata->dev;
 207
 208        error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES,
 209                                               ddata->status_gpios->desc,
 210                                               ddata->status_gpios->info,
 211                                               values);
 212        if (error)
 213                return;
 214
 215        for (i = 0; i < PHY_MDM6600_NR_STATUS_LINES; i++) {
 216                val |= test_bit(i, values) << i;
 217                dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n",
 218                        __func__, i, test_bit(i, values), val);
 219        }
 220        ddata->status = values[0];
 221
 222        dev_info(dev, "modem status: %i %s\n",
 223                 ddata->status,
 224                 phy_mdm6600_status_name[ddata->status & 7]);
 225        complete(&ddata->ack);
 226}
 227
 228static irqreturn_t phy_mdm6600_irq_thread(int irq, void *data)
 229{
 230        struct phy_mdm6600 *ddata = data;
 231
 232        schedule_delayed_work(&ddata->status_work, msecs_to_jiffies(10));
 233
 234        return IRQ_HANDLED;
 235}
 236
 237/**
 238 * phy_mdm6600_wakeirq_thread - handle mode1 line OOB wake after booting
 239 * @irq: interrupt
 240 * @data: interrupt handler data
 241 *
 242 * GPIO mode1 is used initially as output to configure the USB boot
 243 * mode for mdm6600. After booting it is used as input for OOB wake
 244 * signal from mdm6600 to the SoC. Just use it for debug info only
 245 * for now.
 246 */
 247static irqreturn_t phy_mdm6600_wakeirq_thread(int irq, void *data)
 248{
 249        struct phy_mdm6600 *ddata = data;
 250        struct gpio_desc *mode_gpio1;
 251
 252        mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1];
 253        dev_dbg(ddata->dev, "OOB wake on mode_gpio1: %i\n",
 254                gpiod_get_value(mode_gpio1));
 255
 256        return IRQ_HANDLED;
 257}
 258
 259/**
 260 * phy_mdm6600_init_irq() - initialize mdm6600 status IRQ lines
 261 * @ddata: device driver data
 262 */
 263static void phy_mdm6600_init_irq(struct phy_mdm6600 *ddata)
 264{
 265        struct device *dev = ddata->dev;
 266        int i, error, irq;
 267
 268        for (i = PHY_MDM6600_STATUS0;
 269             i <= PHY_MDM6600_STATUS2; i++) {
 270                struct gpio_desc *gpio = ddata->status_gpios->desc[i];
 271
 272                irq = gpiod_to_irq(gpio);
 273                if (irq <= 0)
 274                        continue;
 275
 276                error = devm_request_threaded_irq(dev, irq, NULL,
 277                                        phy_mdm6600_irq_thread,
 278                                        IRQF_TRIGGER_RISING |
 279                                        IRQF_TRIGGER_FALLING |
 280                                        IRQF_ONESHOT,
 281                                        "mdm6600",
 282                                        ddata);
 283                if (error)
 284                        dev_warn(dev, "no modem status irq%i: %i\n",
 285                                 irq, error);
 286        }
 287}
 288
 289struct phy_mdm6600_map {
 290        const char *name;
 291        int direction;
 292};
 293
 294static const struct phy_mdm6600_map
 295phy_mdm6600_ctrl_gpio_map[PHY_MDM6600_NR_CTRL_LINES] = {
 296        { "enable", GPIOD_OUT_LOW, },           /* low = phy disabled */
 297        { "power", GPIOD_OUT_LOW, },            /* low = off */
 298        { "reset", GPIOD_OUT_HIGH, },           /* high = reset */
 299};
 300
 301/**
 302 * phy_mdm6600_init_lines() - initialize mdm6600 GPIO lines
 303 * @ddata: device driver data
 304 */
 305static int phy_mdm6600_init_lines(struct phy_mdm6600 *ddata)
 306{
 307        struct device *dev = ddata->dev;
 308        int i;
 309
 310        /* MDM6600 control lines */
 311        for (i = 0; i < ARRAY_SIZE(phy_mdm6600_ctrl_gpio_map); i++) {
 312                const struct phy_mdm6600_map *map =
 313                        &phy_mdm6600_ctrl_gpio_map[i];
 314                struct gpio_desc **gpio = &ddata->ctrl_gpios[i];
 315
 316                *gpio = devm_gpiod_get(dev, map->name, map->direction);
 317                if (IS_ERR(*gpio)) {
 318                        dev_info(dev, "gpio %s error %li\n",
 319                                 map->name, PTR_ERR(*gpio));
 320                        return PTR_ERR(*gpio);
 321                }
 322        }
 323
 324        /* MDM6600 USB start-up mode output lines */
 325        ddata->mode_gpios = devm_gpiod_get_array(dev, "motorola,mode",
 326                                                 GPIOD_OUT_LOW);
 327        if (IS_ERR(ddata->mode_gpios))
 328                return PTR_ERR(ddata->mode_gpios);
 329
 330        if (ddata->mode_gpios->ndescs != PHY_MDM6600_NR_MODE_LINES)
 331                return -EINVAL;
 332
 333        /* MDM6600 status input lines */
 334        ddata->status_gpios = devm_gpiod_get_array(dev, "motorola,status",
 335                                                   GPIOD_IN);
 336        if (IS_ERR(ddata->status_gpios))
 337                return PTR_ERR(ddata->status_gpios);
 338
 339        if (ddata->status_gpios->ndescs != PHY_MDM6600_NR_STATUS_LINES)
 340                return -EINVAL;
 341
 342        /* MDM6600 cmd output lines */
 343        ddata->cmd_gpios = devm_gpiod_get_array(dev, "motorola,cmd",
 344                                                GPIOD_OUT_LOW);
 345        if (IS_ERR(ddata->cmd_gpios))
 346                return PTR_ERR(ddata->cmd_gpios);
 347
 348        if (ddata->cmd_gpios->ndescs != PHY_MDM6600_NR_CMD_LINES)
 349                return -EINVAL;
 350
 351        return 0;
 352}
 353
 354/**
 355 * phy_mdm6600_device_power_on() - power on mdm6600 device
 356 * @ddata: device driver data
 357 *
 358 * To get the integrated USB phy in MDM6600 takes some hoops. We must ensure
 359 * the shared USB bootmode GPIOs are configured, then request modem start-up,
 360 * reset and power-up.. And then we need to recycle the shared USB bootmode
 361 * GPIOs as they are also used for Out of Band (OOB) wake for the USB and
 362 * TS 27.010 serial mux.
 363 */
 364static int phy_mdm6600_device_power_on(struct phy_mdm6600 *ddata)
 365{
 366        struct gpio_desc *mode_gpio0, *mode_gpio1, *reset_gpio, *power_gpio;
 367        int error = 0, wakeirq;
 368
 369        mode_gpio0 = ddata->mode_gpios->desc[PHY_MDM6600_MODE0];
 370        mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1];
 371        reset_gpio = ddata->ctrl_gpios[PHY_MDM6600_RESET];
 372        power_gpio = ddata->ctrl_gpios[PHY_MDM6600_POWER];
 373
 374        /*
 375         * Shared GPIOs must be low for normal USB mode. After booting
 376         * they are used for OOB wake signaling. These can be also used
 377         * to configure USB flashing mode later on based on a module
 378         * parameter.
 379         */
 380        gpiod_set_value_cansleep(mode_gpio0, 0);
 381        gpiod_set_value_cansleep(mode_gpio1, 0);
 382
 383        /* Request start-up mode */
 384        phy_mdm6600_cmd(ddata, PHY_MDM6600_CMD_NO_BYPASS);
 385
 386        /* Request a reset first */
 387        gpiod_set_value_cansleep(reset_gpio, 0);
 388        msleep(100);
 389
 390        /* Toggle power GPIO to request mdm6600 to start */
 391        gpiod_set_value_cansleep(power_gpio, 1);
 392        msleep(100);
 393        gpiod_set_value_cansleep(power_gpio, 0);
 394
 395        /*
 396         * Looks like the USB PHY needs between 2.2 to 4 seconds.
 397         * If we try to use it before that, we will get L3 errors
 398         * from omap-usb-host trying to access the PHY. See also
 399         * phy_mdm6600_init() for -EPROBE_DEFER.
 400         */
 401        msleep(PHY_MDM6600_PHY_DELAY_MS);
 402        ddata->enabled = true;
 403
 404        /* Booting up the rest of MDM6600 will take total about 8 seconds */
 405        dev_info(ddata->dev, "Waiting for power up request to complete..\n");
 406        if (wait_for_completion_timeout(&ddata->ack,
 407                        msecs_to_jiffies(PHY_MDM6600_ENABLED_DELAY_MS))) {
 408                if (ddata->status > PHY_MDM6600_STATUS_PANIC &&
 409                    ddata->status < PHY_MDM6600_STATUS_SHUTDOWN_ACK)
 410                        dev_info(ddata->dev, "Powered up OK\n");
 411        } else {
 412                ddata->enabled = false;
 413                error = -ETIMEDOUT;
 414                dev_err(ddata->dev, "Timed out powering up\n");
 415        }
 416
 417        /* Reconfigure mode1 GPIO as input for OOB wake */
 418        gpiod_direction_input(mode_gpio1);
 419
 420        wakeirq = gpiod_to_irq(mode_gpio1);
 421        if (wakeirq <= 0)
 422                return wakeirq;
 423
 424        error = devm_request_threaded_irq(ddata->dev, wakeirq, NULL,
 425                                          phy_mdm6600_wakeirq_thread,
 426                                          IRQF_TRIGGER_RISING |
 427                                          IRQF_TRIGGER_FALLING |
 428                                          IRQF_ONESHOT,
 429                                          "mdm6600-wake",
 430                                          ddata);
 431        if (error)
 432                dev_warn(ddata->dev, "no modem wakeirq irq%i: %i\n",
 433                         wakeirq, error);
 434
 435        ddata->running = true;
 436
 437        return error;
 438}
 439
 440/**
 441 * phy_mdm6600_device_power_off() - power off mdm6600 device
 442 * @ddata: device driver data
 443 */
 444static void phy_mdm6600_device_power_off(struct phy_mdm6600 *ddata)
 445{
 446        struct gpio_desc *reset_gpio =
 447                ddata->ctrl_gpios[PHY_MDM6600_RESET];
 448
 449        ddata->enabled = false;
 450        phy_mdm6600_cmd(ddata, PHY_MDM6600_CMD_BP_SHUTDOWN_REQ);
 451        msleep(100);
 452
 453        gpiod_set_value_cansleep(reset_gpio, 1);
 454
 455        dev_info(ddata->dev, "Waiting for power down request to complete.. ");
 456        if (wait_for_completion_timeout(&ddata->ack,
 457                                        msecs_to_jiffies(5000))) {
 458                if (ddata->status == PHY_MDM6600_STATUS_PANIC)
 459                        dev_info(ddata->dev, "Powered down OK\n");
 460        } else {
 461                dev_err(ddata->dev, "Timed out powering down\n");
 462        }
 463}
 464
 465static void phy_mdm6600_deferred_power_on(struct work_struct *work)
 466{
 467        struct phy_mdm6600 *ddata;
 468        int error;
 469
 470        ddata = container_of(work, struct phy_mdm6600, bootup_work.work);
 471
 472        error = phy_mdm6600_device_power_on(ddata);
 473        if (error)
 474                dev_err(ddata->dev, "Device not functional\n");
 475}
 476
 477/*
 478 * USB suspend puts mdm6600 into low power mode. For any n_gsm using apps,
 479 * we need to keep the modem awake by kicking it's mode0 GPIO. This will
 480 * keep the modem awake for about 1.2 seconds. When no n_gsm apps are using
 481 * the modem, runtime PM auto mode can be enabled so modem can enter low
 482 * power mode.
 483 */
 484static void phy_mdm6600_wake_modem(struct phy_mdm6600 *ddata)
 485{
 486        struct gpio_desc *mode_gpio0;
 487
 488        mode_gpio0 = ddata->mode_gpios->desc[PHY_MDM6600_MODE0];
 489        gpiod_set_value_cansleep(mode_gpio0, 1);
 490        usleep_range(5, 15);
 491        gpiod_set_value_cansleep(mode_gpio0, 0);
 492        if (ddata->awake)
 493                usleep_range(5, 15);
 494        else
 495                msleep(MDM6600_MODEM_WAKE_DELAY_MS);
 496}
 497
 498static void phy_mdm6600_modem_wake(struct work_struct *work)
 499{
 500        struct phy_mdm6600 *ddata;
 501
 502        ddata = container_of(work, struct phy_mdm6600, modem_wake_work.work);
 503        phy_mdm6600_wake_modem(ddata);
 504        schedule_delayed_work(&ddata->modem_wake_work,
 505                              msecs_to_jiffies(MDM6600_MODEM_IDLE_DELAY_MS));
 506}
 507
 508static int __maybe_unused phy_mdm6600_runtime_suspend(struct device *dev)
 509{
 510        struct phy_mdm6600 *ddata = dev_get_drvdata(dev);
 511
 512        cancel_delayed_work_sync(&ddata->modem_wake_work);
 513        ddata->awake = false;
 514
 515        return 0;
 516}
 517
 518static int __maybe_unused phy_mdm6600_runtime_resume(struct device *dev)
 519{
 520        struct phy_mdm6600 *ddata = dev_get_drvdata(dev);
 521
 522        phy_mdm6600_modem_wake(&ddata->modem_wake_work.work);
 523        ddata->awake = true;
 524
 525        return 0;
 526}
 527
 528static const struct dev_pm_ops phy_mdm6600_pm_ops = {
 529        SET_RUNTIME_PM_OPS(phy_mdm6600_runtime_suspend,
 530                           phy_mdm6600_runtime_resume, NULL)
 531};
 532
 533static const struct of_device_id phy_mdm6600_id_table[] = {
 534        { .compatible = "motorola,mapphone-mdm6600", },
 535        {},
 536};
 537MODULE_DEVICE_TABLE(of, phy_mdm6600_id_table);
 538
 539static int phy_mdm6600_probe(struct platform_device *pdev)
 540{
 541        struct phy_mdm6600 *ddata;
 542        int error;
 543
 544        ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
 545        if (!ddata)
 546                return -ENOMEM;
 547
 548        INIT_DELAYED_WORK(&ddata->bootup_work,
 549                          phy_mdm6600_deferred_power_on);
 550        INIT_DELAYED_WORK(&ddata->status_work, phy_mdm6600_status);
 551        INIT_DELAYED_WORK(&ddata->modem_wake_work, phy_mdm6600_modem_wake);
 552        init_completion(&ddata->ack);
 553
 554        ddata->dev = &pdev->dev;
 555        platform_set_drvdata(pdev, ddata);
 556
 557        /* Active state selected in phy_mdm6600_power_on() */
 558        error = pinctrl_pm_select_sleep_state(ddata->dev);
 559        if (error)
 560                dev_warn(ddata->dev, "%s: error with sleep_state: %i\n",
 561                         __func__, error);
 562
 563        error = phy_mdm6600_init_lines(ddata);
 564        if (error)
 565                return error;
 566
 567        phy_mdm6600_init_irq(ddata);
 568        schedule_delayed_work(&ddata->bootup_work, 0);
 569
 570        /*
 571         * See phy_mdm6600_device_power_on(). We should be able
 572         * to remove this eventually when ohci-platform can deal
 573         * with -EPROBE_DEFER.
 574         */
 575        msleep(PHY_MDM6600_PHY_DELAY_MS + 500);
 576
 577        /*
 578         * Enable PM runtime only after PHY has been powered up properly.
 579         * It is currently only needed after USB suspends mdm6600 and n_gsm
 580         * needs to access the device. We don't want to do this earlier as
 581         * gpio mode0 pin doubles as mdm6600 wake-up gpio.
 582         */
 583        pm_runtime_use_autosuspend(ddata->dev);
 584        pm_runtime_set_autosuspend_delay(ddata->dev,
 585                                         MDM6600_MODEM_IDLE_DELAY_MS);
 586        pm_runtime_enable(ddata->dev);
 587        error = pm_runtime_get_sync(ddata->dev);
 588        if (error < 0) {
 589                dev_warn(ddata->dev, "failed to wake modem: %i\n", error);
 590                pm_runtime_put_noidle(ddata->dev);
 591                goto cleanup;
 592        }
 593
 594        ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops);
 595        if (IS_ERR(ddata->generic_phy)) {
 596                error = PTR_ERR(ddata->generic_phy);
 597                goto idle;
 598        }
 599
 600        phy_set_drvdata(ddata->generic_phy, ddata);
 601
 602        ddata->phy_provider =
 603                devm_of_phy_provider_register(ddata->dev,
 604                                              of_phy_simple_xlate);
 605        if (IS_ERR(ddata->phy_provider))
 606                error = PTR_ERR(ddata->phy_provider);
 607
 608idle:
 609        pm_runtime_mark_last_busy(ddata->dev);
 610        pm_runtime_put_autosuspend(ddata->dev);
 611
 612cleanup:
 613        if (error < 0)
 614                phy_mdm6600_device_power_off(ddata);
 615
 616        return error;
 617}
 618
 619static int phy_mdm6600_remove(struct platform_device *pdev)
 620{
 621        struct phy_mdm6600 *ddata = platform_get_drvdata(pdev);
 622        struct gpio_desc *reset_gpio = ddata->ctrl_gpios[PHY_MDM6600_RESET];
 623
 624        pm_runtime_dont_use_autosuspend(ddata->dev);
 625        pm_runtime_put_sync(ddata->dev);
 626        pm_runtime_disable(ddata->dev);
 627
 628        if (!ddata->running)
 629                wait_for_completion_timeout(&ddata->ack,
 630                        msecs_to_jiffies(PHY_MDM6600_ENABLED_DELAY_MS));
 631
 632        gpiod_set_value_cansleep(reset_gpio, 1);
 633        phy_mdm6600_device_power_off(ddata);
 634
 635        cancel_delayed_work_sync(&ddata->modem_wake_work);
 636        cancel_delayed_work_sync(&ddata->bootup_work);
 637        cancel_delayed_work_sync(&ddata->status_work);
 638
 639        return 0;
 640}
 641
 642static struct platform_driver phy_mdm6600_driver = {
 643        .probe = phy_mdm6600_probe,
 644        .remove = phy_mdm6600_remove,
 645        .driver = {
 646                .name = "phy-mapphone-mdm6600",
 647                .pm = &phy_mdm6600_pm_ops,
 648                .of_match_table = of_match_ptr(phy_mdm6600_id_table),
 649        },
 650};
 651
 652module_platform_driver(phy_mdm6600_driver);
 653
 654MODULE_ALIAS("platform:gpio_usb");
 655MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
 656MODULE_DESCRIPTION("mdm6600 gpio usb phy driver");
 657MODULE_LICENSE("GPL v2");
 658