aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan T. Ivanov <ivan.ivanov@linaro.org>2015-09-07 07:45:25 -0400
committerPeter Chen <peter.chen@freescale.com>2015-10-21 21:24:25 -0400
commit3ecb3e09b042e70799ff3a1ff464a5ecaa7547d9 (patch)
treefea2789cc3e67fac569c9a47d7035b1e33a032d2
parentffa2366666f06ce1df3296d106d90e0c2e0cd6b7 (diff)
usb: chipidea: Use extcon framework for VBUS and ID detect
On recent Qualcomm platforms VBUS and ID lines are not routed to USB PHY LINK controller. Use extcon framework to receive connect and disconnect ID and VBUS notification. Signed-off-by: Ivan T. Ivanov <ivan.ivanov@linaro.org> Signed-off-by: Peter Chen <peter.chen@freescale.com>
-rw-r--r--Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt6
-rw-r--r--drivers/usb/chipidea/Kconfig1
-rw-r--r--drivers/usb/chipidea/core.c125
-rw-r--r--drivers/usb/chipidea/otg.c39
-rw-r--r--include/linux/usb/chipidea.h23
5 files changed, 193 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt
index a057b75ba4b5..db48bad84228 100644
--- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt
+++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt
@@ -46,6 +46,11 @@ Optional properties:
46 (4 bytes), This register represents the maximum length of a the burst 46 (4 bytes), This register represents the maximum length of a the burst
47 in 32-bit words while moving data from the USB bus to system memory, 47 in 32-bit words while moving data from the USB bus to system memory,
48 changing this value takes effect only the SBUSCFG.AHBBRST is 0. 48 changing this value takes effect only the SBUSCFG.AHBBRST is 0.
49- extcon: phandles to external connector devices. First phandle should point to
50 external connector, which provide "USB" cable events, the second should point
51 to external connector device, which provide "USB-HOST" cable events. If one
52 of the external connector devices is not required, empty <0> phandle should
53 be specified.
49 54
50Example: 55Example:
51 56
@@ -62,4 +67,5 @@ Example:
62 ahb-burst-config = <0x0>; 67 ahb-burst-config = <0x0>;
63 tx-burst-size-dword = <0x10>; /* 64 bytes */ 68 tx-burst-size-dword = <0x10>; /* 64 bytes */
64 rx-burst-size-dword = <0x10>; 69 rx-burst-size-dword = <0x10>;
70 extcon = <0>, <&usb_id>;
65 }; 71 };
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig
index 5ce3f1d6a6ed..5619b8ca3bf3 100644
--- a/drivers/usb/chipidea/Kconfig
+++ b/drivers/usb/chipidea/Kconfig
@@ -1,6 +1,7 @@
1config USB_CHIPIDEA 1config USB_CHIPIDEA
2 tristate "ChipIdea Highspeed Dual Role Controller" 2 tristate "ChipIdea Highspeed Dual Role Controller"
3 depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA 3 depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA
4 select EXTCON
4 help 5 help
5 Say Y here if your system has a dual role high speed USB 6 Say Y here if your system has a dual role high speed USB
6 controller based on ChipIdea silicon IP. Currently, only the 7 controller based on ChipIdea silicon IP. Currently, only the
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 3feebf7f31f0..573c2876b263 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -47,6 +47,7 @@
47#include <linux/delay.h> 47#include <linux/delay.h>
48#include <linux/device.h> 48#include <linux/device.h>
49#include <linux/dma-mapping.h> 49#include <linux/dma-mapping.h>
50#include <linux/extcon.h>
50#include <linux/phy/phy.h> 51#include <linux/phy/phy.h>
51#include <linux/platform_device.h> 52#include <linux/platform_device.h>
52#include <linux/module.h> 53#include <linux/module.h>
@@ -602,9 +603,45 @@ static irqreturn_t ci_irq(int irq, void *data)
602 return ret; 603 return ret;
603} 604}
604 605
606static int ci_vbus_notifier(struct notifier_block *nb, unsigned long event,
607 void *ptr)
608{
609 struct ci_hdrc_cable *vbus = container_of(nb, struct ci_hdrc_cable, nb);
610 struct ci_hdrc *ci = vbus->ci;
611
612 if (event)
613 vbus->state = true;
614 else
615 vbus->state = false;
616
617 vbus->changed = true;
618
619 ci_irq(ci->irq, ci);
620 return NOTIFY_DONE;
621}
622
623static int ci_id_notifier(struct notifier_block *nb, unsigned long event,
624 void *ptr)
625{
626 struct ci_hdrc_cable *id = container_of(nb, struct ci_hdrc_cable, nb);
627 struct ci_hdrc *ci = id->ci;
628
629 if (event)
630 id->state = false;
631 else
632 id->state = true;
633
634 id->changed = true;
635
636 ci_irq(ci->irq, ci);
637 return NOTIFY_DONE;
638}
639
605static int ci_get_platdata(struct device *dev, 640static int ci_get_platdata(struct device *dev,
606 struct ci_hdrc_platform_data *platdata) 641 struct ci_hdrc_platform_data *platdata)
607{ 642{
643 struct extcon_dev *ext_vbus, *ext_id;
644 struct ci_hdrc_cable *cable;
608 int ret; 645 int ret;
609 646
610 if (!platdata->phy_mode) 647 if (!platdata->phy_mode)
@@ -695,9 +732,91 @@ static int ci_get_platdata(struct device *dev,
695 platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST; 732 platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST;
696 } 733 }
697 734
735 ext_id = ERR_PTR(-ENODEV);
736 ext_vbus = ERR_PTR(-ENODEV);
737 if (of_property_read_bool(dev->of_node, "extcon")) {
738 /* Each one of them is not mandatory */
739 ext_vbus = extcon_get_edev_by_phandle(dev, 0);
740 if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV)
741 return PTR_ERR(ext_vbus);
742
743 ext_id = extcon_get_edev_by_phandle(dev, 1);
744 if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV)
745 return PTR_ERR(ext_id);
746 }
747
748 cable = &platdata->vbus_extcon;
749 cable->nb.notifier_call = ci_vbus_notifier;
750 cable->edev = ext_vbus;
751
752 if (!IS_ERR(ext_vbus)) {
753 ret = extcon_get_cable_state_(cable->edev, EXTCON_USB);
754 if (ret)
755 cable->state = true;
756 else
757 cable->state = false;
758 }
759
760 cable = &platdata->id_extcon;
761 cable->nb.notifier_call = ci_id_notifier;
762 cable->edev = ext_id;
763
764 if (!IS_ERR(ext_id)) {
765 ret = extcon_get_cable_state_(cable->edev, EXTCON_USB_HOST);
766 if (ret)
767 cable->state = false;
768 else
769 cable->state = true;
770 }
698 return 0; 771 return 0;
699} 772}
700 773
774static int ci_extcon_register(struct ci_hdrc *ci)
775{
776 struct ci_hdrc_cable *id, *vbus;
777 int ret;
778
779 id = &ci->platdata->id_extcon;
780 id->ci = ci;
781 if (!IS_ERR(id->edev)) {
782 ret = extcon_register_notifier(id->edev, EXTCON_USB_HOST,
783 &id->nb);
784 if (ret < 0) {
785 dev_err(ci->dev, "register ID failed\n");
786 return ret;
787 }
788 }
789
790 vbus = &ci->platdata->vbus_extcon;
791 vbus->ci = ci;
792 if (!IS_ERR(vbus->edev)) {
793 ret = extcon_register_notifier(vbus->edev, EXTCON_USB,
794 &vbus->nb);
795 if (ret < 0) {
796 extcon_unregister_notifier(id->edev, EXTCON_USB_HOST,
797 &id->nb);
798 dev_err(ci->dev, "register VBUS failed\n");
799 return ret;
800 }
801 }
802
803 return 0;
804}
805
806static void ci_extcon_unregister(struct ci_hdrc *ci)
807{
808 struct ci_hdrc_cable *cable;
809
810 cable = &ci->platdata->id_extcon;
811 if (!IS_ERR(cable->edev))
812 extcon_unregister_notifier(cable->edev, EXTCON_USB_HOST,
813 &cable->nb);
814
815 cable = &ci->platdata->vbus_extcon;
816 if (!IS_ERR(cable->edev))
817 extcon_unregister_notifier(cable->edev, EXTCON_USB, &cable->nb);
818}
819
701static DEFINE_IDA(ci_ida); 820static DEFINE_IDA(ci_ida);
702 821
703struct platform_device *ci_hdrc_add_device(struct device *dev, 822struct platform_device *ci_hdrc_add_device(struct device *dev,
@@ -921,6 +1040,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
921 if (ret) 1040 if (ret)
922 goto stop; 1041 goto stop;
923 1042
1043 ret = ci_extcon_register(ci);
1044 if (ret)
1045 goto stop;
1046
924 if (ci->supports_runtime_pm) { 1047 if (ci->supports_runtime_pm) {
925 pm_runtime_set_active(&pdev->dev); 1048 pm_runtime_set_active(&pdev->dev);
926 pm_runtime_enable(&pdev->dev); 1049 pm_runtime_enable(&pdev->dev);
@@ -938,6 +1061,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
938 if (!ret) 1061 if (!ret)
939 return 0; 1062 return 0;
940 1063
1064 ci_extcon_unregister(ci);
941stop: 1065stop:
942 ci_role_destroy(ci); 1066 ci_role_destroy(ci);
943deinit_phy: 1067deinit_phy:
@@ -957,6 +1081,7 @@ static int ci_hdrc_remove(struct platform_device *pdev)
957 } 1081 }
958 1082
959 dbg_remove_files(ci); 1083 dbg_remove_files(ci);
1084 ci_extcon_unregister(ci);
960 ci_role_destroy(ci); 1085 ci_role_destroy(ci);
961 ci_hdrc_enter_lpm(ci, true); 1086 ci_hdrc_enter_lpm(ci, true);
962 ci_usb_phy_exit(ci); 1087 ci_usb_phy_exit(ci);
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index ad6c87a4653c..ab4bd0c2d4ef 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -30,7 +30,44 @@
30 */ 30 */
31u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask) 31u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
32{ 32{
33 return hw_read(ci, OP_OTGSC, mask); 33 struct ci_hdrc_cable *cable;
34 u32 val = hw_read(ci, OP_OTGSC, mask);
35
36 /*
37 * If using extcon framework for VBUS and/or ID signal
38 * detection overwrite OTGSC register value
39 */
40 cable = &ci->platdata->vbus_extcon;
41 if (!IS_ERR(cable->edev)) {
42 if (cable->changed)
43 val |= OTGSC_BSVIS;
44 else
45 val &= ~OTGSC_BSVIS;
46
47 cable->changed = false;
48
49 if (cable->state)
50 val |= OTGSC_BSV;
51 else
52 val &= ~OTGSC_BSV;
53 }
54
55 cable = &ci->platdata->id_extcon;
56 if (!IS_ERR(cable->edev)) {
57 if (cable->changed)
58 val |= OTGSC_IDIS;
59 else
60 val &= ~OTGSC_IDIS;
61
62 cable->changed = false;
63
64 if (cable->state)
65 val |= OTGSC_ID;
66 else
67 val &= ~OTGSC_ID;
68 }
69
70 return val;
34} 71}
35 72
36/** 73/**
diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h
index a41833cd184c..c5cddc6901d0 100644
--- a/include/linux/usb/chipidea.h
+++ b/include/linux/usb/chipidea.h
@@ -5,9 +5,28 @@
5#ifndef __LINUX_USB_CHIPIDEA_H 5#ifndef __LINUX_USB_CHIPIDEA_H
6#define __LINUX_USB_CHIPIDEA_H 6#define __LINUX_USB_CHIPIDEA_H
7 7
8#include <linux/extcon.h>
8#include <linux/usb/otg.h> 9#include <linux/usb/otg.h>
9 10
10struct ci_hdrc; 11struct ci_hdrc;
12
13/**
14 * struct ci_hdrc_cable - structure for external connector cable state tracking
15 * @state: current state of the line
16 * @changed: set to true when extcon event happen
17 * @edev: device which generate events
18 * @ci: driver state of the chipidea device
19 * @nb: hold event notification callback
20 * @conn: used for notification registration
21 */
22struct ci_hdrc_cable {
23 bool state;
24 bool changed;
25 struct extcon_dev *edev;
26 struct ci_hdrc *ci;
27 struct notifier_block nb;
28};
29
11struct ci_hdrc_platform_data { 30struct ci_hdrc_platform_data {
12 const char *name; 31 const char *name;
13 /* offset of the capability registers */ 32 /* offset of the capability registers */
@@ -48,6 +67,10 @@ struct ci_hdrc_platform_data {
48 u32 ahb_burst_config; 67 u32 ahb_burst_config;
49 u32 tx_burst_size; 68 u32 tx_burst_size;
50 u32 rx_burst_size; 69 u32 rx_burst_size;
70
71 /* VBUS and ID signal state tracking, using extcon framework */
72 struct ci_hdrc_cable vbus_extcon;
73 struct ci_hdrc_cable id_extcon;
51}; 74};
52 75
53/* Default offset of capability registers */ 76/* Default offset of capability registers */