usb: phy: msm: Add D+/D- lines route control

apq8016-sbc board is using Dual SPDT USB Switch (TC7USB40MU),
witch is controlled by GPIO to de/multiplex D+/D- USB lines to
USB2513B Hub and uB connector. Add support for this.

Signed-off-by: Ivan T. Ivanov <ivan.ivanov@linaro.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
Ivan T. Ivanov
2015-07-28 11:10:22 +03:00
committed by Felipe Balbi
parent 736d093b59
commit 6f98f545b0
3 changed files with 58 additions and 0 deletions

View File

@ -52,6 +52,10 @@ Required properties:
Optional properties: Optional properties:
- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg" - dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg"
- switch-gpio: A phandle + gpio-specifier pair. Some boards are using Dual
SPDT USB Switch, witch is cotrolled by GPIO to de/multiplex
D+/D- USB lines between connectors.
- qcom,phy-init-sequence: PHY configuration sequence values. This is related to Device - qcom,phy-init-sequence: PHY configuration sequence values. This is related to Device
Mode Eye Diagram test. Start address at which these values will be Mode Eye Diagram test. Start address at which these values will be
written is ULPI_EXT_VENDOR_SPECIFIC. Value of -1 is reserved as written is ULPI_EXT_VENDOR_SPECIFIC. Value of -1 is reserved as

View File

@ -18,6 +18,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -32,6 +33,7 @@
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/reboot.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/usb.h> #include <linux/usb.h>
@ -1471,6 +1473,14 @@ static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event,
else else
clear_bit(B_SESS_VLD, &motg->inputs); clear_bit(B_SESS_VLD, &motg->inputs);
if (test_bit(B_SESS_VLD, &motg->inputs)) {
/* Switch D+/D- lines to Device connector */
gpiod_set_value_cansleep(motg->switch_gpio, 0);
} else {
/* Switch D+/D- lines to Hub */
gpiod_set_value_cansleep(motg->switch_gpio, 1);
}
schedule_work(&motg->sm_work); schedule_work(&motg->sm_work);
return NOTIFY_DONE; return NOTIFY_DONE;
@ -1546,6 +1556,11 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
motg->manual_pullup = of_property_read_bool(node, "qcom,manual-pullup"); motg->manual_pullup = of_property_read_bool(node, "qcom,manual-pullup");
motg->switch_gpio = devm_gpiod_get_optional(&pdev->dev, "switch",
GPIOD_OUT_LOW);
if (IS_ERR(motg->switch_gpio))
return PTR_ERR(motg->switch_gpio);
ext_id = ERR_PTR(-ENODEV); ext_id = ERR_PTR(-ENODEV);
ext_vbus = ERR_PTR(-ENODEV); ext_vbus = ERR_PTR(-ENODEV);
if (of_property_read_bool(node, "extcon")) { if (of_property_read_bool(node, "extcon")) {
@ -1617,6 +1632,19 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
return 0; return 0;
} }
static int msm_otg_reboot_notify(struct notifier_block *this,
unsigned long code, void *unused)
{
struct msm_otg *motg = container_of(this, struct msm_otg, reboot);
/*
* Ensure that D+/D- lines are routed to uB connector, so
* we could load bootloader/kernel at next reboot
*/
gpiod_set_value_cansleep(motg->switch_gpio, 0);
return NOTIFY_DONE;
}
static int msm_otg_probe(struct platform_device *pdev) static int msm_otg_probe(struct platform_device *pdev)
{ {
struct regulator_bulk_data regs[3]; struct regulator_bulk_data regs[3];
@ -1781,6 +1809,17 @@ static int msm_otg_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "Can not create mode change file\n"); dev_dbg(&pdev->dev, "Can not create mode change file\n");
} }
if (test_bit(B_SESS_VLD, &motg->inputs)) {
/* Switch D+/D- lines to Device connector */
gpiod_set_value_cansleep(motg->switch_gpio, 0);
} else {
/* Switch D+/D- lines to Hub */
gpiod_set_value_cansleep(motg->switch_gpio, 1);
}
motg->reboot.notifier_call = msm_otg_reboot_notify;
register_reboot_notifier(&motg->reboot);
pm_runtime_set_active(&pdev->dev); pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
@ -1807,6 +1846,14 @@ static int msm_otg_remove(struct platform_device *pdev)
if (phy->otg->host || phy->otg->gadget) if (phy->otg->host || phy->otg->gadget)
return -EBUSY; return -EBUSY;
unregister_reboot_notifier(&motg->reboot);
/*
* Ensure that D+/D- lines are routed to uB connector, so
* we could load bootloader/kernel at next reboot
*/
gpiod_set_value_cansleep(motg->switch_gpio, 0);
extcon_unregister_notifier(motg->id.extcon, EXTCON_USB_HOST, &motg->id.nb); extcon_unregister_notifier(motg->id.extcon, EXTCON_USB_HOST, &motg->id.nb);
extcon_unregister_notifier(motg->vbus.extcon, EXTCON_USB, &motg->vbus.nb); extcon_unregister_notifier(motg->vbus.extcon, EXTCON_USB, &motg->vbus.nb);

View File

@ -155,6 +155,10 @@ struct msm_usb_cable {
* starting controller using usbcmd run/stop bit. * starting controller using usbcmd run/stop bit.
* @vbus: VBUS signal state trakining, using extcon framework * @vbus: VBUS signal state trakining, using extcon framework
* @id: ID signal state trakining, using extcon framework * @id: ID signal state trakining, using extcon framework
* @switch_gpio: Descriptor for GPIO used to control external Dual
* SPDT USB Switch.
* @reboot: Used to inform the driver to route USB D+/D- line to Device
* connector
*/ */
struct msm_otg { struct msm_otg {
struct usb_phy phy; struct usb_phy phy;
@ -188,6 +192,9 @@ struct msm_otg {
struct msm_usb_cable vbus; struct msm_usb_cable vbus;
struct msm_usb_cable id; struct msm_usb_cable id;
struct gpio_desc *switch_gpio;
struct notifier_block reboot;
}; };
#endif #endif