Lines Matching +full:need +full:- +full:phy +full:- +full:for +full:- +full:wake

1 // SPDX-License-Identifier: GPL-2.0
3 * Motorola Mapphone MDM6600 modem GPIO controlled USB PHY driver
18 #include <linux/phy/phy.h>
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 */
28 PHY_MDM6600_ENABLE, /* USB PHY enable */
35 PHY_MDM6600_MODE0, /* out USB mode0 and OOB wake */
36 PHY_MDM6600_MODE1, /* out USB mode1, in OOB wake */
60 PHY_MDM6600_CMD_DATA_ONLY_BYPASS, /* Reroute USB to CPCAP PHY */
61 PHY_MDM6600_CMD_FULL_BYPASS, /* Reroute USB to CPCAP PHY */
92 struct phy *generic_phy;
102 bool enabled; /* mdm6600 phy enabled */
108 static int phy_mdm6600_init(struct phy *x) in phy_mdm6600_init()
111 struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE]; in phy_mdm6600_init()
113 if (!ddata->enabled) in phy_mdm6600_init()
114 return -EPROBE_DEFER; in phy_mdm6600_init()
121 static int phy_mdm6600_power_on(struct phy *x) in phy_mdm6600_power_on()
124 struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE]; in phy_mdm6600_power_on()
126 if (!ddata->enabled) in phy_mdm6600_power_on()
127 return -ENODEV; in phy_mdm6600_power_on()
131 /* Allow aggressive PM for USB, it's only needed for n_gsm port */ in phy_mdm6600_power_on()
132 if (pm_runtime_enabled(&x->dev)) in phy_mdm6600_power_on()
138 static int phy_mdm6600_power_off(struct phy *x) in phy_mdm6600_power_off()
141 struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE]; in phy_mdm6600_power_off()
144 if (!ddata->enabled) in phy_mdm6600_power_off()
145 return -ENODEV; in phy_mdm6600_power_off()
148 if (pm_runtime_enabled(&x->dev)) { in phy_mdm6600_power_off()
150 if (error < 0 && error != -EINPROGRESS) in phy_mdm6600_power_off()
151 dev_warn(ddata->dev, "%s: phy_pm_runtime_get: %i\n", in phy_mdm6600_power_off()
168 * phy_mdm6600_cmd() - send a command request to mdm6600
180 gpiod_multi_set_value_cansleep(ddata->cmd_gpios, values); in phy_mdm6600_cmd()
184 * phy_mdm6600_status() - read mdm6600 status lines
195 dev = ddata->dev; in phy_mdm6600_status()
198 ddata->status_gpios->desc, in phy_mdm6600_status()
199 ddata->status_gpios->info, in phy_mdm6600_status()
204 ddata->status = values[0] & ((1 << PHY_MDM6600_NR_STATUS_LINES) - 1); in phy_mdm6600_status()
207 ddata->status, in phy_mdm6600_status()
208 phy_mdm6600_status_name[ddata->status]); in phy_mdm6600_status()
209 complete(&ddata->ack); in phy_mdm6600_status()
216 schedule_delayed_work(&ddata->status_work, msecs_to_jiffies(10)); in phy_mdm6600_irq_thread()
222 * phy_mdm6600_wakeirq_thread - handle mode1 line OOB wake after booting
227 * mode for mdm6600. After booting it is used as input for OOB wake
228 * signal from mdm6600 to the SoC. Just use it for debug info only
229 * for now.
237 mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1]; in phy_mdm6600_wakeirq_thread()
242 dev_dbg(ddata->dev, "OOB wake on mode_gpio1: %i\n", wakeup); in phy_mdm6600_wakeirq_thread()
243 error = pm_runtime_get_sync(ddata->dev); in phy_mdm6600_wakeirq_thread()
245 pm_runtime_put_noidle(ddata->dev); in phy_mdm6600_wakeirq_thread()
250 /* Just wake-up and kick the autosuspend timer */ in phy_mdm6600_wakeirq_thread()
251 pm_runtime_mark_last_busy(ddata->dev); in phy_mdm6600_wakeirq_thread()
252 pm_runtime_put_autosuspend(ddata->dev); in phy_mdm6600_wakeirq_thread()
258 * phy_mdm6600_init_irq() - initialize mdm6600 status IRQ lines
263 struct device *dev = ddata->dev; in phy_mdm6600_init_irq()
266 for (i = PHY_MDM6600_STATUS0; in phy_mdm6600_init_irq()
268 struct gpio_desc *gpio = ddata->status_gpios->desc[i]; in phy_mdm6600_init_irq()
294 { "enable", GPIOD_OUT_LOW, }, /* low = phy disabled */
300 * phy_mdm6600_init_lines() - initialize mdm6600 GPIO lines
305 struct device *dev = ddata->dev; in phy_mdm6600_init_lines()
309 for (i = 0; i < ARRAY_SIZE(phy_mdm6600_ctrl_gpio_map); i++) { in phy_mdm6600_init_lines()
312 struct gpio_desc **gpio = &ddata->ctrl_gpios[i]; in phy_mdm6600_init_lines()
314 *gpio = devm_gpiod_get(dev, map->name, map->direction); in phy_mdm6600_init_lines()
317 map->name, PTR_ERR(*gpio)); in phy_mdm6600_init_lines()
322 /* MDM6600 USB start-up mode output lines */ in phy_mdm6600_init_lines()
323 ddata->mode_gpios = devm_gpiod_get_array(dev, "motorola,mode", in phy_mdm6600_init_lines()
325 if (IS_ERR(ddata->mode_gpios)) in phy_mdm6600_init_lines()
326 return PTR_ERR(ddata->mode_gpios); in phy_mdm6600_init_lines()
328 if (ddata->mode_gpios->ndescs != PHY_MDM6600_NR_MODE_LINES) in phy_mdm6600_init_lines()
329 return -EINVAL; in phy_mdm6600_init_lines()
332 ddata->status_gpios = devm_gpiod_get_array(dev, "motorola,status", in phy_mdm6600_init_lines()
334 if (IS_ERR(ddata->status_gpios)) in phy_mdm6600_init_lines()
335 return PTR_ERR(ddata->status_gpios); in phy_mdm6600_init_lines()
337 if (ddata->status_gpios->ndescs != PHY_MDM6600_NR_STATUS_LINES) in phy_mdm6600_init_lines()
338 return -EINVAL; in phy_mdm6600_init_lines()
341 ddata->cmd_gpios = devm_gpiod_get_array(dev, "motorola,cmd", in phy_mdm6600_init_lines()
343 if (IS_ERR(ddata->cmd_gpios)) in phy_mdm6600_init_lines()
344 return PTR_ERR(ddata->cmd_gpios); in phy_mdm6600_init_lines()
346 if (ddata->cmd_gpios->ndescs != PHY_MDM6600_NR_CMD_LINES) in phy_mdm6600_init_lines()
347 return -EINVAL; in phy_mdm6600_init_lines()
353 * phy_mdm6600_device_power_on() - power on mdm6600 device
356 * To get the integrated USB phy in MDM6600 takes some hoops. We must ensure
357 * the shared USB bootmode GPIOs are configured, then request modem start-up,
358 * reset and power-up.. And then we need to recycle the shared USB bootmode
359 * GPIOs as they are also used for Out of Band (OOB) wake for the USB and
367 mode_gpio0 = ddata->mode_gpios->desc[PHY_MDM6600_MODE0]; in phy_mdm6600_device_power_on()
368 mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1]; in phy_mdm6600_device_power_on()
369 reset_gpio = ddata->ctrl_gpios[PHY_MDM6600_RESET]; in phy_mdm6600_device_power_on()
370 power_gpio = ddata->ctrl_gpios[PHY_MDM6600_POWER]; in phy_mdm6600_device_power_on()
373 * Shared GPIOs must be low for normal USB mode. After booting in phy_mdm6600_device_power_on()
374 * they are used for OOB wake signaling. These can be also used in phy_mdm6600_device_power_on()
381 /* Request start-up mode */ in phy_mdm6600_device_power_on()
394 * Looks like the USB PHY needs between 2.2 to 4 seconds. in phy_mdm6600_device_power_on()
396 * from omap-usb-host trying to access the PHY. See also in phy_mdm6600_device_power_on()
397 * phy_mdm6600_init() for -EPROBE_DEFER. in phy_mdm6600_device_power_on()
400 ddata->enabled = true; in phy_mdm6600_device_power_on()
403 dev_info(ddata->dev, "Waiting for power up request to complete..\n"); in phy_mdm6600_device_power_on()
404 if (wait_for_completion_timeout(&ddata->ack, in phy_mdm6600_device_power_on()
406 if (ddata->status > PHY_MDM6600_STATUS_PANIC && in phy_mdm6600_device_power_on()
407 ddata->status < PHY_MDM6600_STATUS_SHUTDOWN_ACK) in phy_mdm6600_device_power_on()
408 dev_info(ddata->dev, "Powered up OK\n"); in phy_mdm6600_device_power_on()
410 ddata->enabled = false; in phy_mdm6600_device_power_on()
411 error = -ETIMEDOUT; in phy_mdm6600_device_power_on()
412 dev_err(ddata->dev, "Timed out powering up\n"); in phy_mdm6600_device_power_on()
415 /* Reconfigure mode1 GPIO as input for OOB wake */ in phy_mdm6600_device_power_on()
422 error = devm_request_threaded_irq(ddata->dev, wakeirq, NULL, in phy_mdm6600_device_power_on()
427 "mdm6600-wake", in phy_mdm6600_device_power_on()
430 dev_warn(ddata->dev, "no modem wakeirq irq%i: %i\n", in phy_mdm6600_device_power_on()
433 ddata->running = true; in phy_mdm6600_device_power_on()
439 * phy_mdm6600_device_power_off() - power off mdm6600 device
445 ddata->ctrl_gpios[PHY_MDM6600_RESET]; in phy_mdm6600_device_power_off()
448 ddata->enabled = false; in phy_mdm6600_device_power_off()
454 dev_info(ddata->dev, "Waiting for power down request to complete.. "); in phy_mdm6600_device_power_off()
455 if (wait_for_completion_timeout(&ddata->ack, in phy_mdm6600_device_power_off()
457 if (ddata->status == PHY_MDM6600_STATUS_PANIC) in phy_mdm6600_device_power_off()
458 dev_info(ddata->dev, "Powered down OK\n"); in phy_mdm6600_device_power_off()
460 dev_err(ddata->dev, "Timed out powering down\n"); in phy_mdm6600_device_power_off()
464 * Keep reset gpio high with padconf internal pull-up resistor to in phy_mdm6600_device_power_off()
466 * gpio bank lines can have glitches if not in the always-on wkup in phy_mdm6600_device_power_off()
469 error = pinctrl_pm_select_sleep_state(ddata->dev); in phy_mdm6600_device_power_off()
471 dev_warn(ddata->dev, "%s: error with sleep_state: %i\n", in phy_mdm6600_device_power_off()
484 dev_err(ddata->dev, "Device not functional\n"); in phy_mdm6600_deferred_power_on()
488 * USB suspend puts mdm6600 into low power mode. For any n_gsm using apps,
489 * we need to keep the modem awake by kicking it's mode0 GPIO. This will
490 * keep the modem awake for about 1.2 seconds. When no n_gsm apps are using
498 mode_gpio0 = ddata->mode_gpios->desc[PHY_MDM6600_MODE0]; in phy_mdm6600_wake_modem()
502 if (ddata->awake) in phy_mdm6600_wake_modem()
517 * the wake GPIO, and sometimes it idles after about some 600 ms in phy_mdm6600_modem_wake()
520 schedule_delayed_work(&ddata->modem_wake_work, in phy_mdm6600_modem_wake()
528 cancel_delayed_work_sync(&ddata->modem_wake_work); in phy_mdm6600_runtime_suspend()
529 ddata->awake = false; in phy_mdm6600_runtime_suspend()
538 phy_mdm6600_modem_wake(&ddata->modem_wake_work.work); in phy_mdm6600_runtime_resume()
539 ddata->awake = true; in phy_mdm6600_runtime_resume()
550 { .compatible = "motorola,mapphone-mdm6600", },
560 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); in phy_mdm6600_probe()
562 return -ENOMEM; in phy_mdm6600_probe()
564 INIT_DELAYED_WORK(&ddata->bootup_work, in phy_mdm6600_probe()
566 INIT_DELAYED_WORK(&ddata->status_work, phy_mdm6600_status); in phy_mdm6600_probe()
567 INIT_DELAYED_WORK(&ddata->modem_wake_work, phy_mdm6600_modem_wake); in phy_mdm6600_probe()
568 init_completion(&ddata->ack); in phy_mdm6600_probe()
570 ddata->dev = &pdev->dev; in phy_mdm6600_probe()
578 schedule_delayed_work(&ddata->bootup_work, 0); in phy_mdm6600_probe()
582 * to remove this eventually when ohci-platform can deal in phy_mdm6600_probe()
583 * with -EPROBE_DEFER. in phy_mdm6600_probe()
588 * Enable PM runtime only after PHY has been powered up properly. in phy_mdm6600_probe()
591 * gpio mode0 pin doubles as mdm6600 wake-up gpio. in phy_mdm6600_probe()
593 pm_runtime_use_autosuspend(ddata->dev); in phy_mdm6600_probe()
594 pm_runtime_set_autosuspend_delay(ddata->dev, in phy_mdm6600_probe()
596 pm_runtime_enable(ddata->dev); in phy_mdm6600_probe()
597 error = pm_runtime_get_sync(ddata->dev); in phy_mdm6600_probe()
599 dev_warn(ddata->dev, "failed to wake modem: %i\n", error); in phy_mdm6600_probe()
600 pm_runtime_put_noidle(ddata->dev); in phy_mdm6600_probe()
604 ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops); in phy_mdm6600_probe()
605 if (IS_ERR(ddata->generic_phy)) { in phy_mdm6600_probe()
606 error = PTR_ERR(ddata->generic_phy); in phy_mdm6600_probe()
610 phy_set_drvdata(ddata->generic_phy, ddata); in phy_mdm6600_probe()
612 ddata->phy_provider = in phy_mdm6600_probe()
613 devm_of_phy_provider_register(ddata->dev, in phy_mdm6600_probe()
615 if (IS_ERR(ddata->phy_provider)) in phy_mdm6600_probe()
616 error = PTR_ERR(ddata->phy_provider); in phy_mdm6600_probe()
619 pm_runtime_mark_last_busy(ddata->dev); in phy_mdm6600_probe()
620 pm_runtime_put_autosuspend(ddata->dev); in phy_mdm6600_probe()
625 pm_runtime_disable(ddata->dev); in phy_mdm6600_probe()
626 pm_runtime_dont_use_autosuspend(ddata->dev); in phy_mdm6600_probe()
635 struct gpio_desc *reset_gpio = ddata->ctrl_gpios[PHY_MDM6600_RESET]; in phy_mdm6600_remove()
637 pm_runtime_get_noresume(ddata->dev); in phy_mdm6600_remove()
638 pm_runtime_dont_use_autosuspend(ddata->dev); in phy_mdm6600_remove()
639 pm_runtime_put_sync(ddata->dev); in phy_mdm6600_remove()
640 pm_runtime_disable(ddata->dev); in phy_mdm6600_remove()
642 if (!ddata->running) in phy_mdm6600_remove()
643 wait_for_completion_timeout(&ddata->ack, in phy_mdm6600_remove()
649 cancel_delayed_work_sync(&ddata->modem_wake_work); in phy_mdm6600_remove()
650 cancel_delayed_work_sync(&ddata->bootup_work); in phy_mdm6600_remove()
651 cancel_delayed_work_sync(&ddata->status_work); in phy_mdm6600_remove()
658 .name = "phy-mapphone-mdm6600",
668 MODULE_DESCRIPTION("mdm6600 gpio usb phy driver");