cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

phy-mapphone-mdm6600.c (18646B)


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