Commit 6f98f545 authored by Ivan T. Ivanov's avatar Ivan T. Ivanov Committed by Felipe Balbi

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: default avatarIvan T. Ivanov <ivan.ivanov@linaro.org>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent 736d093b
...@@ -52,6 +52,10 @@ Required properties: ...@@ -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
......
...@@ -18,6 +18,7 @@ ...@@ -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 @@ ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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);
......
...@@ -155,6 +155,10 @@ struct msm_usb_cable { ...@@ -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 { ...@@ -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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment