diff options
author | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-04-11 10:41:07 +0300 |
---|---|---|
committer | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-04-13 08:17:18 +0300 |
commit | e09b41010ba33a20a87472ee821fa407a5b8da36 (patch) | |
tree | d10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/drivers/usb/chipidea | |
parent | f93b97fd65072de626c074dbe099a1fff05ce060 (diff) |
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page.
During the rebasing, the following patch collided:
Force tick interrupt and get rid of softirq magic(I70131fb85).
Collisions have been removed because its logic was found on the
source already.
Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769
Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/drivers/usb/chipidea')
-rw-r--r-- | kernel/drivers/usb/chipidea/Kconfig | 1 | ||||
-rw-r--r-- | kernel/drivers/usb/chipidea/bits.h | 12 | ||||
-rw-r--r-- | kernel/drivers/usb/chipidea/ci.h | 9 | ||||
-rw-r--r-- | kernel/drivers/usb/chipidea/ci_hdrc_imx.c | 170 | ||||
-rw-r--r-- | kernel/drivers/usb/chipidea/ci_hdrc_pci.c | 6 | ||||
-rw-r--r-- | kernel/drivers/usb/chipidea/ci_hdrc_usb2.c | 33 | ||||
-rw-r--r-- | kernel/drivers/usb/chipidea/core.c | 264 | ||||
-rw-r--r-- | kernel/drivers/usb/chipidea/debug.c | 9 | ||||
-rw-r--r-- | kernel/drivers/usb/chipidea/host.c | 32 | ||||
-rw-r--r-- | kernel/drivers/usb/chipidea/otg.c | 50 | ||||
-rw-r--r-- | kernel/drivers/usb/chipidea/otg_fsm.c | 1 | ||||
-rw-r--r-- | kernel/drivers/usb/chipidea/udc.c | 131 | ||||
-rw-r--r-- | kernel/drivers/usb/chipidea/usbmisc_imx.c | 90 |
13 files changed, 693 insertions, 115 deletions
diff --git a/kernel/drivers/usb/chipidea/Kconfig b/kernel/drivers/usb/chipidea/Kconfig index 5ce3f1d6a..5619b8ca3 100644 --- a/kernel/drivers/usb/chipidea/Kconfig +++ b/kernel/drivers/usb/chipidea/Kconfig @@ -1,6 +1,7 @@ config USB_CHIPIDEA tristate "ChipIdea Highspeed Dual Role Controller" depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA + select EXTCON help Say Y here if your system has a dual role high speed USB controller based on ChipIdea silicon IP. Currently, only the diff --git a/kernel/drivers/usb/chipidea/bits.h b/kernel/drivers/usb/chipidea/bits.h index 3cb9bda51..e462f55c8 100644 --- a/kernel/drivers/usb/chipidea/bits.h +++ b/kernel/drivers/usb/chipidea/bits.h @@ -25,6 +25,9 @@ #define VERSION (0xF << 25) #define CIVERSION (0x7 << 29) +/* SBUSCFG */ +#define AHBBRST_MASK 0x7 + /* HCCPARAMS */ #define HCCPARAMS_LEN BIT(17) @@ -53,6 +56,15 @@ #define DEVICEADDR_USBADRA BIT(24) #define DEVICEADDR_USBADR (0x7FUL << 25) +/* TTCTRL */ +#define TTCTRL_TTHA_MASK (0x7fUL << 24) +/* Set non-zero value for internal TT Hub address representation */ +#define TTCTRL_TTHA (0x7fUL << 24) + +/* BURSTSIZE */ +#define RX_BURST_MASK 0xff +#define TX_BURST_MASK 0xff00 + /* PORTSC */ #define PORTSC_CCS BIT(0) #define PORTSC_CSC BIT(1) diff --git a/kernel/drivers/usb/chipidea/ci.h b/kernel/drivers/usb/chipidea/ci.h index 6d6200e37..41d7cf6d6 100644 --- a/kernel/drivers/usb/chipidea/ci.h +++ b/kernel/drivers/usb/chipidea/ci.h @@ -50,6 +50,8 @@ enum ci_hw_regs { OP_USBINTR, OP_DEVICEADDR, OP_ENDPTLISTADDR, + OP_TTCTRL, + OP_BURSTSIZE, OP_PORTSC, OP_DEVLC, OP_OTGSC, @@ -406,8 +408,11 @@ static inline u32 hw_test_and_write(struct ci_hdrc *ci, enum ci_hw_regs reg, static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci) { #ifdef CONFIG_USB_OTG_FSM + struct usb_otg_caps *otg_caps = &ci->platdata->ci_otg_caps; + return ci->is_otg && ci->roles[CI_ROLE_HOST] && - ci->roles[CI_ROLE_GADGET]; + ci->roles[CI_ROLE_GADGET] && (otg_caps->srp_support || + otg_caps->hnp_support || otg_caps->adp_support); #else return false; #endif @@ -426,4 +431,6 @@ u8 hw_port_test_get(struct ci_hdrc *ci); int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask, u32 value, unsigned int timeout_ms); +void ci_platform_configure(struct ci_hdrc *ci); + #endif /* __DRIVERS_USB_CHIPIDEA_CI_H */ diff --git a/kernel/drivers/usb/chipidea/ci_hdrc_imx.c b/kernel/drivers/usb/chipidea/ci_hdrc_imx.c index 389f0e034..5a048b7b9 100644 --- a/kernel/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/kernel/drivers/usb/chipidea/ci_hdrc_imx.c @@ -29,34 +29,50 @@ struct ci_hdrc_imx_platform_flag { }; static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { + CI_HDRC_DISABLE_STREAMING, }; static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { .flags = CI_HDRC_IMX28_WRITE_FIX | - CI_HDRC_TURN_VBUS_EARLY_ON, + CI_HDRC_TURN_VBUS_EARLY_ON | + CI_HDRC_DISABLE_STREAMING, }; static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = { .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | - CI_HDRC_TURN_VBUS_EARLY_ON, + CI_HDRC_TURN_VBUS_EARLY_ON | + CI_HDRC_DISABLE_STREAMING, }; static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = { .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | - CI_HDRC_TURN_VBUS_EARLY_ON, + CI_HDRC_TURN_VBUS_EARLY_ON | + CI_HDRC_DISABLE_HOST_STREAMING, }; static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = { .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | + CI_HDRC_TURN_VBUS_EARLY_ON | + CI_HDRC_DISABLE_HOST_STREAMING, +}; + +static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = { + .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | CI_HDRC_TURN_VBUS_EARLY_ON, }; +static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = { + .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, +}; + static const struct of_device_id ci_hdrc_imx_dt_ids[] = { { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data}, - { .compatible = "fsl,imx6sx-usb", .data = &imx6sl_usb_data}, + { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data}, + { .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data}, + { .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); @@ -68,6 +84,12 @@ struct ci_hdrc_imx_data { struct imx_usbmisc_data *usbmisc_data; bool supports_runtime_pm; bool in_lpm; + /* SoC before i.mx6 (except imx23/imx28) needs three clks */ + bool need_three_clks; + struct clk *clk_ipg; + struct clk *clk_ahb; + struct clk *clk_per; + /* --------------------------------- */ }; /* Common functions shared by usbmisc drivers */ @@ -104,7 +126,7 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) misc_pdev = of_find_device_by_node(args.np); of_node_put(args.np); - if (!misc_pdev) + if (!misc_pdev || !platform_get_drvdata(misc_pdev)) return ERR_PTR(-EPROBE_DEFER); data->dev = &misc_pdev->dev; @@ -119,6 +141,102 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) } /* End of common functions shared by usbmisc drivers*/ +static int imx_get_clks(struct device *dev) +{ + struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); + int ret = 0; + + data->clk_ipg = devm_clk_get(dev, "ipg"); + if (IS_ERR(data->clk_ipg)) { + /* If the platform only needs one clocks */ + data->clk = devm_clk_get(dev, NULL); + if (IS_ERR(data->clk)) { + ret = PTR_ERR(data->clk); + dev_err(dev, + "Failed to get clks, err=%ld,%ld\n", + PTR_ERR(data->clk), PTR_ERR(data->clk_ipg)); + return ret; + } + return ret; + } + + data->clk_ahb = devm_clk_get(dev, "ahb"); + if (IS_ERR(data->clk_ahb)) { + ret = PTR_ERR(data->clk_ahb); + dev_err(dev, + "Failed to get ahb clock, err=%d\n", ret); + return ret; + } + + data->clk_per = devm_clk_get(dev, "per"); + if (IS_ERR(data->clk_per)) { + ret = PTR_ERR(data->clk_per); + dev_err(dev, + "Failed to get per clock, err=%d\n", ret); + return ret; + } + + data->need_three_clks = true; + return ret; +} + +static int imx_prepare_enable_clks(struct device *dev) +{ + struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); + int ret = 0; + + if (data->need_three_clks) { + ret = clk_prepare_enable(data->clk_ipg); + if (ret) { + dev_err(dev, + "Failed to prepare/enable ipg clk, err=%d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(data->clk_ahb); + if (ret) { + dev_err(dev, + "Failed to prepare/enable ahb clk, err=%d\n", + ret); + clk_disable_unprepare(data->clk_ipg); + return ret; + } + + ret = clk_prepare_enable(data->clk_per); + if (ret) { + dev_err(dev, + "Failed to prepare/enable per clk, err=%d\n", + ret); + clk_disable_unprepare(data->clk_ahb); + clk_disable_unprepare(data->clk_ipg); + return ret; + } + } else { + ret = clk_prepare_enable(data->clk); + if (ret) { + dev_err(dev, + "Failed to prepare/enable clk, err=%d\n", + ret); + return ret; + } + } + + return ret; +} + +static void imx_disable_unprepare_clks(struct device *dev) +{ + struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); + + if (data->need_three_clks) { + clk_disable_unprepare(data->clk_per); + clk_disable_unprepare(data->clk_ahb); + clk_disable_unprepare(data->clk_ipg); + } else { + clk_disable_unprepare(data->clk); + } +} static int ci_hdrc_imx_probe(struct platform_device *pdev) { @@ -126,34 +244,34 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) struct ci_hdrc_platform_data pdata = { .name = dev_name(&pdev->dev), .capoffset = DEF_CAPOFFSET, - .flags = CI_HDRC_DISABLE_STREAMING, + .flags = CI_HDRC_SET_NON_ZERO_TTHA, }; int ret; - const struct of_device_id *of_id = - of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev); - const struct ci_hdrc_imx_platform_flag *imx_platform_flag = of_id->data; + const struct of_device_id *of_id; + const struct ci_hdrc_imx_platform_flag *imx_platform_flag; + + of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev); + if (!of_id) + return -ENODEV; + + imx_platform_flag = of_id->data; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; + platform_set_drvdata(pdev, data); data->usbmisc_data = usbmisc_get_init_data(&pdev->dev); if (IS_ERR(data->usbmisc_data)) return PTR_ERR(data->usbmisc_data); - data->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(data->clk)) { - dev_err(&pdev->dev, - "Failed to get clock, err=%ld\n", PTR_ERR(data->clk)); - return PTR_ERR(data->clk); - } + ret = imx_get_clks(&pdev->dev); + if (ret) + return ret; - ret = clk_prepare_enable(data->clk); - if (ret) { - dev_err(&pdev->dev, - "Failed to prepare or enable clock, err=%d\n", ret); + ret = imx_prepare_enable_clks(&pdev->dev); + if (ret) return ret; - } data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0); if (IS_ERR(data->phy)) { @@ -196,8 +314,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) goto disable_device; } - platform_set_drvdata(pdev, data); - if (data->supports_runtime_pm) { pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); @@ -210,7 +326,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) disable_device: ci_hdrc_remove_device(data->ci_pdev); err_clk: - clk_disable_unprepare(data->clk); + imx_disable_unprepare_clks(&pdev->dev); return ret; } @@ -224,7 +340,7 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); } ci_hdrc_remove_device(data->ci_pdev); - clk_disable_unprepare(data->clk); + imx_disable_unprepare_clks(&pdev->dev); return 0; } @@ -236,7 +352,7 @@ static int imx_controller_suspend(struct device *dev) dev_dbg(dev, "at %s\n", __func__); - clk_disable_unprepare(data->clk); + imx_disable_unprepare_clks(dev); data->in_lpm = true; return 0; @@ -254,7 +370,7 @@ static int imx_controller_resume(struct device *dev) return 0; } - ret = clk_prepare_enable(data->clk); + ret = imx_prepare_enable_clks(dev); if (ret) return ret; @@ -269,7 +385,7 @@ static int imx_controller_resume(struct device *dev) return 0; clk_disable: - clk_disable_unprepare(data->clk); + imx_disable_unprepare_clks(dev); return ret; } diff --git a/kernel/drivers/usb/chipidea/ci_hdrc_pci.c b/kernel/drivers/usb/chipidea/ci_hdrc_pci.c index 773d15051..b59195edf 100644 --- a/kernel/drivers/usb/chipidea/ci_hdrc_pci.c +++ b/kernel/drivers/usb/chipidea/ci_hdrc_pci.c @@ -142,16 +142,16 @@ static const struct pci_device_id ci_hdrc_pci_id_table[] = { .driver_data = (kernel_ulong_t)&pci_platdata, }, { - PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0811), + PCI_VDEVICE(INTEL, 0x0811), .driver_data = (kernel_ulong_t)&langwell_pci_platdata, }, { - PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0829), + PCI_VDEVICE(INTEL, 0x0829), .driver_data = (kernel_ulong_t)&penwell_pci_platdata, }, { /* Intel Clovertrail */ - PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe006), + PCI_VDEVICE(INTEL, 0xe006), .driver_data = (kernel_ulong_t)&penwell_pci_platdata, }, { 0 } /* end: all zeroes */ diff --git a/kernel/drivers/usb/chipidea/ci_hdrc_usb2.c b/kernel/drivers/usb/chipidea/ci_hdrc_usb2.c index 45844c951..4456d2cf8 100644 --- a/kernel/drivers/usb/chipidea/ci_hdrc_usb2.c +++ b/kernel/drivers/usb/chipidea/ci_hdrc_usb2.c @@ -12,6 +12,7 @@ #include <linux/dma-mapping.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_platform.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/usb/chipidea.h> @@ -25,20 +26,40 @@ struct ci_hdrc_usb2_priv { struct clk *clk; }; -static struct ci_hdrc_platform_data ci_default_pdata = { +static const struct ci_hdrc_platform_data ci_default_pdata = { .capoffset = DEF_CAPOFFSET, .flags = CI_HDRC_DISABLE_STREAMING, }; +static struct ci_hdrc_platform_data ci_zynq_pdata = { + .capoffset = DEF_CAPOFFSET, +}; + +static const struct of_device_id ci_hdrc_usb2_of_match[] = { + { .compatible = "chipidea,usb2"}, + { .compatible = "xlnx,zynq-usb-2.20a", .data = &ci_zynq_pdata}, + { } +}; +MODULE_DEVICE_TABLE(of, ci_hdrc_usb2_of_match); + static int ci_hdrc_usb2_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ci_hdrc_usb2_priv *priv; struct ci_hdrc_platform_data *ci_pdata = dev_get_platdata(dev); int ret; + const struct of_device_id *match; - if (!ci_pdata) - ci_pdata = &ci_default_pdata; + if (!ci_pdata) { + ci_pdata = devm_kmalloc(dev, sizeof(*ci_pdata), GFP_KERNEL); + *ci_pdata = ci_default_pdata; /* struct copy */ + } + + match = of_match_device(ci_hdrc_usb2_of_match, &pdev->dev); + if (match && match->data) { + /* struct copy */ + *ci_pdata = *(struct ci_hdrc_platform_data *)match->data; + } priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -94,12 +115,6 @@ static int ci_hdrc_usb2_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id ci_hdrc_usb2_of_match[] = { - { .compatible = "chipidea,usb2" }, - { } -}; -MODULE_DEVICE_TABLE(of, ci_hdrc_usb2_of_match); - static struct platform_driver ci_hdrc_usb2_driver = { .probe = ci_hdrc_usb2_probe, .remove = ci_hdrc_usb2_remove, diff --git a/kernel/drivers/usb/chipidea/core.c b/kernel/drivers/usb/chipidea/core.c index 3ad48e1c0..965d0e240 100644 --- a/kernel/drivers/usb/chipidea/core.c +++ b/kernel/drivers/usb/chipidea/core.c @@ -47,6 +47,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/dma-mapping.h> +#include <linux/extcon.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/module.h> @@ -64,6 +65,7 @@ #include <linux/of.h> #include <linux/phy.h> #include <linux/regulator/consumer.h> +#include <linux/usb/ehci_def.h> #include "ci.h" #include "udc.h" @@ -84,6 +86,8 @@ static const u8 ci_regs_nolpm[] = { [OP_USBINTR] = 0x08U, [OP_DEVICEADDR] = 0x14U, [OP_ENDPTLISTADDR] = 0x18U, + [OP_TTCTRL] = 0x1CU, + [OP_BURSTSIZE] = 0x20U, [OP_PORTSC] = 0x44U, [OP_DEVLC] = 0x84U, [OP_OTGSC] = 0x64U, @@ -106,6 +110,8 @@ static const u8 ci_regs_lpm[] = { [OP_USBINTR] = 0x08U, [OP_DEVICEADDR] = 0x14U, [OP_ENDPTLISTADDR] = 0x18U, + [OP_TTCTRL] = 0x1CU, + [OP_BURSTSIZE] = 0x20U, [OP_PORTSC] = 0x44U, [OP_DEVLC] = 0x84U, [OP_OTGSC] = 0xC4U, @@ -118,7 +124,7 @@ static const u8 ci_regs_lpm[] = { [OP_ENDPTCTRL] = 0xECU, }; -static int hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm) +static void hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm) { int i; @@ -134,7 +140,6 @@ static int hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm) ? ci_regs_lpm[OP_ENDPTCTRL] : ci_regs_nolpm[OP_ENDPTCTRL]); - return 0; } static enum ci_revision ci_get_revision(struct ci_hdrc *ci) @@ -403,6 +408,55 @@ static int ci_usb_phy_init(struct ci_hdrc *ci) return ret; } + +/** + * ci_platform_configure: do controller configure + * @ci: the controller + * + */ +void ci_platform_configure(struct ci_hdrc *ci) +{ + bool is_device_mode, is_host_mode; + + is_device_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_DC; + is_host_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_HC; + + if (is_device_mode && + (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING)) + hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); + + if (is_host_mode && + (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING)) + hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); + + if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) { + if (ci->hw_bank.lpm) + hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC); + else + hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); + } + + if (ci->platdata->flags & CI_HDRC_SET_NON_ZERO_TTHA) + hw_write(ci, OP_TTCTRL, TTCTRL_TTHA_MASK, TTCTRL_TTHA); + + hw_write(ci, OP_USBCMD, 0xff0000, ci->platdata->itc_setting << 16); + + if (ci->platdata->flags & CI_HDRC_OVERRIDE_AHB_BURST) + hw_write_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK, + ci->platdata->ahb_burst_config); + + /* override burst size, take effect only when ahb_burst_config is 0 */ + if (!hw_read_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK)) { + if (ci->platdata->flags & CI_HDRC_OVERRIDE_TX_BURST) + hw_write(ci, OP_BURSTSIZE, TX_BURST_MASK, + ci->platdata->tx_burst_size << __ffs(TX_BURST_MASK)); + + if (ci->platdata->flags & CI_HDRC_OVERRIDE_RX_BURST) + hw_write(ci, OP_BURSTSIZE, RX_BURST_MASK, + ci->platdata->rx_burst_size); + } +} + /** * hw_controller_reset: do controller reset * @ci: the controller @@ -447,16 +501,6 @@ int hw_device_reset(struct ci_hdrc *ci) ci->platdata->notify_event(ci, CI_HDRC_CONTROLLER_RESET_EVENT); - if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING) - hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); - - if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) { - if (ci->hw_bank.lpm) - hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC); - else - hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); - } - /* USBMODE should be configured step by step */ hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_DC); @@ -469,6 +513,8 @@ int hw_device_reset(struct ci_hdrc *ci) return -ENODEV; } + ci_platform_configure(ci); + return 0; } @@ -557,14 +603,52 @@ static irqreturn_t ci_irq(int irq, void *data) return ret; } +static int ci_vbus_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct ci_hdrc_cable *vbus = container_of(nb, struct ci_hdrc_cable, nb); + struct ci_hdrc *ci = vbus->ci; + + if (event) + vbus->state = true; + else + vbus->state = false; + + vbus->changed = true; + + ci_irq(ci->irq, ci); + return NOTIFY_DONE; +} + +static int ci_id_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct ci_hdrc_cable *id = container_of(nb, struct ci_hdrc_cable, nb); + struct ci_hdrc *ci = id->ci; + + if (event) + id->state = false; + else + id->state = true; + + id->changed = true; + + ci_irq(ci->irq, ci); + return NOTIFY_DONE; +} + static int ci_get_platdata(struct device *dev, struct ci_hdrc_platform_data *platdata) { + struct extcon_dev *ext_vbus, *ext_id; + struct ci_hdrc_cable *cable; + int ret; + if (!platdata->phy_mode) platdata->phy_mode = of_usb_get_phy_mode(dev->of_node); if (!platdata->dr_mode) - platdata->dr_mode = of_usb_get_dr_mode(dev->of_node); + platdata->dr_mode = usb_get_dr_mode(dev); if (platdata->dr_mode == USB_DR_MODE_UNKNOWN) platdata->dr_mode = USB_DR_MODE_OTG; @@ -588,12 +672,155 @@ static int ci_get_platdata(struct device *dev, of_usb_host_tpl_support(dev->of_node); } - if (of_usb_get_maximum_speed(dev->of_node) == USB_SPEED_FULL) + if (platdata->dr_mode == USB_DR_MODE_OTG) { + /* We can support HNP and SRP of OTG 2.0 */ + platdata->ci_otg_caps.otg_rev = 0x0200; + platdata->ci_otg_caps.hnp_support = true; + platdata->ci_otg_caps.srp_support = true; + + /* Update otg capabilities by DT properties */ + ret = of_usb_update_otg_caps(dev->of_node, + &platdata->ci_otg_caps); + if (ret) + return ret; + } + + if (usb_get_maximum_speed(dev) == USB_SPEED_FULL) platdata->flags |= CI_HDRC_FORCE_FULLSPEED; + if (of_find_property(dev->of_node, "phy-clkgate-delay-us", NULL)) + of_property_read_u32(dev->of_node, "phy-clkgate-delay-us", + &platdata->phy_clkgate_delay_us); + + platdata->itc_setting = 1; + if (of_find_property(dev->of_node, "itc-setting", NULL)) { + ret = of_property_read_u32(dev->of_node, "itc-setting", + &platdata->itc_setting); + if (ret) { + dev_err(dev, + "failed to get itc-setting\n"); + return ret; + } + } + + if (of_find_property(dev->of_node, "ahb-burst-config", NULL)) { + ret = of_property_read_u32(dev->of_node, "ahb-burst-config", + &platdata->ahb_burst_config); + if (ret) { + dev_err(dev, + "failed to get ahb-burst-config\n"); + return ret; + } + platdata->flags |= CI_HDRC_OVERRIDE_AHB_BURST; + } + + if (of_find_property(dev->of_node, "tx-burst-size-dword", NULL)) { + ret = of_property_read_u32(dev->of_node, "tx-burst-size-dword", + &platdata->tx_burst_size); + if (ret) { + dev_err(dev, + "failed to get tx-burst-size-dword\n"); + return ret; + } + platdata->flags |= CI_HDRC_OVERRIDE_TX_BURST; + } + + if (of_find_property(dev->of_node, "rx-burst-size-dword", NULL)) { + ret = of_property_read_u32(dev->of_node, "rx-burst-size-dword", + &platdata->rx_burst_size); + if (ret) { + dev_err(dev, + "failed to get rx-burst-size-dword\n"); + return ret; + } + platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST; + } + + ext_id = ERR_PTR(-ENODEV); + ext_vbus = ERR_PTR(-ENODEV); + if (of_property_read_bool(dev->of_node, "extcon")) { + /* Each one of them is not mandatory */ + ext_vbus = extcon_get_edev_by_phandle(dev, 0); + if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) + return PTR_ERR(ext_vbus); + + ext_id = extcon_get_edev_by_phandle(dev, 1); + if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV) + return PTR_ERR(ext_id); + } + + cable = &platdata->vbus_extcon; + cable->nb.notifier_call = ci_vbus_notifier; + cable->edev = ext_vbus; + + if (!IS_ERR(ext_vbus)) { + ret = extcon_get_cable_state_(cable->edev, EXTCON_USB); + if (ret) + cable->state = true; + else + cable->state = false; + } + + cable = &platdata->id_extcon; + cable->nb.notifier_call = ci_id_notifier; + cable->edev = ext_id; + + if (!IS_ERR(ext_id)) { + ret = extcon_get_cable_state_(cable->edev, EXTCON_USB_HOST); + if (ret) + cable->state = false; + else + cable->state = true; + } + return 0; +} + +static int ci_extcon_register(struct ci_hdrc *ci) +{ + struct ci_hdrc_cable *id, *vbus; + int ret; + + id = &ci->platdata->id_extcon; + id->ci = ci; + if (!IS_ERR(id->edev)) { + ret = extcon_register_notifier(id->edev, EXTCON_USB_HOST, + &id->nb); + if (ret < 0) { + dev_err(ci->dev, "register ID failed\n"); + return ret; + } + } + + vbus = &ci->platdata->vbus_extcon; + vbus->ci = ci; + if (!IS_ERR(vbus->edev)) { + ret = extcon_register_notifier(vbus->edev, EXTCON_USB, + &vbus->nb); + if (ret < 0) { + extcon_unregister_notifier(id->edev, EXTCON_USB_HOST, + &id->nb); + dev_err(ci->dev, "register VBUS failed\n"); + return ret; + } + } + return 0; } +static void ci_extcon_unregister(struct ci_hdrc *ci) +{ + struct ci_hdrc_cable *cable; + + cable = &ci->platdata->id_extcon; + if (!IS_ERR(cable->edev)) + extcon_unregister_notifier(cable->edev, EXTCON_USB_HOST, + &cable->nb); + + cable = &ci->platdata->vbus_extcon; + if (!IS_ERR(cable->edev)) + extcon_unregister_notifier(cable->edev, EXTCON_USB, &cable->nb); +} + static DEFINE_IDA(ci_ida); struct platform_device *ci_hdrc_add_device(struct device *dev, @@ -817,6 +1044,10 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (ret) goto stop; + ret = ci_extcon_register(ci); + if (ret) + goto stop; + if (ci->supports_runtime_pm) { pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); @@ -834,6 +1065,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (!ret) return 0; + ci_extcon_unregister(ci); stop: ci_role_destroy(ci); deinit_phy: @@ -853,6 +1085,7 @@ static int ci_hdrc_remove(struct platform_device *pdev) } dbg_remove_files(ci); + ci_extcon_unregister(ci); ci_role_destroy(ci); ci_hdrc_enter_lpm(ci, true); ci_usb_phy_exit(ci); @@ -892,6 +1125,9 @@ static void ci_controller_suspend(struct ci_hdrc *ci) { disable_irq(ci->irq); ci_hdrc_enter_lpm(ci, true); + if (ci->platdata->phy_clkgate_delay_us) + usleep_range(ci->platdata->phy_clkgate_delay_us, + ci->platdata->phy_clkgate_delay_us + 50); usb_phy_set_suspend(ci->usb_phy, 1); ci->in_lpm = true; enable_irq(ci->irq); diff --git a/kernel/drivers/usb/chipidea/debug.c b/kernel/drivers/usb/chipidea/debug.c index 5b7061a33..58c8485a0 100644 --- a/kernel/drivers/usb/chipidea/debug.c +++ b/kernel/drivers/usb/chipidea/debug.c @@ -10,6 +10,7 @@ #include <linux/usb/phy.h> #include <linux/usb/otg.h> #include <linux/usb/otg-fsm.h> +#include <linux/usb/chipidea.h> #include "ci.h" #include "udc.h" @@ -66,9 +67,11 @@ static int ci_port_test_show(struct seq_file *s, void *data) unsigned long flags; unsigned mode; + pm_runtime_get_sync(ci->dev); spin_lock_irqsave(&ci->lock, flags); mode = hw_port_test_get(ci); spin_unlock_irqrestore(&ci->lock, flags); + pm_runtime_put_sync(ci->dev); seq_printf(s, "mode = %u\n", mode); @@ -98,9 +101,11 @@ static ssize_t ci_port_test_write(struct file *file, const char __user *ubuf, if (sscanf(buf, "%u", &mode) != 1) return -EINVAL; + pm_runtime_get_sync(ci->dev); spin_lock_irqsave(&ci->lock, flags); ret = hw_port_test_set(ci, mode); spin_unlock_irqrestore(&ci->lock, flags); + pm_runtime_put_sync(ci->dev); return ret ? ret : count; } @@ -316,8 +321,12 @@ static ssize_t ci_role_write(struct file *file, const char __user *ubuf, if (role == CI_ROLE_END || role == ci->role) return -EINVAL; + pm_runtime_get_sync(ci->dev); + disable_irq(ci->irq); ci_role_stop(ci); ret = ci_role_start(ci, role); + enable_irq(ci->irq); + pm_runtime_put_sync(ci->dev); return ret ? ret : count; } diff --git a/kernel/drivers/usb/chipidea/host.c b/kernel/drivers/usb/chipidea/host.c index 2f8af40e8..3d2430440 100644 --- a/kernel/drivers/usb/chipidea/host.c +++ b/kernel/drivers/usb/chipidea/host.c @@ -44,6 +44,7 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv; struct device *dev = hcd->self.controller; + struct ci_hdrc *ci = dev_get_drvdata(dev); int ret = 0; int port = HCS_N_PORTS(ehci->hcs_params); @@ -64,12 +65,37 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) return ret; } } + + if (enable && (ci->platdata->phy_mode == USBPHY_INTERFACE_MODE_HSIC)) { + /* + * Marvell 28nm HSIC PHY requires forcing the port to HS mode. + * As HSIC is always HS, this should be safe for others. + */ + hw_port_test_set(ci, 5); + hw_port_test_set(ci, 0); + } return 0; }; +static int ehci_ci_reset(struct usb_hcd *hcd) +{ + struct device *dev = hcd->self.controller; + struct ci_hdrc *ci = dev_get_drvdata(dev); + int ret; + + ret = ehci_setup(hcd); + if (ret) + return ret; + + ci_platform_configure(ci); + + return ret; +} + static const struct ehci_driver_overrides ehci_ci_overrides = { .extra_priv_size = sizeof(struct ehci_ci_priv), .port_power = ehci_ci_portpower, + .reset = ehci_ci_reset, }; static irqreturn_t host_irq(struct ci_hdrc *ci) @@ -141,12 +167,6 @@ static int host_start(struct ci_hdrc *ci) } } - if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING) - hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); - - if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) - hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); - return ret; disable_reg: diff --git a/kernel/drivers/usb/chipidea/otg.c b/kernel/drivers/usb/chipidea/otg.c index ad6c87a46..03b674346 100644 --- a/kernel/drivers/usb/chipidea/otg.c +++ b/kernel/drivers/usb/chipidea/otg.c @@ -30,7 +30,44 @@ */ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask) { - return hw_read(ci, OP_OTGSC, mask); + struct ci_hdrc_cable *cable; + u32 val = hw_read(ci, OP_OTGSC, mask); + + /* + * If using extcon framework for VBUS and/or ID signal + * detection overwrite OTGSC register value + */ + cable = &ci->platdata->vbus_extcon; + if (!IS_ERR(cable->edev)) { + if (cable->changed) + val |= OTGSC_BSVIS; + else + val &= ~OTGSC_BSVIS; + + cable->changed = false; + + if (cable->state) + val |= OTGSC_BSV; + else + val &= ~OTGSC_BSV; + } + + cable = &ci->platdata->id_extcon; + if (!IS_ERR(cable->edev)) { + if (cable->changed) + val |= OTGSC_IDIS; + else + val &= ~OTGSC_IDIS; + + cable->changed = false; + + if (cable->state) + val |= OTGSC_ID; + else + val &= ~OTGSC_ID; + } + + return val; } /** @@ -77,9 +114,12 @@ static void ci_handle_id_switch(struct ci_hdrc *ci) ci_role(ci)->name, ci->roles[role]->name); ci_role_stop(ci); - /* wait vbus lower than OTGSC_BSV */ - hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0, - CI_VBUS_STABLE_TIMEOUT_MS); + + if (role == CI_ROLE_GADGET) + /* wait vbus lower than OTGSC_BSV */ + hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0, + CI_VBUS_STABLE_TIMEOUT_MS); + ci_role_start(ci, role); } } @@ -118,7 +158,7 @@ static void ci_otg_work(struct work_struct *work) int ci_hdrc_otg_init(struct ci_hdrc *ci) { INIT_WORK(&ci->work, ci_otg_work); - ci->wq = create_singlethread_workqueue("ci_otg"); + ci->wq = create_freezable_workqueue("ci_otg"); if (!ci->wq) { dev_err(ci->dev, "can't create workqueue\n"); return -ENODEV; diff --git a/kernel/drivers/usb/chipidea/otg_fsm.c b/kernel/drivers/usb/chipidea/otg_fsm.c index 19d655a74..00ab59d45 100644 --- a/kernel/drivers/usb/chipidea/otg_fsm.c +++ b/kernel/drivers/usb/chipidea/otg_fsm.c @@ -525,7 +525,6 @@ static int ci_otg_start_host(struct otg_fsm *fsm, int on) ci_role_start(ci, CI_ROLE_HOST); } else { ci_role_stop(ci); - hw_device_reset(ci); ci_role_start(ci, CI_ROLE_GADGET); } return 0; diff --git a/kernel/drivers/usb/chipidea/udc.c b/kernel/drivers/usb/chipidea/udc.c index 764f668d4..391a1225b 100644 --- a/kernel/drivers/usb/chipidea/udc.c +++ b/kernel/drivers/usb/chipidea/udc.c @@ -445,7 +445,7 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) rest -= count; } - if (hwreq->req.zero && hwreq->req.length + if (hwreq->req.zero && hwreq->req.length && hwep->dir == TX && (hwreq->req.length % hwep->ep.maxpacket == 0)) add_td_to_list(hwep, hwreq, 0); @@ -656,6 +656,44 @@ __acquires(hwep->lock) return 0; } +static int _ep_set_halt(struct usb_ep *ep, int value, bool check_transfer) +{ + struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); + int direction, retval = 0; + unsigned long flags; + + if (ep == NULL || hwep->ep.desc == NULL) + return -EINVAL; + + if (usb_endpoint_xfer_isoc(hwep->ep.desc)) + return -EOPNOTSUPP; + + spin_lock_irqsave(hwep->lock, flags); + + if (value && hwep->dir == TX && check_transfer && + !list_empty(&hwep->qh.queue) && + !usb_endpoint_xfer_control(hwep->ep.desc)) { + spin_unlock_irqrestore(hwep->lock, flags); + return -EAGAIN; + } + + direction = hwep->dir; + do { + retval |= hw_ep_set_halt(hwep->ci, hwep->num, hwep->dir, value); + + if (!value) + hwep->wedge = 0; + + if (hwep->type == USB_ENDPOINT_XFER_CONTROL) + hwep->dir = (hwep->dir == TX) ? RX : TX; + + } while (hwep->dir != direction); + + spin_unlock_irqrestore(hwep->lock, flags); + return retval; +} + + /** * _gadget_stop_activity: stops all USB activity, flushes & disables all endpts * @gadget: gadget @@ -1051,7 +1089,7 @@ __acquires(ci->lock) num += ci->hw_ep_max / 2; spin_unlock(&ci->lock); - err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep); + err = _ep_set_halt(&ci->ci_hw_ep[num].ep, 1, false); spin_lock(&ci->lock); if (!err) isr_setup_status_phase(ci); @@ -1090,6 +1128,13 @@ __acquires(ci->lock) if (ci_otg_is_fsm_mode(ci)) err = otg_a_alt_hnp_support(ci); break; + case USB_DEVICE_A_HNP_SUPPORT: + if (ci_otg_is_fsm_mode(ci)) { + ci->gadget.a_hnp_support = 1; + err = isr_setup_status_phase( + ci); + } + break; default: goto delegate; } @@ -1110,8 +1155,8 @@ delegate: if (err < 0) { spin_unlock(&ci->lock); - if (usb_ep_set_halt(&hwep->ep)) - dev_err(ci->dev, "error: ep_set_halt\n"); + if (_ep_set_halt(&hwep->ep, 1, false)) + dev_err(ci->dev, "error: _ep_set_halt\n"); spin_lock(&ci->lock); } } @@ -1142,9 +1187,9 @@ __acquires(ci->lock) err = isr_setup_status_phase(ci); if (err < 0) { spin_unlock(&ci->lock); - if (usb_ep_set_halt(&hwep->ep)) + if (_ep_set_halt(&hwep->ep, 1, false)) dev_err(ci->dev, - "error: ep_set_halt\n"); + "error: _ep_set_halt\n"); spin_lock(&ci->lock); } } @@ -1390,41 +1435,7 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) */ static int ep_set_halt(struct usb_ep *ep, int value) { - struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); - int direction, retval = 0; - unsigned long flags; - - if (ep == NULL || hwep->ep.desc == NULL) - return -EINVAL; - - if (usb_endpoint_xfer_isoc(hwep->ep.desc)) - return -EOPNOTSUPP; - - spin_lock_irqsave(hwep->lock, flags); - -#ifndef STALL_IN - /* g_file_storage MS compliant but g_zero fails chapter 9 compliance */ - if (value && hwep->type == USB_ENDPOINT_XFER_BULK && hwep->dir == TX && - !list_empty(&hwep->qh.queue)) { - spin_unlock_irqrestore(hwep->lock, flags); - return -EAGAIN; - } -#endif - - direction = hwep->dir; - do { - retval |= hw_ep_set_halt(hwep->ci, hwep->num, hwep->dir, value); - - if (!value) - hwep->wedge = 0; - - if (hwep->type == USB_ENDPOINT_XFER_CONTROL) - hwep->dir = (hwep->dir == TX) ? RX : TX; - - } while (hwep->dir != direction); - - spin_unlock_irqrestore(hwep->lock, flags); - return retval; + return _ep_set_halt(ep, value, true); } /** @@ -1624,6 +1635,20 @@ static int init_eps(struct ci_hdrc *ci) hwep->ep.name = hwep->name; hwep->ep.ops = &usb_ep_ops; + + if (i == 0) { + hwep->ep.caps.type_control = true; + } else { + hwep->ep.caps.type_iso = true; + hwep->ep.caps.type_bulk = true; + hwep->ep.caps.type_int = true; + } + + if (j == TX) + hwep->ep.caps.dir_in = true; + else + hwep->ep.caps.dir_out = true; + /* * for ep0: maxP defined in desc, for other * eps, maxP is set by epautoconfig() called @@ -1726,6 +1751,22 @@ static int ci_udc_start(struct usb_gadget *gadget, return retval; } +static void ci_udc_stop_for_otg_fsm(struct ci_hdrc *ci) +{ + if (!ci_otg_is_fsm_mode(ci)) + return; + + mutex_lock(&ci->fsm.lock); + if (ci->fsm.otg->state == OTG_STATE_A_PERIPHERAL) { + ci->fsm.a_bidl_adis_tmout = 1; + ci_hdrc_otg_fsm_start(ci); + } else if (ci->fsm.otg->state == OTG_STATE_B_PERIPHERAL) { + ci->fsm.protocol = PROTO_UNDEF; + ci->fsm.otg->state = OTG_STATE_UNDEFINED; + } + mutex_unlock(&ci->fsm.lock); +} + /** * ci_udc_stop: unregister a gadget driver */ @@ -1750,6 +1791,7 @@ static int ci_udc_stop(struct usb_gadget *gadget) ci->driver = NULL; spin_unlock_irqrestore(&ci->lock, flags); + ci_udc_stop_for_otg_fsm(ci); return 0; } @@ -1827,6 +1869,7 @@ static irqreturn_t udc_irq(struct ci_hdrc *ci) static int udc_start(struct ci_hdrc *ci) { struct device *dev = ci->dev; + struct usb_otg_caps *otg_caps = &ci->platdata->ci_otg_caps; int retval = 0; spin_lock_init(&ci->lock); @@ -1834,8 +1877,12 @@ static int udc_start(struct ci_hdrc *ci) ci->gadget.ops = &usb_gadget_ops; ci->gadget.speed = USB_SPEED_UNKNOWN; ci->gadget.max_speed = USB_SPEED_HIGH; - ci->gadget.is_otg = ci->is_otg ? 1 : 0; ci->gadget.name = ci->platdata->name; + ci->gadget.otg_caps = otg_caps; + + if (ci->is_otg && (otg_caps->hnp_support || otg_caps->srp_support || + otg_caps->adp_support)) + ci->gadget.is_otg = 1; INIT_LIST_HEAD(&ci->gadget.ep_list); diff --git a/kernel/drivers/usb/chipidea/usbmisc_imx.c b/kernel/drivers/usb/chipidea/usbmisc_imx.c index 140945cb1..ab8b027e8 100644 --- a/kernel/drivers/usb/chipidea/usbmisc_imx.c +++ b/kernel/drivers/usb/chipidea/usbmisc_imx.c @@ -54,6 +54,7 @@ #define MX53_USB_PHYCTRL1_PLLDIV_MASK 0x3 #define MX53_USB_PLL_DIV_24_MHZ 0x01 +#define MX6_BM_NON_BURST_SETTING BIT(1) #define MX6_BM_OVER_CUR_DIS BIT(7) #define MX6_BM_WAKEUP_ENABLE BIT(10) #define MX6_BM_ID_WAKEUP BIT(16) @@ -71,6 +72,14 @@ #define VF610_OVER_CUR_DIS BIT(7) +#define MX7D_USBNC_USB_CTRL2 0x4 +#define MX7D_USB_VBUS_WAKEUP_SOURCE_MASK 0x3 +#define MX7D_USB_VBUS_WAKEUP_SOURCE(v) (v << 0) +#define MX7D_USB_VBUS_WAKEUP_SOURCE_VBUS MX7D_USB_VBUS_WAKEUP_SOURCE(0) +#define MX7D_USB_VBUS_WAKEUP_SOURCE_AVALID MX7D_USB_VBUS_WAKEUP_SOURCE(1) +#define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID MX7D_USB_VBUS_WAKEUP_SOURCE(2) +#define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END MX7D_USB_VBUS_WAKEUP_SOURCE(3) + struct usbmisc_ops { /* It's called once when probe a usb device */ int (*init)(struct imx_usbmisc_data *data); @@ -160,7 +169,7 @@ static int usbmisc_imx27_init(struct imx_usbmisc_data *data) break; default: return -EINVAL; - }; + } spin_lock_irqsave(&usbmisc->lock, flags); if (data->disable_oc) @@ -255,14 +264,21 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) if (data->index > 3) return -EINVAL; + spin_lock_irqsave(&usbmisc->lock, flags); + if (data->disable_oc) { - spin_lock_irqsave(&usbmisc->lock, flags); reg = readl(usbmisc->base + data->index * 4); writel(reg | MX6_BM_OVER_CUR_DIS, usbmisc->base + data->index * 4); - spin_unlock_irqrestore(&usbmisc->lock, flags); } + /* SoC non-burst setting */ + reg = readl(usbmisc->base + data->index * 4); + writel(reg | MX6_BM_NON_BURST_SETTING, + usbmisc->base + data->index * 4); + + spin_unlock_irqrestore(&usbmisc->lock, flags); + usbmisc_imx6q_set_wakeup(data, false); return 0; @@ -316,6 +332,55 @@ static int usbmisc_vf610_init(struct imx_usbmisc_data *data) return 0; } +static int usbmisc_imx7d_set_wakeup + (struct imx_usbmisc_data *data, bool enabled) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 val; + u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE | + MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP); + + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base); + if (enabled) { + writel(val | wakeup_setting, usbmisc->base); + } else { + if (val & MX6_BM_WAKEUP_INTR) + dev_dbg(data->dev, "wakeup int\n"); + writel(val & ~wakeup_setting, usbmisc->base); + } + spin_unlock_irqrestore(&usbmisc->lock, flags); + + return 0; +} + +static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 reg; + + if (data->index >= 1) + return -EINVAL; + + spin_lock_irqsave(&usbmisc->lock, flags); + if (data->disable_oc) { + reg = readl(usbmisc->base); + writel(reg | MX6_BM_OVER_CUR_DIS, usbmisc->base); + } + + reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK; + writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID, + usbmisc->base + MX7D_USBNC_USB_CTRL2); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + usbmisc_imx7d_set_wakeup(data, false); + + return 0; +} + static const struct usbmisc_ops imx25_usbmisc_ops = { .init = usbmisc_imx25_init, .post = usbmisc_imx25_post, @@ -343,6 +408,11 @@ static const struct usbmisc_ops imx6sx_usbmisc_ops = { .init = usbmisc_imx6sx_init, }; +static const struct usbmisc_ops imx7d_usbmisc_ops = { + .init = usbmisc_imx7d_init, + .set_wakeup = usbmisc_imx7d_set_wakeup, +}; + int imx_usbmisc_init(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc; @@ -418,6 +488,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = { .compatible = "fsl,imx6sx-usbmisc", .data = &imx6sx_usbmisc_ops, }, + { + .compatible = "fsl,imx6ul-usbmisc", + .data = &imx6sx_usbmisc_ops, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids); @@ -426,7 +500,11 @@ static int usbmisc_imx_probe(struct platform_device *pdev) { struct resource *res; struct imx_usbmisc *data; - struct of_device_id *tmp_dev; + const struct of_device_id *of_id; + + of_id = of_match_device(usbmisc_imx_dt_ids, &pdev->dev); + if (!of_id) + return -ENODEV; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -439,9 +517,7 @@ static int usbmisc_imx_probe(struct platform_device *pdev) if (IS_ERR(data->base)) return PTR_ERR(data->base); - tmp_dev = (struct of_device_id *) - of_match_device(usbmisc_imx_dt_ids, &pdev->dev); - data->ops = (const struct usbmisc_ops *)tmp_dev->data; + data->ops = (const struct usbmisc_ops *)of_id->data; platform_set_drvdata(pdev, data); return 0; |