Loading Documentation/devicetree/bindings/platform/msm/gpio-usbdetect.txt +4 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,10 @@ Optional Properties: value is 0 or device mode if 1. - qcom,disable-device-mode: If present then don't treat GPIO high as Vbus high. And only notify host mode similar to ID line. -qcom,id-det-gpio: If present, specifies a gpio that is tied to ID, switch to host mode if value is 0. -qcom,dpdm_switch_gpio: If present, specifies a gpio that drives a switch which routes DPDM lines to different ports. Example: Loading arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts +30 −1 Original line number Diff line number Diff line Loading @@ -30,6 +30,19 @@ status = "disabled"; }; &pm8916_gpios { gpio@c200 { qcom,mode = <1>; qcom,output-type = <0>; qcom,src-sel = <0>; qcom,pull = <5>; qcom,master-en = <1>; qcom,vin-sel = <1>; qcom,out-strength = <3>; status = "ok"; }; }; &msm_gpio { hsuart_active: default { mux { Loading Loading @@ -69,6 +82,19 @@ bias-disable; }; }; usb_id_detect: usb_id_detect { mux { pins = "gpio110"; function = "gpio"; }; config { pins = "gpio110"; drive-strength = <2>; bias-pull-up; }; }; }; &soc { Loading Loading @@ -197,9 +223,12 @@ interrupt-parent = <&msm_gpio>; interrupts = <97 0>; interrupt-names = "vbus_det_irq"; pinctrl-names = "usb_vbus_detect"; pinctrl-names = "usb_vbus_detect", "usb_id_detect"; pinctrl-0 = <&usb_vbus_detect>; pinctrl-1 = <&usb_id_detect>; qcom,gpio-mode-sel = <&msm_gpio 97 0>; qcom,id-det-gpio = <&msm_gpio 110 0>; qcom,dpdm_switch_gpio = <&pm8916_gpios 3 0>; }; }; Loading arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts +30 −1 Original line number Diff line number Diff line Loading @@ -30,6 +30,19 @@ status = "disabled"; }; &pm8916_gpios { gpio@c200 { qcom,mode = <1>; qcom,output-type = <0>; qcom,src-sel = <0>; qcom,pull = <5>; qcom,master-en = <1>; qcom,vin-sel = <1>; qcom,out-strength = <3>; status = "ok"; }; }; &msm_gpio { hsuart_active: default { mux { Loading Loading @@ -69,6 +82,19 @@ bias-disable; }; }; usb_id_detect: usb_id_detect { mux { pins = "gpio110"; function = "gpio"; }; config { pins = "gpio110"; drive-strength = <2>; bias-pull-up; }; }; }; &soc { Loading Loading @@ -175,9 +201,12 @@ interrupt-parent = <&msm_gpio>; interrupts = <97 0>; interrupt-names = "vbus_det_irq"; pinctrl-names = "usb_vbus_detect"; pinctrl-names = "usb_vbus_detect", "usb_id_detect"; pinctrl-0 = <&usb_vbus_detect>; pinctrl-1 = <&usb_id_detect>; qcom,gpio-mode-sel = <&msm_gpio 97 0>; qcom,id-det-gpio = <&msm_gpio 110 0>; qcom,dpdm_switch_gpio = <&pm8916_gpios 3 0>; }; }; Loading drivers/platform/msm/gpio-usbdetect.c +89 −4 Original line number Diff line number Diff line /* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -37,8 +37,15 @@ struct gpio_usbdetect { struct regulator *vdd33; struct regulator *vdd12; int gpio_usbdetect; int id; int id_det_gpio; int id_det_irq; bool notify_host_mode; bool disable_device_mode; int dpdm_switch_gpio; }; static int gpio_enable_ldos(struct gpio_usbdetect *usb, int on) Loading Loading @@ -154,15 +161,28 @@ disable_vin: return ret; } static irqreturn_t gpio_usbdetect_vbus_irq(int irq, void *data) static irqreturn_t gpio_usbdetect_irq(int irq, void *data) { struct gpio_usbdetect *usb = data; if (gpio_is_valid(usb->id_det_gpio)) { usb->id = gpio_get_value(usb->id_det_gpio); if (usb->id) { dev_dbg(&usb->pdev->dev, "ID\n"); usb->vbus = gpio_get_value(usb->gpio_usbdetect); goto queue_chg_work; } dev_dbg(&usb->pdev->dev, "!ID\n"); schedule_delayed_work(&usb->chg_work, 0); return IRQ_HANDLED; } if (gpio_is_valid(usb->gpio_usbdetect)) usb->vbus = gpio_get_value(usb->gpio_usbdetect); else usb->vbus = !!irq_read_line(irq); queue_chg_work: pm_wakeup_event(&usb->pdev->dev, WAKEUP_SRC_TIMEOUT_MS); if (!usb->vbus) schedule_delayed_work(&usb->chg_work, 0); Loading @@ -178,6 +198,37 @@ static void gpio_usbdetect_chg_work(struct work_struct *w) struct gpio_usbdetect *usb = container_of(w, struct gpio_usbdetect, chg_work.work); if (gpio_is_valid(usb->id_det_gpio)) { dev_dbg(&usb->pdev->dev, "ID:%d VBUS:%d\n", usb->id, usb->vbus); if (!usb->id) { power_supply_set_supply_type(usb->usb_psy, POWER_SUPPLY_TYPE_UNKNOWN); power_supply_set_present(usb->usb_psy, 0); power_supply_set_usb_otg(usb->usb_psy, 1); if (gpio_is_valid(usb->dpdm_switch_gpio)) gpio_set_value(usb->dpdm_switch_gpio, 1); return; } power_supply_set_usb_otg(usb->usb_psy, 0); if (gpio_is_valid(usb->dpdm_switch_gpio)) gpio_set_value(usb->dpdm_switch_gpio, 0); if (usb->vbus) { power_supply_set_supply_type(usb->usb_psy, POWER_SUPPLY_TYPE_USB); power_supply_set_present(usb->usb_psy, usb->vbus); } else { power_supply_set_supply_type(usb->usb_psy, POWER_SUPPLY_TYPE_UNKNOWN); power_supply_set_present(usb->usb_psy, usb->vbus); } return; } if (usb->vbus) { if (usb->notify_host_mode) power_supply_set_usb_otg(usb->usb_psy, 0); Loading Loading @@ -254,7 +305,7 @@ static int gpio_usbdetect_probe(struct platform_device *pdev) } rc = devm_request_irq(&pdev->dev, usb->vbus_det_irq, gpio_usbdetect_vbus_irq, gpio_usbdetect_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "vbus_det_irq", usb); if (rc) { Loading @@ -263,14 +314,48 @@ static int gpio_usbdetect_probe(struct platform_device *pdev) goto disable_ldo; } usb->id_det_gpio = of_get_named_gpio(pdev->dev.of_node, "qcom,id-det-gpio", 0); if (gpio_is_valid(usb->id_det_gpio)) { rc = devm_gpio_request(&pdev->dev, usb->id_det_gpio, "GPIO_ID_DET"); if (rc) { dev_err(&pdev->dev, "gpio req failed for gpio_%d\n", usb->id_det_gpio); goto disable_ldo; } usb->id_det_irq = gpio_to_irq(usb->id_det_gpio); if (usb->id_det_irq < 0) { dev_err(&pdev->dev, "get id_det_irq failed\n"); goto disable_ldo; } rc = devm_request_irq(&pdev->dev, usb->id_det_irq, gpio_usbdetect_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "id_det_irq", usb); if (rc) { dev_err(&pdev->dev, "request for id_det_irq failed:%d\n", rc); goto disable_ldo; } } usb->dpdm_switch_gpio = of_get_named_gpio(pdev->dev.of_node, "qcom,dpdm_switch_gpio", 0); dev_dbg(&pdev->dev, "is dpdm_switch_gpio valid:%d\n", gpio_is_valid(usb->dpdm_switch_gpio)); device_init_wakeup(&pdev->dev, 1); enable_irq_wake(usb->vbus_det_irq); enable_irq_wake(usb->id_det_irq); dev_set_drvdata(&pdev->dev, usb); /* Read and report initial VBUS state */ local_irq_save(flags); gpio_usbdetect_vbus_irq(usb->vbus_det_irq, usb); gpio_usbdetect_irq(usb->vbus_det_irq, usb); local_irq_restore(flags); return 0; Loading Loading
Documentation/devicetree/bindings/platform/msm/gpio-usbdetect.txt +4 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,10 @@ Optional Properties: value is 0 or device mode if 1. - qcom,disable-device-mode: If present then don't treat GPIO high as Vbus high. And only notify host mode similar to ID line. -qcom,id-det-gpio: If present, specifies a gpio that is tied to ID, switch to host mode if value is 0. -qcom,dpdm_switch_gpio: If present, specifies a gpio that drives a switch which routes DPDM lines to different ports. Example: Loading
arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-excelpoint-refboard.dts +30 −1 Original line number Diff line number Diff line Loading @@ -30,6 +30,19 @@ status = "disabled"; }; &pm8916_gpios { gpio@c200 { qcom,mode = <1>; qcom,output-type = <0>; qcom,src-sel = <0>; qcom,pull = <5>; qcom,master-en = <1>; qcom,vin-sel = <1>; qcom,out-strength = <3>; status = "ok"; }; }; &msm_gpio { hsuart_active: default { mux { Loading Loading @@ -69,6 +82,19 @@ bias-disable; }; }; usb_id_detect: usb_id_detect { mux { pins = "gpio110"; function = "gpio"; }; config { pins = "gpio110"; drive-strength = <2>; bias-pull-up; }; }; }; &soc { Loading Loading @@ -197,9 +223,12 @@ interrupt-parent = <&msm_gpio>; interrupts = <97 0>; interrupt-names = "vbus_det_irq"; pinctrl-names = "usb_vbus_detect"; pinctrl-names = "usb_vbus_detect", "usb_id_detect"; pinctrl-0 = <&usb_vbus_detect>; pinctrl-1 = <&usb_id_detect>; qcom,gpio-mode-sel = <&msm_gpio 97 0>; qcom,id-det-gpio = <&msm_gpio 110 0>; qcom,dpdm_switch_gpio = <&pm8916_gpios 3 0>; }; }; Loading
arch/arm/boot/dts/qcom/apq8009-mtp-wcd9326-refboard.dts +30 −1 Original line number Diff line number Diff line Loading @@ -30,6 +30,19 @@ status = "disabled"; }; &pm8916_gpios { gpio@c200 { qcom,mode = <1>; qcom,output-type = <0>; qcom,src-sel = <0>; qcom,pull = <5>; qcom,master-en = <1>; qcom,vin-sel = <1>; qcom,out-strength = <3>; status = "ok"; }; }; &msm_gpio { hsuart_active: default { mux { Loading Loading @@ -69,6 +82,19 @@ bias-disable; }; }; usb_id_detect: usb_id_detect { mux { pins = "gpio110"; function = "gpio"; }; config { pins = "gpio110"; drive-strength = <2>; bias-pull-up; }; }; }; &soc { Loading Loading @@ -175,9 +201,12 @@ interrupt-parent = <&msm_gpio>; interrupts = <97 0>; interrupt-names = "vbus_det_irq"; pinctrl-names = "usb_vbus_detect"; pinctrl-names = "usb_vbus_detect", "usb_id_detect"; pinctrl-0 = <&usb_vbus_detect>; pinctrl-1 = <&usb_id_detect>; qcom,gpio-mode-sel = <&msm_gpio 97 0>; qcom,id-det-gpio = <&msm_gpio 110 0>; qcom,dpdm_switch_gpio = <&pm8916_gpios 3 0>; }; }; Loading
drivers/platform/msm/gpio-usbdetect.c +89 −4 Original line number Diff line number Diff line /* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -37,8 +37,15 @@ struct gpio_usbdetect { struct regulator *vdd33; struct regulator *vdd12; int gpio_usbdetect; int id; int id_det_gpio; int id_det_irq; bool notify_host_mode; bool disable_device_mode; int dpdm_switch_gpio; }; static int gpio_enable_ldos(struct gpio_usbdetect *usb, int on) Loading Loading @@ -154,15 +161,28 @@ disable_vin: return ret; } static irqreturn_t gpio_usbdetect_vbus_irq(int irq, void *data) static irqreturn_t gpio_usbdetect_irq(int irq, void *data) { struct gpio_usbdetect *usb = data; if (gpio_is_valid(usb->id_det_gpio)) { usb->id = gpio_get_value(usb->id_det_gpio); if (usb->id) { dev_dbg(&usb->pdev->dev, "ID\n"); usb->vbus = gpio_get_value(usb->gpio_usbdetect); goto queue_chg_work; } dev_dbg(&usb->pdev->dev, "!ID\n"); schedule_delayed_work(&usb->chg_work, 0); return IRQ_HANDLED; } if (gpio_is_valid(usb->gpio_usbdetect)) usb->vbus = gpio_get_value(usb->gpio_usbdetect); else usb->vbus = !!irq_read_line(irq); queue_chg_work: pm_wakeup_event(&usb->pdev->dev, WAKEUP_SRC_TIMEOUT_MS); if (!usb->vbus) schedule_delayed_work(&usb->chg_work, 0); Loading @@ -178,6 +198,37 @@ static void gpio_usbdetect_chg_work(struct work_struct *w) struct gpio_usbdetect *usb = container_of(w, struct gpio_usbdetect, chg_work.work); if (gpio_is_valid(usb->id_det_gpio)) { dev_dbg(&usb->pdev->dev, "ID:%d VBUS:%d\n", usb->id, usb->vbus); if (!usb->id) { power_supply_set_supply_type(usb->usb_psy, POWER_SUPPLY_TYPE_UNKNOWN); power_supply_set_present(usb->usb_psy, 0); power_supply_set_usb_otg(usb->usb_psy, 1); if (gpio_is_valid(usb->dpdm_switch_gpio)) gpio_set_value(usb->dpdm_switch_gpio, 1); return; } power_supply_set_usb_otg(usb->usb_psy, 0); if (gpio_is_valid(usb->dpdm_switch_gpio)) gpio_set_value(usb->dpdm_switch_gpio, 0); if (usb->vbus) { power_supply_set_supply_type(usb->usb_psy, POWER_SUPPLY_TYPE_USB); power_supply_set_present(usb->usb_psy, usb->vbus); } else { power_supply_set_supply_type(usb->usb_psy, POWER_SUPPLY_TYPE_UNKNOWN); power_supply_set_present(usb->usb_psy, usb->vbus); } return; } if (usb->vbus) { if (usb->notify_host_mode) power_supply_set_usb_otg(usb->usb_psy, 0); Loading Loading @@ -254,7 +305,7 @@ static int gpio_usbdetect_probe(struct platform_device *pdev) } rc = devm_request_irq(&pdev->dev, usb->vbus_det_irq, gpio_usbdetect_vbus_irq, gpio_usbdetect_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "vbus_det_irq", usb); if (rc) { Loading @@ -263,14 +314,48 @@ static int gpio_usbdetect_probe(struct platform_device *pdev) goto disable_ldo; } usb->id_det_gpio = of_get_named_gpio(pdev->dev.of_node, "qcom,id-det-gpio", 0); if (gpio_is_valid(usb->id_det_gpio)) { rc = devm_gpio_request(&pdev->dev, usb->id_det_gpio, "GPIO_ID_DET"); if (rc) { dev_err(&pdev->dev, "gpio req failed for gpio_%d\n", usb->id_det_gpio); goto disable_ldo; } usb->id_det_irq = gpio_to_irq(usb->id_det_gpio); if (usb->id_det_irq < 0) { dev_err(&pdev->dev, "get id_det_irq failed\n"); goto disable_ldo; } rc = devm_request_irq(&pdev->dev, usb->id_det_irq, gpio_usbdetect_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "id_det_irq", usb); if (rc) { dev_err(&pdev->dev, "request for id_det_irq failed:%d\n", rc); goto disable_ldo; } } usb->dpdm_switch_gpio = of_get_named_gpio(pdev->dev.of_node, "qcom,dpdm_switch_gpio", 0); dev_dbg(&pdev->dev, "is dpdm_switch_gpio valid:%d\n", gpio_is_valid(usb->dpdm_switch_gpio)); device_init_wakeup(&pdev->dev, 1); enable_irq_wake(usb->vbus_det_irq); enable_irq_wake(usb->id_det_irq); dev_set_drvdata(&pdev->dev, usb); /* Read and report initial VBUS state */ local_irq_save(flags); gpio_usbdetect_vbus_irq(usb->vbus_det_irq, usb); gpio_usbdetect_irq(usb->vbus_det_irq, usb); local_irq_restore(flags); return 0; Loading