aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/chipidea
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-10-22 21:24:38 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-10-22 21:24:38 -0400
commit9e43643b11695911bb1dd585d9a9f758a5ad4d89 (patch)
treecb9cdcfea0895229fcaceee6bf51d0aa345f5159 /drivers/usb/chipidea
parenta4d8e93c3182a54d8d21a4d1cec6538ae1be9e16 (diff)
parent851ce932242d5a79bef8fe625fce37cc2f27033e (diff)
Merge tag 'usb-ci-v4.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb into usb-next
Peter writes: USB Chipidea updates for v4.4-rc1 - Use extcon framework for VBUS and ID detect - Add imx6sx and imx7d support - Other small changes
Diffstat (limited to 'drivers/usb/chipidea')
-rw-r--r--drivers/usb/chipidea/Kconfig1
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c11
-rw-r--r--drivers/usb/chipidea/ci_hdrc_pci.c6
-rw-r--r--drivers/usb/chipidea/core.c132
-rw-r--r--drivers/usb/chipidea/otg.c48
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c66
6 files changed, 257 insertions, 7 deletions
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/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index dcc50c878159..6ccbf60cdd5c 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -56,12 +56,23 @@ static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = {
56 CI_HDRC_DISABLE_HOST_STREAMING, 56 CI_HDRC_DISABLE_HOST_STREAMING,
57}; 57};
58 58
59static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = {
60 .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
61 CI_HDRC_TURN_VBUS_EARLY_ON,
62};
63
64static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = {
65 .flags = CI_HDRC_SUPPORTS_RUNTIME_PM,
66};
67
59static const struct of_device_id ci_hdrc_imx_dt_ids[] = { 68static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
60 { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, 69 { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
61 { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, 70 { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
62 { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, 71 { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
63 { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data}, 72 { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
64 { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data}, 73 { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data},
74 { .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data},
75 { .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
65 { /* sentinel */ } 76 { /* sentinel */ }
66}; 77};
67MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); 78MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
diff --git a/drivers/usb/chipidea/ci_hdrc_pci.c b/drivers/usb/chipidea/ci_hdrc_pci.c
index 773d150512fa..b59195edf636 100644
--- a/drivers/usb/chipidea/ci_hdrc_pci.c
+++ b/drivers/usb/chipidea/ci_hdrc_pci.c
@@ -142,16 +142,16 @@ static const struct pci_device_id ci_hdrc_pci_id_table[] = {
142 .driver_data = (kernel_ulong_t)&pci_platdata, 142 .driver_data = (kernel_ulong_t)&pci_platdata,
143 }, 143 },
144 { 144 {
145 PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0811), 145 PCI_VDEVICE(INTEL, 0x0811),
146 .driver_data = (kernel_ulong_t)&langwell_pci_platdata, 146 .driver_data = (kernel_ulong_t)&langwell_pci_platdata,
147 }, 147 },
148 { 148 {
149 PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0829), 149 PCI_VDEVICE(INTEL, 0x0829),
150 .driver_data = (kernel_ulong_t)&penwell_pci_platdata, 150 .driver_data = (kernel_ulong_t)&penwell_pci_platdata,
151 }, 151 },
152 { 152 {
153 /* Intel Clovertrail */ 153 /* Intel Clovertrail */
154 PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe006), 154 PCI_VDEVICE(INTEL, 0xe006),
155 .driver_data = (kernel_ulong_t)&penwell_pci_platdata, 155 .driver_data = (kernel_ulong_t)&penwell_pci_platdata,
156 }, 156 },
157 { 0 } /* end: all zeroes */ 157 { 0 } /* end: all zeroes */
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index bf2599757f55..965d0e240dcb 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)
@@ -651,6 +688,10 @@ static int ci_get_platdata(struct device *dev,
651 if (usb_get_maximum_speed(dev) == USB_SPEED_FULL) 688 if (usb_get_maximum_speed(dev) == USB_SPEED_FULL)
652 platdata->flags |= CI_HDRC_FORCE_FULLSPEED; 689 platdata->flags |= CI_HDRC_FORCE_FULLSPEED;
653 690
691 if (of_find_property(dev->of_node, "phy-clkgate-delay-us", NULL))
692 of_property_read_u32(dev->of_node, "phy-clkgate-delay-us",
693 &platdata->phy_clkgate_delay_us);
694
654 platdata->itc_setting = 1; 695 platdata->itc_setting = 1;
655 if (of_find_property(dev->of_node, "itc-setting", NULL)) { 696 if (of_find_property(dev->of_node, "itc-setting", NULL)) {
656 ret = of_property_read_u32(dev->of_node, "itc-setting", 697 ret = of_property_read_u32(dev->of_node, "itc-setting",
@@ -695,9 +736,91 @@ static int ci_get_platdata(struct device *dev,
695 platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST; 736 platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST;
696 } 737 }
697 738
739 ext_id = ERR_PTR(-ENODEV);
740 ext_vbus = ERR_PTR(-ENODEV);
741 if (of_property_read_bool(dev->of_node, "extcon")) {
742 /* Each one of them is not mandatory */
743 ext_vbus = extcon_get_edev_by_phandle(dev, 0);
744 if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV)
745 return PTR_ERR(ext_vbus);
746
747 ext_id = extcon_get_edev_by_phandle(dev, 1);
748 if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV)
749 return PTR_ERR(ext_id);
750 }
751
752 cable = &platdata->vbus_extcon;
753 cable->nb.notifier_call = ci_vbus_notifier;
754 cable->edev = ext_vbus;
755
756 if (!IS_ERR(ext_vbus)) {
757 ret = extcon_get_cable_state_(cable->edev, EXTCON_USB);
758 if (ret)
759 cable->state = true;
760 else
761 cable->state = false;
762 }
763
764 cable = &platdata->id_extcon;
765 cable->nb.notifier_call = ci_id_notifier;
766 cable->edev = ext_id;
767
768 if (!IS_ERR(ext_id)) {
769 ret = extcon_get_cable_state_(cable->edev, EXTCON_USB_HOST);
770 if (ret)
771 cable->state = false;
772 else
773 cable->state = true;
774 }
775 return 0;
776}
777
778static int ci_extcon_register(struct ci_hdrc *ci)
779{
780 struct ci_hdrc_cable *id, *vbus;
781 int ret;
782
783 id = &ci->platdata->id_extcon;
784 id->ci = ci;
785 if (!IS_ERR(id->edev)) {
786 ret = extcon_register_notifier(id->edev, EXTCON_USB_HOST,
787 &id->nb);
788 if (ret < 0) {
789 dev_err(ci->dev, "register ID failed\n");
790 return ret;
791 }
792 }
793
794 vbus = &ci->platdata->vbus_extcon;
795 vbus->ci = ci;
796 if (!IS_ERR(vbus->edev)) {
797 ret = extcon_register_notifier(vbus->edev, EXTCON_USB,
798 &vbus->nb);
799 if (ret < 0) {
800 extcon_unregister_notifier(id->edev, EXTCON_USB_HOST,
801 &id->nb);
802 dev_err(ci->dev, "register VBUS failed\n");
803 return ret;
804 }
805 }
806
698 return 0; 807 return 0;
699} 808}
700 809
810static void ci_extcon_unregister(struct ci_hdrc *ci)
811{
812 struct ci_hdrc_cable *cable;
813
814 cable = &ci->platdata->id_extcon;
815 if (!IS_ERR(cable->edev))
816 extcon_unregister_notifier(cable->edev, EXTCON_USB_HOST,
817 &cable->nb);
818
819 cable = &ci->platdata->vbus_extcon;
820 if (!IS_ERR(cable->edev))
821 extcon_unregister_notifier(cable->edev, EXTCON_USB, &cable->nb);
822}
823
701static DEFINE_IDA(ci_ida); 824static DEFINE_IDA(ci_ida);
702 825
703struct platform_device *ci_hdrc_add_device(struct device *dev, 826struct platform_device *ci_hdrc_add_device(struct device *dev,
@@ -921,6 +1044,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
921 if (ret) 1044 if (ret)
922 goto stop; 1045 goto stop;
923 1046
1047 ret = ci_extcon_register(ci);
1048 if (ret)
1049 goto stop;
1050
924 if (ci->supports_runtime_pm) { 1051 if (ci->supports_runtime_pm) {
925 pm_runtime_set_active(&pdev->dev); 1052 pm_runtime_set_active(&pdev->dev);
926 pm_runtime_enable(&pdev->dev); 1053 pm_runtime_enable(&pdev->dev);
@@ -938,6 +1065,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
938 if (!ret) 1065 if (!ret)
939 return 0; 1066 return 0;
940 1067
1068 ci_extcon_unregister(ci);
941stop: 1069stop:
942 ci_role_destroy(ci); 1070 ci_role_destroy(ci);
943deinit_phy: 1071deinit_phy:
@@ -957,6 +1085,7 @@ static int ci_hdrc_remove(struct platform_device *pdev)
957 } 1085 }
958 1086
959 dbg_remove_files(ci); 1087 dbg_remove_files(ci);
1088 ci_extcon_unregister(ci);
960 ci_role_destroy(ci); 1089 ci_role_destroy(ci);
961 ci_hdrc_enter_lpm(ci, true); 1090 ci_hdrc_enter_lpm(ci, true);
962 ci_usb_phy_exit(ci); 1091 ci_usb_phy_exit(ci);
@@ -996,6 +1125,9 @@ static void ci_controller_suspend(struct ci_hdrc *ci)
996{ 1125{
997 disable_irq(ci->irq); 1126 disable_irq(ci->irq);
998 ci_hdrc_enter_lpm(ci, true); 1127 ci_hdrc_enter_lpm(ci, true);
1128 if (ci->platdata->phy_clkgate_delay_us)
1129 usleep_range(ci->platdata->phy_clkgate_delay_us,
1130 ci->platdata->phy_clkgate_delay_us + 50);
999 usb_phy_set_suspend(ci->usb_phy, 1); 1131 usb_phy_set_suspend(ci->usb_phy, 1);
1000 ci->in_lpm = true; 1132 ci->in_lpm = true;
1001 enable_irq(ci->irq); 1133 enable_irq(ci->irq);
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index ad6c87a4653c..45f86da1d6d3 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/**
@@ -77,9 +114,12 @@ static void ci_handle_id_switch(struct ci_hdrc *ci)
77 ci_role(ci)->name, ci->roles[role]->name); 114 ci_role(ci)->name, ci->roles[role]->name);
78 115
79 ci_role_stop(ci); 116 ci_role_stop(ci);
80 /* wait vbus lower than OTGSC_BSV */ 117
81 hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0, 118 if (role == CI_ROLE_GADGET)
82 CI_VBUS_STABLE_TIMEOUT_MS); 119 /* wait vbus lower than OTGSC_BSV */
120 hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0,
121 CI_VBUS_STABLE_TIMEOUT_MS);
122
83 ci_role_start(ci, role); 123 ci_role_start(ci, role);
84 } 124 }
85} 125}
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index 5ddab30ee240..fcea4eb36eee 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -72,6 +72,14 @@
72 72
73#define VF610_OVER_CUR_DIS BIT(7) 73#define VF610_OVER_CUR_DIS BIT(7)
74 74
75#define MX7D_USBNC_USB_CTRL2 0x4
76#define MX7D_USB_VBUS_WAKEUP_SOURCE_MASK 0x3
77#define MX7D_USB_VBUS_WAKEUP_SOURCE(v) (v << 0)
78#define MX7D_USB_VBUS_WAKEUP_SOURCE_VBUS MX7D_USB_VBUS_WAKEUP_SOURCE(0)
79#define MX7D_USB_VBUS_WAKEUP_SOURCE_AVALID MX7D_USB_VBUS_WAKEUP_SOURCE(1)
80#define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID MX7D_USB_VBUS_WAKEUP_SOURCE(2)
81#define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END MX7D_USB_VBUS_WAKEUP_SOURCE(3)
82
75struct usbmisc_ops { 83struct usbmisc_ops {
76 /* It's called once when probe a usb device */ 84 /* It's called once when probe a usb device */
77 int (*init)(struct imx_usbmisc_data *data); 85 int (*init)(struct imx_usbmisc_data *data);
@@ -324,6 +332,55 @@ static int usbmisc_vf610_init(struct imx_usbmisc_data *data)
324 return 0; 332 return 0;
325} 333}
326 334
335static int usbmisc_imx7d_set_wakeup
336 (struct imx_usbmisc_data *data, bool enabled)
337{
338 struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
339 unsigned long flags;
340 u32 val;
341 u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE |
342 MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP);
343
344 spin_lock_irqsave(&usbmisc->lock, flags);
345 val = readl(usbmisc->base);
346 if (enabled) {
347 writel(val | wakeup_setting, usbmisc->base);
348 } else {
349 if (val & MX6_BM_WAKEUP_INTR)
350 dev_dbg(data->dev, "wakeup int\n");
351 writel(val & ~wakeup_setting, usbmisc->base);
352 }
353 spin_unlock_irqrestore(&usbmisc->lock, flags);
354
355 return 0;
356}
357
358static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
359{
360 struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
361 unsigned long flags;
362 u32 reg;
363
364 if (data->index >= 1)
365 return -EINVAL;
366
367 spin_lock_irqsave(&usbmisc->lock, flags);
368 if (data->disable_oc) {
369 reg = readl(usbmisc->base);
370 writel(reg | MX6_BM_OVER_CUR_DIS, usbmisc->base);
371 }
372
373 reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
374 reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
375 writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID,
376 usbmisc->base + MX7D_USBNC_USB_CTRL2);
377 spin_unlock_irqrestore(&usbmisc->lock, flags);
378
379 usbmisc_imx7d_set_wakeup(data, false);
380
381 return 0;
382}
383
327static const struct usbmisc_ops imx25_usbmisc_ops = { 384static const struct usbmisc_ops imx25_usbmisc_ops = {
328 .init = usbmisc_imx25_init, 385 .init = usbmisc_imx25_init,
329 .post = usbmisc_imx25_post, 386 .post = usbmisc_imx25_post,
@@ -351,6 +408,11 @@ static const struct usbmisc_ops imx6sx_usbmisc_ops = {
351 .init = usbmisc_imx6sx_init, 408 .init = usbmisc_imx6sx_init,
352}; 409};
353 410
411static const struct usbmisc_ops imx7d_usbmisc_ops = {
412 .init = usbmisc_imx7d_init,
413 .set_wakeup = usbmisc_imx7d_set_wakeup,
414};
415
354int imx_usbmisc_init(struct imx_usbmisc_data *data) 416int imx_usbmisc_init(struct imx_usbmisc_data *data)
355{ 417{
356 struct imx_usbmisc *usbmisc; 418 struct imx_usbmisc *usbmisc;
@@ -426,6 +488,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = {
426 .compatible = "fsl,imx6sx-usbmisc", 488 .compatible = "fsl,imx6sx-usbmisc",
427 .data = &imx6sx_usbmisc_ops, 489 .data = &imx6sx_usbmisc_ops,
428 }, 490 },
491 {
492 .compatible = "fsl,imx6ul-usbmisc",
493 .data = &imx6sx_usbmisc_ops,
494 },
429 { /* sentinel */ } 495 { /* sentinel */ }
430}; 496};
431MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids); 497MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);